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 hash | 0x38c60844f1923d53fdce7d99e9bc2a2d8c73a08e793341dfbb479f8cc74d81d0↗ |
| Block | 26263022 |
| Timestamp | 2026-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. |
| Addresses | Token | TokenID | Balance | Value in USD | Total 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 | - | - |
ranked() and candidacyFee() from the Freezer.applyForSenate(). Both immediately rank into the senate (Ranked events).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.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.
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.
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.
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)
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 |
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) |