maborak.com~/blog/binance-hot-wallet-transfers-eip-7702software & infrastructure engineering
Back to all articles

Binance's $3.3B wallet transfers: what the chain shows, and how to tell a hack from a move

Read from a public Ethereum node: an EIP-7702 delegation on the Binance 14 address, a 65-token sweep, and Tether and Circle freezes. Signs that separate theft from an internal move.

By the Maborak engineering team · · 10 min read

On 29 October 2026 about $3.3 billion in tokens left wallets that block explorers label as Binance’s, and the question “was Binance hacked?” spread faster than any answer. Binance said it was investigating a “potential security issue” and later that “user funds are completely safe”. It has not said what the transfers were. This post does not answer the question either. It shows what anyone can read from the chain, explains the mechanism the transfers used, and lists the signs that would separate a theft from an internal move.

Checked 29 October 2026, at Ethereum block 26,301,820 (23:55 UTC). Every transaction, account state and freeze below was read from a public Ethereum node with the scripts in the Probe section and is marked [reproduced]. Statements by Binance and Tether reached this post through press reports and are marked [reported]. Conclusions are marked [inferred], with the reasoning shown. The incident is still open.

Background

The address 0x28c6c062…1d60 has long carried the explorer label “Binance 14”, and 0xee7ae85f…4055 the label “Binance: Hot Wallet 34”. Labels are assigned by analytics firms, not by Binance, so they are strong hints, not proof of ownership. [reported]

Binance’s first statement, reported at 5:16 p.m. ET (21:16 UTC), said its teams were “actively investigating” a “potential security issue” and had “not confirmed any impact to user funds”. A version reported at 5:29 p.m. ET said: “User funds are completely safe.” Tether’s CEO said Tether had frozen more than 1.567 billion USDT linked to the transfers. [reported]

Mechanics

What happened on chain

All times are UTC on 29 October 2026, taken from the block timestamps. [reproduced]

Time Block What the chain records
20:20:59 26,300,753 0x7c86…5ca0 sends a type-4 (EIP-7702) transaction carrying 50 signed authorizations that delegate “Binance 14” to contract 0x63e8…6fae
20:24:23 26,300,770 0x7c86…5ca0 calls sweep(address,address[]) on “Binance 14”: 65 token contracts, including 1,615,409,498.97 USDT, go to 0x5227…2f8c
20:30:11 26,300,799 0x5227…2f8c sends 500,000,000 USDT to 0x9749…c762, then 200,000,000 USDT to 0x0ddf…697e at 20:33:23 (block 26,300,815)
20:38:47 26,300,842 0x5227…2f8c sends 195,460 ETH to 0x8532…43d4
20:41:23 26,300,855 780,000,000 USDC moves from “Hot Wallet 34” to 0x5227…2f8c
21:01:35 26,300,954 Tether freezes 0x5227…2f8c and 0x9749…c762, then 0x0ddf…697e one block later, and six more addresses by block 26,301,041
21:04:59 26,300,971 Circle freezes 0x5227…2f8c
22:06:23 26,301,278 Tether freezes “Binance 14” itself
23:53:35 26,301,812 Circle freezes “Binance 14”, then “Hot Wallet 34” at 23:54:59 (block 26,301,819)

The transfers did not come from a transaction signed by “Binance 14”. They came from calls to it, by a separate account, 0x7c86…5ca0, which had sent 21 transactions in total at the time of the check.

EIP-7702: how a signature becomes code

EIP-7702, a final Ethereum standard that is active on mainnet (the 20:20:59 transaction is one), lets an ordinary account delegate to contract code. A type-4 transaction carries a list of authorizations. Each one is a signature, by the account’s own private key, over keccak(0x05 || rlp([chain_id, address, nonce])). If the signature recovers to the account and the nonce matches the account’s current nonce, the network writes 0xef0100 followed by the contract address into the account’s code. From then on, calls to the account run that contract’s code, until the account signs another authorization that changes or clears it. [vendor], from the EIP-7702 specification.

