The $68M copy-paste mistake

In May 2024 a wallet lost $68 million in WBTC to one of the oldest tricks on-chain: address poisoning. The attacker seeded the victim's transaction history with a zero-value transfer from a lookalike address — same first 4 and last 4 characters as the real payee. The victim copied "their" payee from history, pasted it, hit send. Irreversible by design. (The funds were mostly recovered weeks later — after being stolen in seconds.)

Most wallets render addresses truncated (0x1234…9c8f), and anyone can inject into your history — dust for incoming transfers, zero-value transferFrom for outgoing on USDT-style ERC-20s. The defense has to happen at the same layer the attack exploits: the transaction itself.

How SafeSend works

SafeSend is a poison-aware EVM payment router. Instead of sending ERC-20s straight to an address, you route through the contract, which classifies each destination with a 32-bit fingerprint — (first 4 hex chars << 16) | last 4 hex chars — exactly the parts wallets show you:

  • Verified payee → settles instantly, like a normal send.
  • Fingerprint collides with a verified payee → flagged LOOKALIKE and held for 24h. Time alone never unlocks it — the sender must explicitly approve (and can always cancel). This is the poisoning case: the lookalike matches your trusted contact's visible edges.
  • Unknown recipient → escrowed under a sender-set cooldown (default 1h, 60s–7d). The recipient claims after unlock — which doubles as a claim-to-verify handshake — or the sender reclaims 30 days after the escrow's unlock time.

The sender sees the full untruncated destination before funds lock, not 0x1234…9c8f.

Watch the attack get caught

The repo ships Poisoner.sol, a Foundry reproduction that plants the same lookalike transactions real poisoners use (zero-value transferFrom + dust, Anvil local only). In the demo, the victim's history shows two identical-looking payees — the router quarantines the send to the wrong one, the non-sender "approve" call reverts, and only the real sender can release. Try it: make anvil && make deploy-local && make seed && make web → localhost:5173/?demo=1.

Technical details

  • Contract: Solidity 0.8.28, OpenZeppelin SafeERC20 + ReentrancyGuard, checks-effects-interactions. No owner, no admin keys, no fees, not upgradeable.
  • Toolchain: Foundry (forge test — 58 tests, 100% line coverage on SafeSend.sol).
  • Frontend: Vite + React 18 + TypeScript + wagmi v2 + viem + Tailwind. Includes an "Attacker console" so judges can fire the poisoning themselves.
  • Networks: local Anvil (chain 31337) for the demo; optional Base Sepolia deploy.

Honest limits

This is a prototype, and we say so: the fingerprint only checks the first/last 4 hex characters (the part users actually compare); routing is detection, not prevention — a poisoned verified payee isn't caught; recipient classification is fixed at escrow creation; addPayee is a trust decision; the contract is unaudited and lives on testnet/local. We also disclose prior art openly: reversible-transaction proposals (REVERSO, ERC-20R/721R), Argent-style trusted contacts, and explorer name-tags — SafeSend's angle is enforcing the edge-fingerprint check inside the transaction path instead of in the UI.

Links

Built With

Share this project:

Updates

Submission history