The Phantom Sender: How Address Poisoning Weaponizes Your Own Transaction History
A Transfer event is a claim, not a fact — attackers write claims that put their address in your history. Two real poisoning campaigns, decoded on-chain.
Here is the uncomfortable thing about an ERC-20 Transfer event: it is not a record of a transfer. It is a log line a contract chose to emit. The from address inside it is whatever the contract wrote there. A well-behaved token writes the truth. A malicious one writes your address.
That single gap — between what a log claims and what actually moved — is the whole foundation of address poisoning, the most common attack in crypto by transaction count and one of the least understood. This is a technical teardown of how it works, using two real campaigns we decoded on-chain: an ordinary wallet hit by competing poisoning crews this week, and — for scale — the KelpDAO exploiter, poisoned mid-heist while sitting on $292 million.
The mechanic: a sender who never signed
Start with the transaction that prompted this piece: 0x66750b2f…fdd9, mined July 14, 2026.
One address (0x80d0…52df) signed it. It called a contract (0x82ac…e888) with an unremarkable selector (0x8aaa8f3b), paid about 0.00002 ETH in gas, and in that single transaction the contract emitted seven Transfer events on behalf of addresses that never signed it — none of the stamped senders authorized, or were even aware of, the transaction.
That should be impossible. A real ERC-20’s transferFrom reverts unless the spender holds an allowance; you cannot move tokens out of a wallet that never approved you. But the attacker isn’t moving anyone’s tokens. They deployed their own contract, and inside it wrote the equivalent of:
emit Transfer(victimAddress, lookalikeAddress, amount);
emit is not a transfer. It is a log. Its contents are entirely under the author’s control — including the from. Nothing on-chain forces a log’s from to be the account that signed the transaction, so the attacker stamps the victim’s address into the sender slot. The result: a row that shows up in the victim’s transaction history looking exactly like something the victim did.
This is the phantom-from signature, and it is the cleanest tell in the entire attack. A legitimate transfer out of an address is always preceded, somewhere in that address’s history, by the tokens arriving. A poisoned row is a token leaving an address that never received it. Any tool that asks “did this sender ever actually hold this token?” catches the fake immediately — which is exactly why TraceFast flags all seven legs of this transaction with a banner reading, in effect, these transfers were emitted on behalf of addresses that did not sign — do not copy addresses from this list.
The bait: dressed like the real thing
A phantom Transfer is only useful if the victim believes it. So the attacker invests in two disguises.
Clone tokens. The seven legs move two fake assets. Call name(), symbol() and decimals() on the first (0xbcef2a80…) and it answers "USD Coin", "USDC", 6 — byte-for-byte identical to Circle’s real USDC metadata. The second (0x08e3c6f2…) answers "ETH", "ETH", 18. In a wallet UI that trusts a token’s self-reported name, these are indistinguishable from the originals. (A forensic tool should not trust that self-report: the honest move is to render such a contract as “Suspicious / impersonates USDC” rather than printing the bait name as identity — the name a scam gives itself is evidence, not truth.)

Vanity look-alike addresses. This is where the craft goes. The attacker grinds a fresh address whose first few and last few hex characters match a real address the victim cares about — because that’s exactly what every block explorer and wallet shows you. 0x4a348a8dedbc…0943af55 truncates on screen to 0x4a34…af55. The attacker’s grinder produces 0x4a353c744c…43af55, which truncates to 0x4a35…af55. Glance at those two in a history list and they are the same address. Only the middle — the part nobody reads — differs.
That match is bought, not found. An address is the last 20 bytes of the hash of a public key, so there’s no shortcut: to fix k hex characters you generate and hash keypairs until one lands, and each additional matched character multiplies the work by sixteen. Matching four leading and four trailing characters — the eight that a truncated display exposes — is millions of tries per target. That cost leaves a fingerprint we saw directly in the KelpDAO campaign: the earliest look-alikes matched the ends only approximately and the later ones matched exactly, because the grinder was still running when the first spam went out. Precision that improves over minutes is a poisoning crew tightening its aim.