“Binance 14” now has exactly that code: 0xef0100 followed by 0x63e842b054bd624a465454d533c33f573e9c6fae. [reproduced] The 50 authorizations in the 20:20:59 transaction cover consecutive nonces from 18,914,775 to 18,914,824. “Binance 14” is a busy account (its nonce was 18,914,902 at the check), and signing a range is one way to be sure one authorization matches the nonce at the moment the transaction lands. [inferred]

What this proves, and what it does not:

  • It proves that whoever prepared the delegation could obtain valid signatures from the “Binance 14” key, or from the system that signs for it. EIP-7702 accepts no other signer. [inferred], from the specification.
  • It does not prove who that was. Binance’s own operations could use EIP-7702 for a wallet migration; an attacker with access to the key or to the signing system could use it the same way. The chain records the signature, not the intent.
  • The delegate contract is opaque. 0x63e8…6fae has no verified source on Blockscout, so its full behaviour cannot be read. Only the sweep(address,address[]) call is visible.

Freezes: what stablecoin issuers can and cannot stop

USDT and USDC are tokens with a blocklist built into their contracts. Tether’s contract has isBlackListed(address) and Circle’s has isBlacklisted(address). A blocklisted address keeps its balance but cannot send it. Receiving is not blocked: after Tether froze 0x5227…2f8c, another 3,917,639.77 and 200,397,771.82 USDT arrived from “Binance 14”. [reproduced]

ETH and most other tokens have no such switch. The 195,460 ETH at 0x8532…43d4 cannot be frozen by anyone; that address had never sent a transaction at the time of the check. [reproduced]

Impact

For Binance users, the only on-record position is Binance’s: user funds are “completely safe”. [reported] Nothing on chain contradicts or confirms that, because exchange balances are an internal ledger, not on-chain data.

On the money, at block 26,301,820: [reproduced]

  • 0x5227…2f8c held 1,175,464,711.08 USDT and 823,000,000 USDC, frozen in both.
  • The nine addresses Tether froze held 1,381,816,113.41 USDT in total, below the “more than 1.567 billion” Tether’s CEO reported. The difference may sit in addresses this search did not find, or reflect balances at the time of each freeze. [inferred]
  • 700 million USDT left 0x5227…2f8c at 20:30 and 20:33, about half an hour before the first freeze. Tether froze both receiving addresses within a block of the first freeze; they held no USDT at the check.
  • 195,460 ETH sits at an unfreezable address.

For anyone running hot wallets on Ethereum, the incident shows a property of EIP-7702 that predates it: a key that can sign transactions can also sign a delegation, and a delegation turns one signature into standing code on the account. A signing policy that reviews outgoing transfers but not authorization payloads does not see it. [inferred], from the specification.

Hack or move: the signs

Sign Points toward theft Points toward an internal move Seen so far
Destination Fresh accounts with no history Known, labelled company wallets Fresh accounts: 0x5227…2f8c had 24 transactions at the check, 0x8532…43d4 none [reproduced]
Speed of onward movement Splitting and moving within minutes Funds parked 500M and 200M USDT and 195,460 ETH moved on within 15 minutes of the sweep [reproduced]
Issuer response Freezes on the destination and the source No freezes Tether froze the destination, the onward hops and “Binance 14”; Circle froze the destination, “Binance 14” and “Hot Wallet 34” [reproduced]
Delegate code Unverified, purpose unknown Published or verified Unverified [reproduced]
Exchange statement Breach confirmed Explanation of a migration Neither yet; “funds are completely safe” [reported]
Service Withdrawals paused or limited Normal operation Not established in the sources

The issuers’ actions are the strongest public signal. Tether and Circle froze the receiving address within 41 minutes of the sweep, and both later froze the Binance addresses the funds came from. Issuers act on information this post does not have, and a freeze can also be a precaution requested by the exchange. [inferred]

