Most crypto isn't lost to "hacks."
It's lost to approvals people sign themselves.
Here's how wallet drainers actually work β and how not to be next π§΅
Half right. "Only EVM" is the door, not the room β what got emptied was the staking module, every validator's and delegator's bond. Cosmos-side money, EVM-side entry.
And the window to update was three days. v0.7.2 shipped Aug 19 marked state-breaking, which means a coordinated upgrade across an entire validator set, not a hotfix you push on a Thursday. TAC fell on the 22nd.
The 96 days before that are the part worth noting: the same fix went into main on May 15 under a plain title, no advisory, and shipped in no release until Aug 19. Nobody was told it was urgent until the release notes said so β and by then the diff was public for anyone who read it.
TAC's entire staking module was emptied in one transaction.
bonded_tokens_pool held 2,985,651,403.4047 TAC at block 24,662,147. Zero at 24,662,148. The 20 bonded validators still book every token of it as staked.
The official line is "a drain, not a mintβ¦ 2,985,651,403 TAC was moved from one account to another." Both halves are true. The account is a module account β the one holding every validator's and every delegator's bond. "One account" is doing a lot of work.
The exploit is one transaction, at 19:46:37 UTC on Aug 22:
0xae4e9b708ecef134a18aef8a1da9b4d24aa2a0e87f98d02695beae588cda46fc
Contract 0x5711c2af77d5Ea8DCFDAD2F3ae3E17d9Dd37c978 calls the staking precompile at 0xβ¦0800 β delegate(address,string,uint256), for 1 utac β which is what pulls the bonded pool into the EVM statedb as a state object. Then it makes a plain value call to that pool's address, 0x4feA76427B8345861e80A3540a8a9D936FD39391, with
value = 115792089237316195423570985008687907853269984665637578388054179295181792313188
= 2^256 β 2,985,651,403,404,712,731,337,326,748
Minus the pool's exact balance, in uint256 wraparound. SubBalance underflows, the pool wraps to zero, and the difference lands in the attacker's wallet.
Total gas: 1.35 TAC charged, 0.675 refunded by the feemarket. 0.675 TAC to take a network's entire stake.
cosmos/evm patched exactly this on 2026-08-19 at 23:01 UTC, in v0.7.2 β "this release contains important security fixes." The patch adds a panic to stateObject.SubBalance when balance < amount.
The patch was not new. PR #1176, "fix: harden statedb balance and event amount handling", was merged to cosmos/evm main on 2026-05-15. Plain title, no advisory, sitting in the open. v0.7.1 shipped on Jul 27 without it. The backport landed on Aug 19 β 96 days later. There is still no advisory for it.
MANTRA's last block came about 24 hours after that release. TAC fell 2d 20h after it. A silent security release on an open repo is a countdown: the diff is public the moment it ships, and it points at the bug more clearly than any advisory would.
The supply claim does check out, though. utac supply sits on an unbroken inflation curve straight through the incident β 10,413,780,898.92 at height 24,000,000, 10,422,742,680.94 at 24,600,000, 10,423,719,038.07 at the halt. Nothing was minted. It was taken.
And the exit was finished long before the halt. 500,000,024 TAC went to BNB Chain via LayerZero at 19:47:09, thirty-two seconds after the drain. 2,485,651,574 more at 19:48:12. 49.9M to TON at 19:59:26. TAC halted at 23:58:11, four hours and eleven minutes late.
On BNB Chain he sold 1,208,500,000 TAC into a single pool across 55 swaps:
first β 5,000,000 TAC β 11,557.77 USDT ($0.00231155)
last β 40,000,000 TAC β 8,329.04 USDT ($0.00020823)
Total 950,293.45 USDT, average realised $0.00078634. The TON leg went through https://t.co/2icCOy0zk1 and DeDust for another 74,750.73 USDT.
Realised: ~$1,025,044. Notional at the pre-attack price: $7,566,640. Thirteen and a half percent.
1,662,322,352.70 TAC β 55.7% of the haul β is still sitting unsold in his BNB Chain wallet, because there is nothing left to sell it into. Another 65,100,988.59 is frozen on TAC by the halt. Both legs reconcile to the wei; the TAC side is off by 2.05 TAC, which is gas.
The proceeds left through nine single-use addresses. Eight of them were swept into 0xe647b0db71985b26a1562be585bf270ea4023b10 inside eight blocks β an address with nonce 37,787 that took deposits from 58 unrelated one-shot addresses in the same window, so a service sweeper, not him. It holds 716,527 USDT.
Interleaved through the whole laundering run are zero-value transfers to lookalikes of his own deposit addresses, matching first and last four hex. The man who emptied a chain's staking module spent the evening being address-poisoned.
Method: https://t.co/lgGbqX73Wc, https://t.co/rGnKVl8kz8 (which serves archive state via x-cosmos-block-height), https://t.co/rjj2b2jmck, and eth_getLogs on BNB Chain. Every number above is one call away.
Any Cosmos EVM chain can check in one call whether this already happened to it.
Compare what the staking module claims against the coins actually behind it:
/cosmos/staking/v1beta1/pool β bonded_tokens
/cosmos/bank/v1beta1/balances/<bonded_tokens_pool address>
On TAC those two agreed at block 24,662,147 β 2,985,651,403404712731337326748 utac on both sides. At 24,662,148 the pool still claims it and the module account returns an empty balance array. Not reduced. Empty.
Any archive endpoint serves past heights via the x-cosmos-block-height header, so this works retroactively, halted or not, and it needs no key. The validators' own records still book every token of it as staked.
What it cost the attacker to do that: 0.675 TAC of gas. https://t.co/OVOgwjU08t
TAC's entire staking module was emptied in one transaction.
bonded_tokens_pool held 2,985,651,403.4047 TAC at block 24,662,147. Zero at 24,662,148. The 20 bonded validators still book every token of it as staked.
The official line is "a drain, not a mintβ¦ 2,985,651,403 TAC was moved from one account to another." Both halves are true. The account is a module account β the one holding every validator's and every delegator's bond. "One account" is doing a lot of work.
The exploit is one transaction, at 19:46:37 UTC on Aug 22:
0xae4e9b708ecef134a18aef8a1da9b4d24aa2a0e87f98d02695beae588cda46fc
Contract 0x5711c2af77d5Ea8DCFDAD2F3ae3E17d9Dd37c978 calls the staking precompile at 0xβ¦0800 β delegate(address,string,uint256), for 1 utac β which is what pulls the bonded pool into the EVM statedb as a state object. Then it makes a plain value call to that pool's address, 0x4feA76427B8345861e80A3540a8a9D936FD39391, with
value = 115792089237316195423570985008687907853269984665637578388054179295181792313188
= 2^256 β 2,985,651,403,404,712,731,337,326,748
Minus the pool's exact balance, in uint256 wraparound. SubBalance underflows, the pool wraps to zero, and the difference lands in the attacker's wallet.
Total gas: 1.35 TAC charged, 0.675 refunded by the feemarket. 0.675 TAC to take a network's entire stake.
cosmos/evm patched exactly this on 2026-08-19 at 23:01 UTC, in v0.7.2 β "this release contains important security fixes." The patch adds a panic to stateObject.SubBalance when balance < amount.
The patch was not new. PR #1176, "fix: harden statedb balance and event amount handling", was merged to cosmos/evm main on 2026-05-15. Plain title, no advisory, sitting in the open. v0.7.1 shipped on Jul 27 without it. The backport landed on Aug 19 β 96 days later. There is still no advisory for it.
MANTRA's last block came about 24 hours after that release. TAC fell 2d 20h after it. A silent security release on an open repo is a countdown: the diff is public the moment it ships, and it points at the bug more clearly than any advisory would.
The supply claim does check out, though. utac supply sits on an unbroken inflation curve straight through the incident β 10,413,780,898.92 at height 24,000,000, 10,422,742,680.94 at 24,600,000, 10,423,719,038.07 at the halt. Nothing was minted. It was taken.
And the exit was finished long before the halt. 500,000,024 TAC went to BNB Chain via LayerZero at 19:47:09, thirty-two seconds after the drain. 2,485,651,574 more at 19:48:12. 49.9M to TON at 19:59:26. TAC halted at 23:58:11, four hours and eleven minutes late.
On BNB Chain he sold 1,208,500,000 TAC into a single pool across 55 swaps:
first β 5,000,000 TAC β 11,557.77 USDT ($0.00231155)
last β 40,000,000 TAC β 8,329.04 USDT ($0.00020823)
Total 950,293.45 USDT, average realised $0.00078634. The TON leg went through https://t.co/2icCOy0zk1 and DeDust for another 74,750.73 USDT.
Realised: ~$1,025,044. Notional at the pre-attack price: $7,566,640. Thirteen and a half percent.
1,662,322,352.70 TAC β 55.7% of the haul β is still sitting unsold in his BNB Chain wallet, because there is nothing left to sell it into. Another 65,100,988.59 is frozen on TAC by the halt. Both legs reconcile to the wei; the TAC side is off by 2.05 TAC, which is gas.
The proceeds left through nine single-use addresses. Eight of them were swept into 0xe647b0db71985b26a1562be585bf270ea4023b10 inside eight blocks β an address with nonce 37,787 that took deposits from 58 unrelated one-shot addresses in the same window, so a service sweeper, not him. It holds 716,527 USDT.
Interleaved through the whole laundering run are zero-value transfers to lookalikes of his own deposit addresses, matching first and last four hex. The man who emptied a chain's staking module spent the evening being address-poisoned.
Method: https://t.co/lgGbqX73Wc, https://t.co/rGnKVl8kz8 (which serves archive state via x-cosmos-block-height), https://t.co/rjj2b2jmck, and eth_getLogs on BNB Chain. Every number above is one call away.
Undisclosed is not the same as unknown. The newest thing cosmos/evm has shipped is still v0.7.2, Aug 19, and its security content is one commit: a panic added to stateObject.SubBalance when balance < amount, plus a denom-aware ParseAmount.
No release, no commit on release/v0.7.x and no advisory since. Whatever is being halted for today, the patch operators are pointed at is six days old and its diff has been readable the whole time β and the same change went into main on 2026-05-15, 96 days before it reached a release.
That commit is what emptied TAC on Aug 22. bonded_tokens_pool: 2,985,651,403.4047 TAC at block 24,662,147, nothing at 24,662,148, one transaction, 0.675 TAC of gas. Full reconstruction: https://t.co/OVOgwjU08t
TAC's entire staking module was emptied in one transaction.
bonded_tokens_pool held 2,985,651,403.4047 TAC at block 24,662,147. Zero at 24,662,148. The 20 bonded validators still book every token of it as staked.
The official line is "a drain, not a mintβ¦ 2,985,651,403 TAC was moved from one account to another." Both halves are true. The account is a module account β the one holding every validator's and every delegator's bond. "One account" is doing a lot of work.
The exploit is one transaction, at 19:46:37 UTC on Aug 22:
0xae4e9b708ecef134a18aef8a1da9b4d24aa2a0e87f98d02695beae588cda46fc
Contract 0x5711c2af77d5Ea8DCFDAD2F3ae3E17d9Dd37c978 calls the staking precompile at 0xβ¦0800 β delegate(address,string,uint256), for 1 utac β which is what pulls the bonded pool into the EVM statedb as a state object. Then it makes a plain value call to that pool's address, 0x4feA76427B8345861e80A3540a8a9D936FD39391, with
value = 115792089237316195423570985008687907853269984665637578388054179295181792313188
= 2^256 β 2,985,651,403,404,712,731,337,326,748
Minus the pool's exact balance, in uint256 wraparound. SubBalance underflows, the pool wraps to zero, and the difference lands in the attacker's wallet.
Total gas: 1.35 TAC charged, 0.675 refunded by the feemarket. 0.675 TAC to take a network's entire stake.
cosmos/evm patched exactly this on 2026-08-19 at 23:01 UTC, in v0.7.2 β "this release contains important security fixes." The patch adds a panic to stateObject.SubBalance when balance < amount.
The patch was not new. PR #1176, "fix: harden statedb balance and event amount handling", was merged to cosmos/evm main on 2026-05-15. Plain title, no advisory, sitting in the open. v0.7.1 shipped on Jul 27 without it. The backport landed on Aug 19 β 96 days later. There is still no advisory for it.
MANTRA's last block came about 24 hours after that release. TAC fell 2d 20h after it. A silent security release on an open repo is a countdown: the diff is public the moment it ships, and it points at the bug more clearly than any advisory would.
The supply claim does check out, though. utac supply sits on an unbroken inflation curve straight through the incident β 10,413,780,898.92 at height 24,000,000, 10,422,742,680.94 at 24,600,000, 10,423,719,038.07 at the halt. Nothing was minted. It was taken.
And the exit was finished long before the halt. 500,000,024 TAC went to BNB Chain via LayerZero at 19:47:09, thirty-two seconds after the drain. 2,485,651,574 more at 19:48:12. 49.9M to TON at 19:59:26. TAC halted at 23:58:11, four hours and eleven minutes late.
On BNB Chain he sold 1,208,500,000 TAC into a single pool across 55 swaps:
first β 5,000,000 TAC β 11,557.77 USDT ($0.00231155)
last β 40,000,000 TAC β 8,329.04 USDT ($0.00020823)
Total 950,293.45 USDT, average realised $0.00078634. The TON leg went through https://t.co/2icCOy0zk1 and DeDust for another 74,750.73 USDT.
Realised: ~$1,025,044. Notional at the pre-attack price: $7,566,640. Thirteen and a half percent.
1,662,322,352.70 TAC β 55.7% of the haul β is still sitting unsold in his BNB Chain wallet, because there is nothing left to sell it into. Another 65,100,988.59 is frozen on TAC by the halt. Both legs reconcile to the wei; the TAC side is off by 2.05 TAC, which is gas.
The proceeds left through nine single-use addresses. Eight of them were swept into 0xe647b0db71985b26a1562be585bf270ea4023b10 inside eight blocks β an address with nonce 37,787 that took deposits from 58 unrelated one-shot addresses in the same window, so a service sweeper, not him. It holds 716,527 USDT.
Interleaved through the whole laundering run are zero-value transfers to lookalikes of his own deposit addresses, matching first and last four hex. The man who emptied a chain's staking module spent the evening being address-poisoned.
Method: https://t.co/lgGbqX73Wc, https://t.co/rGnKVl8kz8 (which serves archive state via x-cosmos-block-height), https://t.co/rjj2b2jmck, and eth_getLogs on BNB Chain. Every number above is one call away.
The window was wider than the release. That fix went into cosmos/evm main on 2026-05-15 as PR #1176 β plain title, no advisory β and sat in the open for 96 days. v0.7.1 shipped in July without it.
So the Aug 19 backport is not when the bug became readable. It is when it became urgent, and the diff had been sitting there since spring for anyone who read commits instead of release notes.
On TAC it took the whole staking module. bonded_tokens_pool: 2,985,651,403.4047 TAC at block 24,662,147, zero at 24,662,148, in one transaction, for 0.675 TAC of gas. The 20 bonded validators still book all of it as staked.
He was out to BNB Chain thirty-two seconds later. The chain halted four hours after that.
Full reconstruction, with the underflow value and what he actually realised: https://t.co/OVOgwjU08t
TAC's entire staking module was emptied in one transaction.
bonded_tokens_pool held 2,985,651,403.4047 TAC at block 24,662,147. Zero at 24,662,148. The 20 bonded validators still book every token of it as staked.
The official line is "a drain, not a mintβ¦ 2,985,651,403 TAC was moved from one account to another." Both halves are true. The account is a module account β the one holding every validator's and every delegator's bond. "One account" is doing a lot of work.
The exploit is one transaction, at 19:46:37 UTC on Aug 22:
0xae4e9b708ecef134a18aef8a1da9b4d24aa2a0e87f98d02695beae588cda46fc
Contract 0x5711c2af77d5Ea8DCFDAD2F3ae3E17d9Dd37c978 calls the staking precompile at 0xβ¦0800 β delegate(address,string,uint256), for 1 utac β which is what pulls the bonded pool into the EVM statedb as a state object. Then it makes a plain value call to that pool's address, 0x4feA76427B8345861e80A3540a8a9D936FD39391, with
value = 115792089237316195423570985008687907853269984665637578388054179295181792313188
= 2^256 β 2,985,651,403,404,712,731,337,326,748
Minus the pool's exact balance, in uint256 wraparound. SubBalance underflows, the pool wraps to zero, and the difference lands in the attacker's wallet.
Total gas: 1.35 TAC charged, 0.675 refunded by the feemarket. 0.675 TAC to take a network's entire stake.
cosmos/evm patched exactly this on 2026-08-19 at 23:01 UTC, in v0.7.2 β "this release contains important security fixes." The patch adds a panic to stateObject.SubBalance when balance < amount.
The patch was not new. PR #1176, "fix: harden statedb balance and event amount handling", was merged to cosmos/evm main on 2026-05-15. Plain title, no advisory, sitting in the open. v0.7.1 shipped on Jul 27 without it. The backport landed on Aug 19 β 96 days later. There is still no advisory for it.
MANTRA's last block came about 24 hours after that release. TAC fell 2d 20h after it. A silent security release on an open repo is a countdown: the diff is public the moment it ships, and it points at the bug more clearly than any advisory would.
The supply claim does check out, though. utac supply sits on an unbroken inflation curve straight through the incident β 10,413,780,898.92 at height 24,000,000, 10,422,742,680.94 at 24,600,000, 10,423,719,038.07 at the halt. Nothing was minted. It was taken.
And the exit was finished long before the halt. 500,000,024 TAC went to BNB Chain via LayerZero at 19:47:09, thirty-two seconds after the drain. 2,485,651,574 more at 19:48:12. 49.9M to TON at 19:59:26. TAC halted at 23:58:11, four hours and eleven minutes late.
On BNB Chain he sold 1,208,500,000 TAC into a single pool across 55 swaps:
first β 5,000,000 TAC β 11,557.77 USDT ($0.00231155)
last β 40,000,000 TAC β 8,329.04 USDT ($0.00020823)
Total 950,293.45 USDT, average realised $0.00078634. The TON leg went through https://t.co/2icCOy0zk1 and DeDust for another 74,750.73 USDT.
Realised: ~$1,025,044. Notional at the pre-attack price: $7,566,640. Thirteen and a half percent.
1,662,322,352.70 TAC β 55.7% of the haul β is still sitting unsold in his BNB Chain wallet, because there is nothing left to sell it into. Another 65,100,988.59 is frozen on TAC by the halt. Both legs reconcile to the wei; the TAC side is off by 2.05 TAC, which is gas.
The proceeds left through nine single-use addresses. Eight of them were swept into 0xe647b0db71985b26a1562be585bf270ea4023b10 inside eight blocks β an address with nonce 37,787 that took deposits from 58 unrelated one-shot addresses in the same window, so a service sweeper, not him. It holds 716,527 USDT.
Interleaved through the whole laundering run are zero-value transfers to lookalikes of his own deposit addresses, matching first and last four hex. The man who emptied a chain's staking module spent the evening being address-poisoned.
Method: https://t.co/lgGbqX73Wc, https://t.co/rGnKVl8kz8 (which serves archive state via x-cosmos-block-height), https://t.co/rjj2b2jmck, and eth_getLogs on BNB Chain. Every number above is one call away.
Sources, all re-runnable:
Exploit tx (EVM view)
https://t.co/uyxmrOWuGG
Same tx, Cosmos events β the delegate, the bank transfer out of the module, the mint/burn hop
https://t.co/SzaRozMgAP
The pool, before and after β archive state, no key:
curl -H "x-cosmos-block-height: 24662147" https://t.co/h8PCB03z3W
curl -H "x-cosmos-block-height: 24662148" https://t.co/h8PCB03z3W
Supply on the same node, /cosmos/bank/v1beta1/supply/by_denom?denom=utac at any height.
bonded_tokens_pool
https://t.co/bc1OnSkqX7
Attacker, TAC side
https://t.co/HmhZdWMpg8
Attacker, BNB Chain side β same address, the 55 sells and the USDT
https://t.co/ca9ITmLrth
TON leg
https://t.co/lEGIlf4FHu
The patch, merged to main 2026-05-15
https://t.co/YrnK4RlfCI
The release that finally carried it, 2026-08-19
https://t.co/CTYi8ngbp4
For anyone holding frozen FOX: this thread is attracting accounts offering to "unlock" locked tokens. There is no such service.
The revert is a state inside the token contract. The only address that has ever changed it is the token's owner β none of these accounts is that address. Nobody can unlock your balance for a fee.
Never share a seed phrase, and never pay upfront for "recovery".
π¦ Fox Market β BSC, 2026-08-15
DeFiLlama logs $120K. That is the attacker's take, not the loss. The FOX/USDT pair is short 677,549 USDT β and FOX transfers were frozen 2 hours later, so nobody can price what is left.
The registry entry has no source link and no outlet has covered it. Two alert accounts wrote up the exploit tx and stopped there. Everything below is from the chain.
WHERE THE 677,549 USDT WENT
Only 118,902 reached the attacker. 558,647 went to the flash-loan venues as fees β PancakeSwap V2 pairs at exactly 0.25% a leg (36,555,218.89 borrowed β 91,617.09 kept = 0.2506%). The two figures plus the pool's deficit net to 0.00.
THE TWO ALERTS DISAGREE, AND ONE IS WRONG
Defimon says the attacker crushed the FOX spot price. They did the opposite. The pair is token0=USDT, token1=FOX, and its own Sync logs read:
before 2,786,697.20 USDT / 496,041.72 FOX β $5.6179
after the buy 243,774,089.97 / 5,684.54 β ~$42,882
stake() spent 240,987,392.77 USDT buying 490,357.18 FOX β 98.85% of every FOX in the pool β then priced the mint off the pre-swap snapshot. 481,974,785.54 staked / 88,659,280.81 sFOX = $5.4364 applied, the $5.44 the second alert quotes.
WHAT IT MINTED
FOX supply 1,732,319 β 93,051,378 in one tx. sFOX 1,203,470 β 89,862,751. A rebase at 00:00:02 added 9,752,563 FOX more, taking supply to 59Γ pre-attack. That last mint went to the protocol's own distributor, not the attacker.
THE MONEY NEVER MOVED
23:33:02 helper β attacker EOA
00:10:44 β 0x7da2af76β¦fee4 (an EIP-7702 account)
00:11:44 β 0x005806c0β¦31e2, 119,136.472192471 USDT
39 minutes, then nothing. It is all still there. No mixer, no bridge, no exchange.
Ignore the "U5DΠ’" and "Uα΅Sα΅DΞ€" transfers on both addresses β those are poisoning bots spoofing 0x7da2β¦fee4 and 0x0058β¦31e2.
THE TEAM'S ENTIRE RESPONSE IS ONE TRANSACTION
2h02m after the exploit, owner EOA 0x12896036β¦c55c called setTransferState(false) on FOX. Its nonce went 1883 β 1884 and has not moved since. The last FOX transfer ever is block 116185210, 44 seconds before the freeze. Zero since.
Frozen inside: 89,862,418 FOX in the treasury, 88,683,477 sFOX in the bond pool.
https://t.co/Qk51kJhOJb
Addresses
attacker EOA 0x5670d36f00bc7f6860b6afddb288e3668efc0ef9
helper 0x3a82a2a77061017927e5331fffd07c0308a1d2da
staker (7702) 0x7da2af76394b7c00ae46001e6139a316554cfee4
proceeds 0x005806c04ec29fe0740eb508c5f431c230b031e2
FoxLpBondsPool 0x9fa6d8a13b35e051bfc145918db0111dec13d1a0
FOX/USDT pair 0xaab18bcdee287aea288c0560612caadf7c328803
Confirmed your 2.94 WETH independently β net flow across the exploit contract is +2.941350352900037140 WETH and exactly 0 wei of ENS. Nothing to correct.
One figure that sits beside yours rather than against it: the vault's LPs are down more than the attacker took. Measured at one fixed tick so the ENS move is out of it, NAV went 28.4579 β 24.6586 WETH β β13.35%, β$9,219. The 0.86 WETH gap went to the 1% pool as fees. Share supply never changed, so all of it is per-share, and 92.67% of the shares belong to a single wallet that deposited in Feb 2022 and never came back.
Seventeen hours on, the proxy implementation slot is still the pre-attack one.
Full working here:
https://t.co/HbsSTVs9qf
π΄ Arrakis V1 Β· G-UNI ENS/WETH
The attacker cleared 2.94 WETH ($7,137). The vault's LPs lost $9,219 β 29% more than that. 93% of it fell on one wallet that deposited in February 2022 and has never touched the position since.
Every number in circulation is the attacker's take. The take is not the damage.
Priced at the same tick before and after, so the market move is out of it, the vault's NAV went 28.4579 β 24.6586 WETH. That is β3.80 WETH, β13.35%, β$9,219 at $2,426.45/ETH. The 0.86 WETH between the loss and the take went to the Uniswap pool as fees β the ENS/WETH pool this vault sits on is the 1% tier.
Share supply never changed: 324.7566 before, 324.7566 after. So the entire drop is per-share, and it lands on whoever was already inside. One wallet holds 300.9671 of those shares β 92.67%. It entered through a single ZapIn on 5 Feb 2022 and has not come back to the vault since. Its share of the loss is 3.52 WETH, β$8,544.
Seventeen hours later, nothing has moved:
Β· proxy implementation slot β same as the block before the attack
Β· position liquidity, idle balances, share supply β identical to the second the attack ended
Β· no rebalance, no withdrawal, no patch
One observation, offered as an observation. 0.05 ETH left the exploit contract to 0xdadB0d80β¦3711 β the builder of block 25817966, extraData "BuilderNet". The attack arrived as a private bundle and was never in the public mempool. The Arrakis risks page lists "real-time mempool exploit-monitoring solutions with emergency hack preventions" among the protocol's defences. Whether legacy V1 was ever inside that perimeter, we cannot tell from the chain.
Root cause is ExVul's and DeFiHackLabs', and it checks out against the deployed source: mint() and burn() value the position off pool.slot0() with no TWAP. A _checkSlippage() does exist in the contract β reachable only from rebalance(), which only the manager can call. The same shape sits in all four implementations behind the 105 live Arrakis V1 vaults, $1.29M between them.
https://t.co/KC8NVrSICU
Method, for anyone re-running it.
NAV is measured at one fixed price for both snapshots β position liquidity from pool.positions() plus idle balances, valued at tick 60333 β so the β13.35% is the hole, not the ENS price. At the end-of-block tick 59789 it reads β12.48%; the loss is 3.71β3.80 WETH either way.
The take closes exactly. Net token flow across the exploit contract: +2.941350352900037140 WETH and 0 wei of ENS. No leg missing. Gas for the whole operation, including a deploy: ~0.0058 ETH, about $14.
The vault's position was out of range when it was hit β tick 60333 against an upper bound of 60000, so it sat 100% in ENS earning nothing. The attacker minted 4,486.6191 shares against 324.7566 outstanding: 13.8x the entire vault in one transaction.
Three blocks earlier the same wallet deployed a byte-identical copy of the exploit contract, 0x5e3eaf56β¦b24c, and abandoned it.
Vault 0x7c687f775a3b73bbab0e15832f24caab5d53bdde
Attacker 0xa3B096e4df1247794599a37Af8F5b8CB05D5EB44
Exploit contract 0x028d9C17B1a097e7e115A6400203df86339BAf4a
Factory (105 V1 vaults) 0xEA1aFf9dbFfD1580F6b81A3ad3589E66652dB7D9
Credit: ExVul and DeFiHackLabs on the mechanism, SlowMist on the incident, DeFiLlama's registry for the $7.1K row that has no source link on it.
π΄ Arrakis V1 Β· G-UNI ENS/WETH
The attacker cleared 2.94 WETH ($7,137). The vault's LPs lost $9,219 β 29% more than that. 93% of it fell on one wallet that deposited in February 2022 and has never touched the position since.
Every number in circulation is the attacker's take. The take is not the damage.
Priced at the same tick before and after, so the market move is out of it, the vault's NAV went 28.4579 β 24.6586 WETH. That is β3.80 WETH, β13.35%, β$9,219 at $2,426.45/ETH. The 0.86 WETH between the loss and the take went to the Uniswap pool as fees β the ENS/WETH pool this vault sits on is the 1% tier.
Share supply never changed: 324.7566 before, 324.7566 after. So the entire drop is per-share, and it lands on whoever was already inside. One wallet holds 300.9671 of those shares β 92.67%. It entered through a single ZapIn on 5 Feb 2022 and has not come back to the vault since. Its share of the loss is 3.52 WETH, β$8,544.
Seventeen hours later, nothing has moved:
Β· proxy implementation slot β same as the block before the attack
Β· position liquidity, idle balances, share supply β identical to the second the attack ended
Β· no rebalance, no withdrawal, no patch
One observation, offered as an observation. 0.05 ETH left the exploit contract to 0xdadB0d80β¦3711 β the builder of block 25817966, extraData "BuilderNet". The attack arrived as a private bundle and was never in the public mempool. The Arrakis risks page lists "real-time mempool exploit-monitoring solutions with emergency hack preventions" among the protocol's defences. Whether legacy V1 was ever inside that perimeter, we cannot tell from the chain.
Root cause is ExVul's and DeFiHackLabs', and it checks out against the deployed source: mint() and burn() value the position off pool.slot0() with no TWAP. A _checkSlippage() does exist in the contract β reachable only from rebalance(), which only the manager can call. The same shape sits in all four implementations behind the 105 live Arrakis V1 vaults, $1.29M between them.
https://t.co/KC8NVrSICU
Good question β the proposal itself is the https://t.co/V4Dt04oQSP wasn't a proposal to approve anything. It was a proposal to veto. In Term's design a curator queues parameter changes and holders get a window to block them; if the window closes without a successful veto, the queued transaction becomes executable. So the attacker queued his own changes and opened the veto vote against himself. He didn't need a single YES. He needed nobody to reach one β and he owned 90.66% of the votes that could.The queued payload was 17 calls. The ones that matter:enableModule(0x0ae12af3β¦) on the vault's Safe 0x35c99cf4β¦ β installs a module with unrestricted execution rights over the vault. Verified: isModuleEnabled returns true at the drain block, false today.
Through that module, execTransactionFromModule β update_debt(strategy, 0, 10000) against the real strategies, incl. Shorewoods ETH 0x330732β¦ β target debt zero, recalling every deployed position back into the vault as idle WETH.
That idle WETH then goes into a "strategy" the attacker deployed at his own nonce 0 at 05:19:11 on Aug 17 β six minutes before he bought his shares. Its name, on-chain: "Fixed Recipient WETH Exit Strategy", ticker frWETH-EXIT.
Step 3 is why the theft is still invisible in the vault's numbers. A vault books a strategy at the value the strategy reports. His contract reports 2,841.7435 back, so totalAssets() reads 2,926.2159 ETH before and after the drain block, and the share price is still 1.0308. The WETH balance is 0.Nothing in it was clever. It was permitted.Proposal: https://t.co/fTJRIF8tkP
The $8.5M was not stolen past the vault's defences. The defence was bought for $951.
Term's vaults let share-holders veto a curator's parameter changes. But votes only count if you stake your shares, and on the block before the drain the entire staked supply was 0.5352 out of 2,838.95 β 0.019% of the vault.
The attacker bought 0.4852 of it for 0.5 ETH on Aug 17. That was 90.66% of every vote in existence. He opened the veto proposal himself, nobody voted, and it executed 12 seconds after the window closed.
The vault's own accounting still hasn't noticed: totalAssets() reads 2,926.2159 ETH before and after the drain block, share price still 1.0308. Its WETH balance is 0.
It cost $951.
0.5 ETH bought 0.4852 staked gtmvETH β 90.66% of every vote in the vault, because only 0.019% of shares had ever been staked.
Nobody voted. It executed on expiry.
It was not a vote, it was a veto. Term's Meta Vaults let a curator queue parameter changes and give LP holders a window to veto them. The attacker queued the change himself, then opened the veto proposal using Term's own boilerplate β "Vote YES to VETO the curator's proposed vault parameter changes." Voting power comes only from tmvETH staked into the governance vault, and pre-attack that was 0.5352 of 2,838.95 shares. The 99.94% holder held plain unstaked shares, so held zero votes. They staked 2,837.28 tmvETH at 07:59:23 β 94 minutes after the money was gone.
The marketed safeguard was the attack surface.
And the hole is still invisible in the vault's own books. ETH Meta Vault, 0x26fcb50eec367ddab060ccf5e7394cecd95f7db2, read at 10:31:15 UTC β four hours after the drain:
totalAssets() 2,926.215884 ETH
convertToAssets(1e18) 1.030751 ETH per share
actual WETH balance 0.000000
actual ETH balance 0.000000
What it holds instead is 2,841.743536 units of a token the attacker deployed at his own nonce 0 and named "Fixed Recipient WETH Exit Strategy". The strategy swap took the WETH and left the vault an IOU written by the person taking it, so totalAssets() never moved across the drain block. Same pattern in all five USDC vaults: every one of them reports the same or a higher totalAssets() after the drain than before, still accruing yield on assets that are gone.
Across the six victim vaults the books say $11.41M. $8.53M of it does not exist.
https://t.co/5QgGqAQy5J
π¨ Term Finance β $8.53M governance attack
The veto that was meant to stop it cost $951 to buy.
Voting power comes only from staked vault shares. 0.5352 of 2,838.95 were staked β 0.019%. The attacker held 90.66% of it.
Term's Strategy Vaults were sold on exactly this safeguard: "LP token holders retain veto power over risk parameter adjustments." The attacker did not beat the veto. He bought it.
How it worked
Voting rights on the ETH Meta Vault don't come from holding tmvETH. They come from staking it into the Governance ETH Meta Vault. On the block before the drain, the entire staked supply was 0.5352 gtmvETH against 2,838.95 tmvETH outstanding β 0.019% of the vault.
Aug 17, 05:19:11 UTC: the attacker deploys a contract at his own nonce 0. It is named "Fixed Recipient WETH Exit Strategy", ticker frWETH-EXIT.
05:21:47: he buys 0.4852 tmvETH for 0.5 ETH (~$951 at that hour's price) and stakes it. That is 90.66% of every vote in existence.
05:25:35: he opens the veto proposal β carrying Term's own boilerplate: "Vote YES to VETO the curator's proposed vault parameter changes. Otherwise, the transaction will become executable when this proposal expires."
Nobody voted. It expired Aug 23, 06:25:35. It executed at 06:25:47 β 12 seconds, one block, after the window closed.
The holder who could have stopped it
One EOA held 2,837.28 tmvETH β 99.94% of the vault. Unstaked, so zero votes for all 145 hours.
They staked it at 07:59:23 today. 94 minutes after the money was gone.
The books still show the money
The vault handed over 2,841.7435 WETH and received 2,841.7435 units of the attacker's own contract, which its accounting values 1:1.
totalAssets() before the drain block: 2,926.2159 ETH
totalAssets() after the drain block: 2,926.2159 ETH
WETH balance: 0
Share price: still 1.0308 ETH
maxDeposit: 47,073 ETH β deposits open (checked 09:45 UTC)
maxWithdraw: 0
The USDC leg is five more vaults
One transaction, 06:47:47, took 1,679,639.29 USDC:
Parity High Yield USDC v2 β 848,410.95
RockawayX Tori USDC β 454,046.26
Parity High Yield USDC β 348,877.20
Parity Core USDC β 14,172.39
Parity Prime USDC β 14,132.49
Converted to 1,679,642.45 DAI. Every one of the five reports the same or a higher totalAssets after the drain than before β they are still accruing yield on assets that left.
Across all six victim vaults the books publish $11.41M. $8.53M of it does not exist.
Two more captures are loaded and unfired
Eight takeovers were queued. Six went off. Two never did:
Parity Core ETH β veto window expired today 06:30:47
Parity Prime ETH β veto window expired today 06:36:47
Those two vaults hold 8.99 + 75.63 = 84.62 ETH (~$204K) β which is precisely the residual the emptied ETH Meta Vault still claims to own. The last of it sits behind two locks that were already picked. Windows don't reopen.
Funding
Tornado Cash 1 ETH pool, twice: 0.9945 ETH on Aug 17 05:01:11 and 0.9944 ETH on Aug 18 03:59:59. Both execution transactions carried a priority fee of exactly 0 β nothing ever hit the public mempool.
ETH drain: https://t.co/G7i6OKDrks
The $8.5M figure and the addresses were first published by CertiK Insight and @osint_based. The cost of the veto, the empty books and the two live windows are ours.
Addresses
Attacker 1: 0xa908b3472d76e7744baB0A5911768a4a6300612B
Attacker 2: 0x686457a7468B9B31c5dbA43b1b16077B48520691
Funds: 0xD5183d8BfC65a50863C62aF2538198A8288FFc13
Fake strategy: 0x184f2e57b4ce135181fa2a2166ac394339016338
ETH Meta Vault: 0x26fcb50eec367ddab060ccf5e7394cecd95f7db2
Governance ETH Meta Vault: 0x5b96c5bbdcb361e1e9944baa071b237e27829be0
Both, and each is one eth_call.
Canonical Ethereum totalSupply: 3,000,000,000 β unchanged through the entire incident.
Base OFT totalSupply right now: 327.57 trillion.
The headline was unbacked bridge balance.
Read at 2026-08-23 10:02:53 UTC, Base block 50,345,013. Do not take my count β call totalSupply() on 0xac531eb26ca1d21b85126de8fb87e80e09002dcf on Base and on 0x3845badade8e6dff049820680d1f14bd3903a5d0 on Ethereum. The two answers sit 109,000x apart and neither needs an indexer.
That gap is the whole answer to the second question. Nothing on Base can move canonical supply β the Ethereum side is an OFT Adapter, not a mint. It backs the satellites by holding real locked SAND, so the theft ceiling was never the minted figure. It was that balance:
14,769,723.07 SAND on Aug 21 07:05 β 0.005560 now
14,753,431.67 out in 15 events, 00:32:11β00:32:35 UTC Aug 22
~$675K notional, of which ~79.74 ETH was actually realized
https://t.co/tD0DFioa5q
So the $706M was price Γ a balance nothing stood behind. So is 327.57 trillion. The only number that ever moved real value was a 14.7M SAND lockbox that emptied in 24 seconds.
Status at time of reading: no mint in 29 hours, peers for eid 30101 and 30102 still zero, and the LayerZero delegate is still 0xa467cd7bβ¦7952, which is not an address the project controls. Containment is the peers, not a fix.
Two more takeovers from the same attacker are queued and were never executed.
Parity Core ETH β veto window expired today 06:30:47 UTC
Parity Prime ETH β expired 06:36:47 UTC
Those two hold 84.62 ETH, which is the entire residual the emptied ETH Meta Vault still claims to own. The strategy contract has a fixed recipient, so whoever fires it, it pays the same address as the first drain.
Also worth flagging: the ETH Meta Vault's totalAssets() reads 2,926.2159 ETH before and after the drain block. WETH balance is 0. It took 2,841.74 units of the attacker's own contract in exchange and books them 1:1.
π¨ Term Finance β $8.53M governance attack
The veto that was meant to stop it cost $951 to buy.
Voting power comes only from staked vault shares. 0.5352 of 2,838.95 were staked β 0.019%. The attacker held 90.66% of it.
Term's Strategy Vaults were sold on exactly this safeguard: "LP token holders retain veto power over risk parameter adjustments." The attacker did not beat the veto. He bought it.
How it worked
Voting rights on the ETH Meta Vault don't come from holding tmvETH. They come from staking it into the Governance ETH Meta Vault. On the block before the drain, the entire staked supply was 0.5352 gtmvETH against 2,838.95 tmvETH outstanding β 0.019% of the vault.
Aug 17, 05:19:11 UTC: the attacker deploys a contract at his own nonce 0. It is named "Fixed Recipient WETH Exit Strategy", ticker frWETH-EXIT.
05:21:47: he buys 0.4852 tmvETH for 0.5 ETH (~$951 at that hour's price) and stakes it. That is 90.66% of every vote in existence.
05:25:35: he opens the veto proposal β carrying Term's own boilerplate: "Vote YES to VETO the curator's proposed vault parameter changes. Otherwise, the transaction will become executable when this proposal expires."
Nobody voted. It expired Aug 23, 06:25:35. It executed at 06:25:47 β 12 seconds, one block, after the window closed.
The holder who could have stopped it
One EOA held 2,837.28 tmvETH β 99.94% of the vault. Unstaked, so zero votes for all 145 hours.
They staked it at 07:59:23 today. 94 minutes after the money was gone.
The books still show the money
The vault handed over 2,841.7435 WETH and received 2,841.7435 units of the attacker's own contract, which its accounting values 1:1.
totalAssets() before the drain block: 2,926.2159 ETH
totalAssets() after the drain block: 2,926.2159 ETH
WETH balance: 0
Share price: still 1.0308 ETH
maxDeposit: 47,073 ETH β deposits open (checked 09:45 UTC)
maxWithdraw: 0
The USDC leg is five more vaults
One transaction, 06:47:47, took 1,679,639.29 USDC:
Parity High Yield USDC v2 β 848,410.95
RockawayX Tori USDC β 454,046.26
Parity High Yield USDC β 348,877.20
Parity Core USDC β 14,172.39
Parity Prime USDC β 14,132.49
Converted to 1,679,642.45 DAI. Every one of the five reports the same or a higher totalAssets after the drain than before β they are still accruing yield on assets that left.
Across all six victim vaults the books publish $11.41M. $8.53M of it does not exist.
Two more captures are loaded and unfired
Eight takeovers were queued. Six went off. Two never did:
Parity Core ETH β veto window expired today 06:30:47
Parity Prime ETH β veto window expired today 06:36:47
Those two vaults hold 8.99 + 75.63 = 84.62 ETH (~$204K) β which is precisely the residual the emptied ETH Meta Vault still claims to own. The last of it sits behind two locks that were already picked. Windows don't reopen.
Funding
Tornado Cash 1 ETH pool, twice: 0.9945 ETH on Aug 17 05:01:11 and 0.9944 ETH on Aug 18 03:59:59. Both execution transactions carried a priority fee of exactly 0 β nothing ever hit the public mempool.
ETH drain: https://t.co/G7i6OKDrks
The $8.5M figure and the addresses were first published by CertiK Insight and @osint_based. The cost of the veto, the empty books and the two live windows are ours.
Addresses
Attacker 1: 0xa908b3472d76e7744baB0A5911768a4a6300612B
Attacker 2: 0x686457a7468B9B31c5dbA43b1b16077B48520691
Funds: 0xD5183d8BfC65a50863C62aF2538198A8288FFc13
Fake strategy: 0x184f2e57b4ce135181fa2a2166ac394339016338
ETH Meta Vault: 0x26fcb50eec367ddab060ccf5e7394cecd95f7db2
Governance ETH Meta Vault: 0x5b96c5bbdcb361e1e9944baa071b237e27829be0
Measured it rather than guessed: 1.71 ETH.
That is the entire gas cost of the campaign that took the $2M β 126,339 baits across 21,977 transactions, about 13.6 micro-ETH per bait. Roughly four cents to lay one.
Method: pulled every dust transfer the controller ever sent, deduped to 21,977 transactions, sampled 45 receipts and summed gasUsed Γ effectiveGasPrice. Mean 0.000078 ETH per tx, median 0.000025, and they batch a median of 2 baits into each one.
So the return on the whole three-month operation is roughly $2,000,000 against $6,000 of gas. That is why the hit rate can sit near zero and the model still works. It isn't a business that needs to win often. It needs to win once.
ON YOUR SECOND POINT β you're right, and I should be clearer about what I actually measured.
1,206 is my sample, not the fleet. I never enumerated fleet two's full address count; I pulled 34 of its batches spread across six months and collected every lookalike in them. Its real size is larger and I don't have the number.
So "zero over $1,000" means zero in those 1,206, not zero overall. With no hits in 1,206 draws the ceiling on the hit rate is about 0.25%, and across a fleet that size 0.25% leaves plenty of room for wins I simply haven't looked at yet.
And your instinct is the right one. Nobody burns 170 ETH for nothing β especially not when the crew next door turned 1.71 ETH into $2M. That gap is the argument that fleet two has been paid too, and that I haven't found where. I'd rather say that than dress a sample up as a conclusion.
The bot that stole $2M address-poisoned itself
13 minutes after parking the stolen DAI, the same controller that funded the theft address dusted the thief's own wallet - the same 0.0008 -> 0.0002 relay it ran on the victim.
https://t.co/8uSckrLNmF
THE SELF-POISONING
Controller 0xedda4e01β¦4143 runs one fixed pattern: a single tx sends 0.0008 to a freshly mined lookalike, which relays 0.0002 to the target.
Jul 21 14:45:23 β 0xf0e63433β¦61af, 8 min after the victim's real payment
Aug 20 20:20:59 β 0xf0e6a496β¦21af, 8m36s after the victim's real payment. This one took the $2M.
Aug 21 23:44:23 β 0xe2ebba3eβ¦416a, 13m12s after the thief's own DAI move β dusting his own swap wallet 0x692729bcβ¦7251.
Same controller on both ends. The bot saw 1,999,939 DAI leave an address, mined a lookalike of the destination, and poisoned the sender. The sender was itself.
THE BAIT
The victim ran a recurring $2,000,000 USDC payment to 0xf0e67a18β¦b1af β Jul 21, then Aug 20, each funded by a Compound v3 withdrawal minutes earlier.
Both times, forged 2,000,000 USDC entries appeared in the wallet's history within minutes, sent from lookalikes of that payee by homoglyph token contracts (ΓΠ DΠ‘, USΝDC, USα DC β Cyrillic and invisible characters).
Jul 21: first forgery 4m48s after the real transfer. No bite.
Aug 21 16:31:11: the payment repeated, into 0xf0e6a496β¦21af.
THE CLOSEST MATCH LOST
Three separate operations worked this victim in August.
0xf0e620bdβ¦b1af β 4 prefix + 4 suffix β operator 0x7d459a40β¦ab22 via batcher 0x2a9617a4β¦dd7f
0xf0e6d532β¦b1af β 4 prefix + 4 suffix β operator 0xd6434d15β¦6908 via batcher 0x7ec8a30aβ¦bef7
0xf0e6a496β¦21af β 4 prefix + 3 suffix β the fleet β took the $2M
The two closer matches have zero transactions and were never funded. They exist only as the destination of zero-value transferFrom calls on real USDC and of forged fake-token events.
The winner was the only one whose entry moved a real, non-zero balance: 0.0002 USDC.
THE FLEET
0xf0e6a496β¦21af is an EIP-7702 EOA delegated to sweeper 0xecad547eβ¦8469 β deployed 2026-05-26 07:53:59, gas-golfed, recipient and amount packed into one uint256, hardcoded paths for USDT, DAI and USDC, callable only by the controller.
That controller has sent 126,339 dust transfers to 80,663 distinct addresses between May 26 08:55 and Aug 22 01:07 UTC.
120 of 120 randomly sampled recipients carry the identical delegation to the same sweeper.
63,354 of them were used exactly once.
THE FORGERY IS A SERVICE
Contract 0xde39ef67β¦27de, deployed Aug 19 08:02 by 0x161643f2β¦5003, its only caller.
320 transactions in 60 hours, a median of 278 Transfer logs each β roughly 89,000 forged history entries, batched across many fake token contracts at once.
THE MONEY
Aug 21 23:24:47 β 2,000,000.0006 USDC swept to 0x692729bcβ¦7251
Aug 21 23:28:35 β swapped via CoW Protocol to 1,999,939.4763 DAI
Aug 21 23:31:11 β parked at 0xe2ebfd6fβ¦1816a
Still there. Nonce 0. Block 25807397.
ADDRESSES
Victim 0x7ba7f4773fa7890bad57879f0a1faa0edffb3520
Real payee 0xf0e67a1896e814e30c011e36174de28caa9ab1af
Spoof 0xf0e6a49668de1195b931a3717c9cc36fc19721af
Swap wallet 0x692729bcd0887b8d02b8ff3169220ba0f4e17251
DAI vault 0xe2ebfd6f329a6330ab7eee68ce1328c21d31816a
Self-poison 0xe2ebba3e64f25f8badf35d2760473748d673416a
Sweeper 0xecad547e905892ff19d162ca57b91f0fecf78469
Controller 0xedda4e01669d30faa04a9cb75488abc366ee4143
Forger 0xde39ef679e12574279e3ed35de4b0721beae27de
Mechanism first reported by PeckShield.
Agreed on the facts β the tests are there, #5101 added cases in quorom_test.go, blockchain_impl_test.go and engine_test.go. But my open question isn't whether it's fixed. It's which hole the attacker actually went through.
The two answers say different things. Receipt replay alone means ONE was minted through a bookkeeping bug in spent-marker keys. If the quorum check was also exercised, it means a header that nothing had signed was accepted on mainnet β a different class of statement about what the chain will take. That's why I keep pointing at the all-zero bitmaps in the receipts rather than at the diff.
Which makes your last point the right one. The patch tells us the holes are closed. Only the postmortem tells us what walked through them.