Case 1: mirroring a payment you made minutes ago
Older poisoning just sprayed round numbers — a fake $1,000 USDC transfer, hoping you’d reuse the address. This campaign is sharper. It mirrors the victim’s real, recent payments, to the last decimal.
We confirmed it on-chain. At block 25,532,699, the victim 0x50a524…38e3 made a genuine payment: 500.00 USDC to 0x4a348a8ded…0943af55. About 160 blocks later, at 25,532,861, the poisoning transaction fired a fake 500.000000 USDC — same amount to the cent — “from” the victim to 0x4a353c744c…43af55, the vanity twin of that real recipient. The other legs do the same to other victims: 0.061126188904374190 ETH mirrored to 0xfe032d…7a88, 7.988209114904549134 ETH to 0xa4020b…fec8 — each a fake token, each an exact copy of a real amount, each aimed at a look-alike of the address that real amount actually went to.
The intent is precise. The victim will, eventually, want to pay that same counterparty again. They’ll open their history, find “the last payment to them,” copy the address — and copy the twin. The next real 500 USDC leaves for the attacker.
And this victim is not being worked by one crew. Their history shows multiple look-alikes all ending af55 — we found the mirror twin 0x4a353c74…af55 and a second, 0x4a3413fa…af55, planted in a separate transaction (block 25,532,745). Poisoning is a low-cost numbers game, so a wallet that makes a visible payment attracts several independent grinders at once, each seeding its own twin of the same target. The victim only has to slip once, for any of them.
There is a small mercy in this particular case, and it’s worth seeing how we know it. Read the transaction’s net flow — what each address actually ended up with, summed across the legs — and the victim’s real 500 USDC is not in it; that payment settled to the legitimate address before the poisoning ever started. Every later “500 → af55” in the history is another phantom log, not money. The attack, so far, has cost the victim nothing but a contaminated address book. The trap is armed; it simply hasn’t been stepped on.

Case 2: even Lazarus gets poisoned
If you think this is a retail-only problem, consider the highest-value wallet we’ve ever watched get poisoned: the KelpDAO exploiter, fresh off draining $292 million in rsETH.
While that thief was laundering the loot across lending markets and bridges, poisoning bots were spraying their wallet — with the identical playbook, scaled to the occasion. The fake token here was named Тoken, where the “Т” is not a Latin T but Cyrillic Те, U+0422, padded with invisible U+200A hair-spaces to slip past naive string filters — a homoglyph, the same disguise as the clone tokens above, one layer deeper. Its amounts mirrored the exploiter’s own branch payouts exactly (53,000 / 30,000 / 10,000 rsETH), sent to vanity twins of the real laundering branches. When the exploiter later swept borrowed ETH to a collector, a second wave mirrored that — counterfeit “ETH” tokens (ĖTḨ, EꓔH, more homoglyphs) carrying the exact 52,440.68 sweep amount, aimed at a twin of the collector.
It didn’t work — the exploiter’s staging wallet had already gone dormant, so the poisoners were grinding vanity addresses to ambush a wallet that would never send again. But the lesson is the point: on a public ledger, a nation-state crew mid-heist and a retail user paying rent are the same thing — a visible wallet with a balance, which is to say, a target. The transparency that lets us reconstruct a $292M exploit is the same transparency that paints everyone into a scammer’s crosshairs.

Telling the real row from the poison
Every disguise in this attack leaves a mark, and none of the marks require special access — just the discipline to check the thing the attacker is betting you won’t:
- Phantom-from. Did the “sender” of a suspicious row ever actually receive that token? If not, the row is a log, not a payment. This alone catches essentially every poison.
- The full address, not the ends. The entire attack lives in the middle characters that UIs hide. Two addresses sharing
0x4a3…af55are not “probably the same” — they are almost certainly an attack. - Homoglyphs and hidden characters. A token or name that renders as
USDCorТokenbut contains non-ASCII or invisible code points is impersonating something. The real asset’s metadata is plain ASCII. - Net flow over row count. Ten identical “500 USDC” rows in a history is not ten payments; sum what each address actually netted. Phantom logs contribute nothing to net flow.
These signals also want to be graded, not lumped together. An active poisoning transaction — phantom from, mirrored amount, look-alike target — is a live attempt at theft and deserves a loud, red treatment. A token that merely carries a homoglyph in its name, with no phantom transfer behind it, is suspicious but passive: worth a quieter warning, not an alarm. Collapsing the two either cries wolf on every oddly-named token or buries the real trap; keeping them separate is what lets a “danger” badge stay meaningful.
Good forensic tooling folds all of this into the interface so you don’t have to run it by hand — a poisoning banner when the phantom-from pattern hits a whole transaction, an “impersonates USDC” tag instead of the bait name, a homoglyph warning that spells out the code point. But the checks are simple enough to internalize, and the first one — never copy an address from your own history; copy it from the source you’re paying — costs nothing and closes the attack entirely.
Reproduce this
Every claim here is public. Open the poisoning transaction and read the banner, the net flow, and the seven flagged legs; call symbol() on the clone contracts and watch them answer “USDC”; and if you want the high-end version, walk the KelpDAO exploiter’s poisoning and count how many crews tried to rob the robber.
The next time your wallet shows a familiar amount going to a familiar-looking address, remember: the familiar part was written by someone who wants you to trust it.
Sources / data
- Case 1 poisoning tx:
0x66750b2f…fdd9(TraceFast) — 7 legs, clone tokens0xbcef2a80…(USDC) and0x08e3c6f2…(ETH), decoded from mainnet receipt. - Mirrored real payment: victim
0x50a524…38e3→0x4a348a8ded…af55, 500.00 USDC, block 25,532,699 (verified via eth_getLogs). - Case 2: KelpDAO post-mortem — the “even Lazarus gets poisoned” sidebar.
- On-chain metadata via
name()/symbol()/decimals(); fees from receipteffectiveGasPrice.