Probe

Three short Python scripts using only the standard library and the public JSON-RPC endpoint https://ethereum-rpc.publicnode.com. They make read-only calls (eth_getTransactionByHash, eth_getTransactionReceipt, eth_getCode, eth_call, eth_getLogs) and change nothing. Python 3.15 was used; any Python 3 works.

1. What a transaction moved. It decodes ERC-20 Transfer logs from the receipt.

import json, sys, urllib.request
RPC = "https://ethereum-rpc.publicnode.com"
TRANSFER = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
TOKENS = {"0xdac17f958d2ee523a2206206994597c13d831ec7": ("USDT", 6),
          "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": ("USDC", 6)}
def rpc(method, params):
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
    req = urllib.request.Request(RPC, body, {"Content-Type": "application/json", "User-Agent": "probe"})
    return json.load(urllib.request.urlopen(req, timeout=30))["result"]
for h in sys.argv[1:]:
    tx, rc = rpc("eth_getTransactionByHash", [h]), rpc("eth_getTransactionReceipt", [h])
    print(f"tx {h[:12]}… type={int(tx['type'],16)} from {tx['from']} to {tx['to']} ETH {int(tx['value'],16)/1e18:,.4f}")
    for log in rc["logs"]:
        if log["topics"][0] == TRANSFER and len(log["topics"]) == 3 and log["address"].lower() in TOKENS:
            sym, dec = TOKENS[log["address"].lower()]
            print(f"  {sym} {int(log['data'],16)/10**dec:,.2f} 0x{log['topics'][1][-40:]} -> 0x{log['topics'][2][-40:]}")

Output for the sweep and the USDC transfer: [reproduced]

tx 0x53bd38b816… type=2 from 0x7c8601d4645b93cea45f71e118a729332e6b5ca0 to 0x28c6c06298d514db089934071355e5743bf21d60 ETH 0.0000
  USDT 1,615,409,498.97 0x28c6c06298d514db089934071355e5743bf21d60 -> 0x5227f603c1ee34ce3c06efe6477003bdfccb2f8c
tx 0xdc102dd89d… type=2 from 0x7c8601d4645b93cea45f71e118a729332e6b5ca0 to 0x28c6c06298d514db089934071355e5743bf21d60 ETH 0.0000
  USDC 780,000,000.00 0xee7ae85f2fe2239e27d9c1e23fffe168d63b4055 -> 0x5227f603c1ee34ce3c06efe6477003bdfccb2f8c

The sweep also moved 64 other tokens; the script prints only USDT and USDC.

2. Whether an account is delegated. eth_getCode returns 0x for a plain account, 0xef0100… for an EIP-7702 delegation, and contract bytecode otherwise. On the delegation transaction, authorizationList shows what was signed.

code = rpc("eth_getCode", ["0x28c6c06298d514db089934071355e5743bf21d60", "latest"])
print("delegated to 0x" + code[8:48] if code.startswith("0xef0100") else code[:20])
tx = rpc("eth_getTransactionByHash", ["0xad99264d5532ae0dd04b2004eebda05f25657e70dacb764f7ac8d4ccd051298b"])
auths = tx["authorizationList"]
print(f"type={int(tx['type'],16)} authorizations={len(auths)} delegate={auths[0]['address']} "
      f"nonces={min(int(a['nonce'],16) for a in auths)}-{max(int(a['nonce'],16) for a in auths)}")
delegated to 0x63e842b054bd624a465454d533c33f573e9c6fae
type=4 authorizations=50 delegate=0x63e842b054bd624a465454d533c33f573e9c6fae nonces=18914775-18914824

[reproduced]

3. Whether a stablecoin balance is frozen. Read-only calls to the issuers’ own contracts: isBlackListed(address) (selector 0xe47d6060) on USDT, isBlacklisted(address) (0xfe575a87) on USDC, and balanceOf(address) (0x70a08231).

