This is a lower bound: only assets with reliable historical USD prices are counted, so the actual loss may be higher.
0x783e2f71d8967bdee8aa2ba0f3b9f402ac871365Ethereum0x8f159f13f64db18b8a0742c86fa8b225cead6c5dEthereum0x899b11881f977aeb5d9fac5105ce62c877f11763Ethereum0x9eaaeab7255296e68ad1f12b969b9e30d1806c9dEthereum0xc21a3b81efbba41dd319191b07a20eb1f5eebd61Ethereum0x0a78fbeb89ee251c0d78e0eeb5e6bb7524a8939fEthereum0x512e9701d314b365921bcb3b8265658a152c9ffdEthereumAt Ethereum block 17646142, EOA 0xc0ccff0b981b419e6e47560c3659c5f0b00e4985 used freshly deployed helper contract 0xf466f9f431aea853040ef837626b1c59cc963ce2 to drain previously approved balances from multiple token holders through router 0x7caec5e4a3906d0919895d113f7ed9b3a0cbf826. The attacker contract finished the transaction holding BONE, WOOF, SANI, 0NE, CELL, and USDC that had been pulled directly from victim EOAs via transferFrom.
The root cause is a public approved-spender surface in 0x7cae. Its public selector 0x5ffe72b7 accepts a caller-chosen pool and later trusts that pool inside uniswapV3MintCallback, where the router spends from a payer decoded out of callback data. Because neither the entrypoint nor the callback authorizes the payer or constrains the recipient pool to a trusted set, any public caller can redirect balances from any address that already approved 0x7cae.
0x7cae is a long-lived public router, not a single-use attacker deployment. Its creation metadata shows it was deployed in tx 0xcf8e9b4005763e5cbe8d5695a18b16005ab4e115894e203352bcad59e38705ae, and the collected Etherscan tx list shows successful public calls to selector 0x5ffe72b7 well before the incident. This matters because the exploit surface was already exposed to any searcher that inspected public state and bytecode.
The disassembly shows the router stores WETH in slot and only checks that a caller-supplied pool reports WETH as or . An attacker can satisfy that requirement with a minimal custom pool contract. The helper contract was therefore only an execution wrapper: it is not the vulnerability, and the ACT property comes from the public router itself.
0xf2cdd8b147802a07f862c9dc125190e0653795a20x7e585b185fc67bc5f815b7abf459300418aa9f97Ethereum0x26d61e57c44525d25aad4ef20bce3f7aa9d64c4cEthereum0x7b05363f549c929c3da930f6728e3d74806e4103Ethereum0x98token0()token1()0xf466The victim side is a set of EOAs that had granted allowances to 0x7cae. The updated auditor artifact victim_code_check.txt confirms the listed affected holder addresses have no runtime code, so the incident is not explained by privileged victim contracts or hidden stakeholder logic.
This is an ATTACK-class ACT exposure centered on missing authorization in a public spender path. The critical invariant is straightforward: if router 0x7cae is approved as an ERC20 spender, any external execution path that spends from payer must verify that the caller is authorized to act for that payer and that the payout recipient is an approved route. The disassembly-derived breakpoint is the chain 0x5ffe72b7 at offset 0x067a -> uniswapV3MintCallback at offset 0x1117 -> pay() at offset 0x1bdd -> safeTransferFrom() at offset 0x20f6.
At 0x067a, the router stores a caller-supplied pool in slot 0x97 and immediately calls pool.mint(address(this), ...). At 0x1117, the callback only checks that msg.sender == sload(0x97), so any pool chosen by the attacker is trusted for that execution. From there, pay() routes to safeTransferFrom() whenever payer != address(this), and the payer comes directly from callback data. That lets a public caller cause transferFrom(victim, attacker-controlled recipient, amount) as long as the victim has already approved 0x7cae.
Observed router flow from the disassembly:
0x067a: public 0x5ffe72b7 stores caller-supplied pool in slot 0x97 and calls pool.mint(address(this), ...)
0x1117: uniswapV3MintCallback only checks msg.sender == slot 0x97
0x1bdd: pay() dispatch
0x20f6: safeTransferFrom(token, payer, pool, amount) when payer != address(this)
The seed transaction 0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6b proves the exploit path on-chain. The call trace repeatedly shows the helper calling router selector 0x5ffe72b7, the router invoking the helper-controlled mint, and the callback forcing token spends from victim EOAs.
Observed seed trace excerpt:
0xF466...::0a5fede2(...)
0x7CAE...::5ffe72b7(...)
0xF466...::mint(0x7CAE..., 0, 0, 0, ...)
0x7CAE...::uniswapV3MintCallback(0, 1900000000, 0x0000000000000000000000007b05363f549c929c3da930f6728e3d74806e4103)
0xA0b86991...::transferFrom(0x7b05363f549c929C3dA930f6728e3D74806E4103, 0xF466F9f431aEa853040EF837626b1c59CC963ce2, 1900000000)
That exact USDC leg is mirrored in the balance diff. Victim 0x7b05363f549c929c3da930f6728e3d74806e4103 drops from 1900052720 to 52720, while helper 0xf466... rises from 0 to 1900000000. The same trace pattern repeats across the other affected tokens and holders. The router is therefore not merely interacting with attacker code; it is actively exercising its own spender allowance against third-party payers supplied in callback data.
The exploit conditions in the structured root cause hold against the collected evidence:
0x7cae.transferFrom for 0x7cae.The fork PoC independently validates the mechanism without using the incident attacker identities. It recreates the historical USDC drain against the same approved victim with a fresh attacker EOA and a fresh locally deployed pool shim, which is consistent with the ACT framing.
The attack unfolded in two adversary-crafted transactions.
0x208f27f35fe302366252741c8cebe0d8861859ae9d7b17f8b2c62156bf5c8e4f at block 17646141 deployed helper 0xf466f9f431aea853040ef837626b1c59cc963ce2.0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6b at block 17646142 used that helper to batch 31 callback-driven drains across previously approved holders.The helper's job was operational, not privileged. For each drain leg it selected a token, victim address, and amount, then called the public router path. The router accepted the helper as the pool, entered uniswapV3MintCallback, decoded the victim from callback data, and executed transferFrom into the helper. The helper therefore accumulated the drained balances solely because the public router transferred them there.
The adversary-related accounts are defensibly identified:
0xc0ccff0b981b419e6e47560c3659c5f0b00e4985: deploying EOA and sender of the seed drain transaction.0xf466f9f431aea853040ef837626b1c59cc963ce2: freshly deployed helper contract and sole recipient of drained balances.The affected holder set in the submitted analysis contains 11 EOAs: 0x783e2f71d8967bdee8aa2ba0f3b9f402ac871365, 0x8f159f13f64db18b8a0742c86fa8b225cead6c5d, 0x899b11881f977aeb5d9fac5105ce62c877f11763, 0x9eaaeab7255296e68ad1f12b969b9e30d1806c9d, 0xc21a3b81efbba41dd319191b07a20eb1f5eebd61, 0x0a78fbeb89ee251c0d78e0eeb5e6bb7524a8939f, 0x512e9701d314b365921bcb3b8265658a152c9ffd, 0xf2cdd8b147802a07f862c9dc125190e0653795a2, 0x7e585b185fc67bc5f815b7abf459300418aa9f97, 0x26d61e57c44525d25aad4ef20bce3f7aa9d64c4c, and 0x7b05363f549c929c3da930f6728e3d74806e4103.
The seed transaction drained balances from 11 holder EOAs across six ERC20 assets. The collected balance diff attributes the following net losses to the unauthorized router-spend path:
BONE: 20121200201296387000000WOOF: 37656666000000000000000000SANI: 24609683809494049098545523490NE: 2584625207128315973299900000000CELL: 10966052921140276000000USDC: 1900000000For 0NE, a portion of the transfer flow was routed to tax and burn destinations during transferFrom, but the holder-side negative delta still originates from the same unauthorized router callback path. The attacker-controlled helper contract ended the transaction holding the captured balances associated with the direct recipient legs, including the full 1900 USDC reproduced by the PoC.
0x208f27f35fe302366252741c8cebe0d8861859ae9d7b17f8b2c62156bf5c8e4f0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6b0xcf8e9b4005763e5cbe8d5695a18b16005ab4e115894e203352bcad59e38705ae0x549b4212b808cd6b6e6952cb5e0f80798a60d4e34cc0e41fcaf0e50a91263dbb and other successful 0x5ffe72b7 calls in the collected Etherscan tx list0xc42fc0e...7cae_disassembled.asm and f466_disassembled.asmartifacts/auditor/iter_1/victim_code_check.txtpoc/test/Exploit.sol