What happened

On 29 October about $6.3 billion was taken from Binance hot wallets, according to Reuters and Bitcoin.com. Withdrawals have been coming back in phases since. TokenPost lists the order: Bitcoin first, then selected routes on Ethereum and BNB Smart Chain, then selected withdrawals on Arbitrum and TRON. On 14 November Binance said 87 networks had reopened that week and that it aims to reopen the rest by the end of next week.

The useful detail for anyone writing transfer code is the shape of that rollout. It went by network, and within a network by route. USDT could be withdrawable on Ethereum while USDT on TRON was still shut.

The bug this exposes

A lot of internal tooling stores status per asset, as in USDT: enabled. The rollout above needs the key (coin, network), and the receiving side has its own list of networks it accepts. A transfer works only where three things line up:

  1. the destination address is valid for the network,
  2. the receiver accepts that coin on that network,
  3. the sender has that coin open for withdrawal on that network.

The check

const ADDRESS_RULES = { TRC20: /^T[1-9A-HJ-NP-Za-km-z]{33}$/, ERC20: /^0x[0-9a-fA-F]{40}$/, BEP20: /^0x[0-9a-fA-F]{40}$/, ARBITRUM: /^0x[0-9a-fA-F]{40}$/, }; export function checkTransfer({ coin, address, withdrawStatus, depositNetworks }) { // 1. Start at the receiving address: which network formats does it match? const accepted = depositNetworks[coin] ?? []; const addressFits = accepted.filter((n) => ADDRESS_RULES[n]?.test(address)); if (addressFits.length === 0) { return { ok: false, reason: `address does not match any network the receiver takes for ${coin}` }; } // 2. Same coin, same network, open on the sending side. const open = withdrawStatus.filter((s) => s.coin === coin && s.withdraw && addressFits.includes(s.network)); if (open.length === 0) { const closed = withdrawStatus.filter((s) => s.coin === coin && addressFits.includes(s.network)); return { ok: false, reason: `no open route: ${closed.map((s) => `${s.network} (${s.note || 'closed'})`).join(', ')}` }; } return { ok: true, network: open[0].network, note: open[0].note }; } 

Feed it a status table shaped like a phased reopening. TRC20 still closed, BEP20 open but busy:

const withdrawStatus = [ { coin: 'USDT', network: 'ERC20', withdraw: true, note: '' }, { coin: 'USDT', network: 'BEP20', withdraw: true, note: 'busy, may be slow' }, { coin: 'USDT', network: 'TRC20', withdraw: false, note: 'not reopened yet' }, { coin: 'USDT', network: 'ARBITRUM', withdraw: true, note: '' }, ]; const depositNetworks = { USDT: ['TRC20', 'BEP20'] }; 

Output from node pick-network.mjs:

TRON address -> { ok: false, reason: 'no open route: TRC20 (not reopened yet)' } 0x address -> { ok: true, network: 'BEP20', note: 'busy, may be slow' } 

The TRON address gets a clear "wait" and the function never falls back to Ethereum on its own. That fallback would send USDT on ERC20 to a receiver that only takes TRC20 and BEP20 for that address.

Notes

  • The address regexes check format only. A TRON address also has a Base58Check checksum and an EVM address can carry an EIP-55 checksum. Validate those too in real code.
  • 0x addresses look the same on Ethereum, BNB Smart Chain and Arbitrum. The format can't tell you which one the receiver expects, so the receiver's network list has to.
  • Treat "open but busy" as a state of its own. Binance and TokenPost both warned that heavy traffic may delay some withdrawals during the reopening.

Sources: Reuters via Yahoo Finance (14 Nov 2026), Bitcoin.com News (14 Nov 2026), TokenPost (13 Nov 2026).