USDT, USDC = "0xdac17f958d2ee523a2206206994597c13d831ec7", "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
def call(to, selector, addr):
    return int(rpc("eth_call", [{"to": to, "data": selector + addr[2:].lower().rjust(64, "0")}, "latest"]), 16)
for a in sys.argv[1:]:
    print(a, "USDT frozen", call(USDT, "0xe47d6060", a) == 1, f"{call(USDT, '0x70a08231', a)/1e6:,.2f}",
             "USDC frozen", call(USDC, "0xfe575a87", a) == 1, f"{call(USDC, '0x70a08231', a)/1e6:,.2f}")
0x5227f603c1ee34ce3c06efe6477003bdfccb2f8c USDT frozen True 1,175,464,711.08 USDC frozen True 823,000,000.00
0x8532fa14acaf483729c449b8458aeddb900543d4 USDT frozen False 0.00 USDC frozen False 0.00
0x28c6c06298d514db089934071355e5743bf21d60 USDT frozen True 1,351,530.01 USDC frozen True 52,363.12

[reproduced] The freeze events themselves (AddedBlackList on USDT, Blacklisted on USDC) and the onward USDT transfers in the timeline came from eth_getLogs on the two token contracts from block 26,300,700. The public node refuses historical state queries, so the scripts show current state only.

Mitigation

For exchange users:

  1. Read updates only on Binance’s own site and verified accounts. Incident windows bring messages offering “compensation” or “recovery”; Binance’s statements contain no such offer.
  2. Use an authenticator app or hardware key for two-factor authentication, turn on withdrawal address allowlisting, and remove API keys you do not use or restrict them to trading without withdrawal rights.
  3. Keep long-term holdings off any single exchange if counterparty risk matters to you; self-custody trades that risk for key-management risk.

For teams running hot wallets or signing services on Ethereum ([inferred], from the EIP-7702 specification):

  1. Refuse to sign authorization payloads you did not plan. An EIP-7702 authorization is a signature over a message starting with the byte 0x05. A signing policy, in an HSM or an MPC service, can reject that domain outright or require separate approval for it, the same way it gates large withdrawals.
  2. Alert on account code. Poll eth_getCode for every hot wallet. Any result other than the expected one, especially 0xef0100…, means the account is delegated and calls to it run someone’s contract.
  3. Watch type-4 transactions that name your addresses in their authorization list, sent by accounts you do not control.
  4. Know the undo. A delegation stays until the account signs a new authorization. Pointing it at the zero address clears the code; that needs the same key, so plan the response before an incident.

Verification

Re-run the probes against the latest block. These outcomes would settle the question:

  • An internal move: Binance publishes an explanation naming the delegate contract or the receiving addresses as its own, the freezes are lifted at Binance’s request, and “Binance 14” code returns to 0x or to a published contract.
  • A theft: Binance confirms a breach, funds keep moving from the unfrozen ETH address, and the delegate contract or the sender account turns up in other incidents.

Until one of these happens, check the facts that change: eth_getCode on “Binance 14”, the freeze status and balances of 0x5227…2f8c, and the nonce of 0x8532…43d4 (still 0 means the ETH has not moved).

Sources

Back to all articles

Technologies we work with

  • Argo CD
  • Kubernetes
  • Docker
  • Podman
  • Ansible
  • Jenkins
  • Chef
  • Proxmox VE
  • Google Cloud
  • Linux
  • Red Hat
  • CentOS
  • NGINX
  • Apache
  • Traefik
  • Prometheus
  • Grafana
  • PostgreSQL
  • MySQL
  • MariaDB
  • Redis
  • etcd
  • Qdrant
  • Elasticsearch
  • Logstash
  • Kibana
  • PagerDuty
  • Go
  • Python
  • FastAPI
  • PHP
  • TypeScript
  • React