Calculated from recorded token losses using historical USD prices at the incident time.
0xf477089602fefcfc1dbdce15834476267914d64a1e6a52f07d3f135f091e1d270x31bfa137c76561ef848c2af9ca301b60451caac0BSC0xf43fd71f404cc450c470d42e3f478a6d38c96311BSCAn unprivileged BSC attacker exploited GGGTOKEN and the PancakeSwap GGG-USDT pair in transaction 0xf477089602fefcfc1dbdce15834476267914d64a1e6a52f07d3f135f091e1d27 at block 35702096. The attacker took a public DODO flash loan, bought GGG, repeatedly triggered GGGTOKEN's sell hook with dust transfers, and then sold the inflated GGG position back into the pair. The result was a collapse of the pair's USDT reserve from 61782429808822044132222 to 116880749382901799296, while the attacker EOA finished with 51722572974878450156452 raw USDT units.
The root cause is that GGGTOKEN stores LP-penalty state in the global variable removePoolAmount, but its sell path reuses that value every time to == _uniswapV2Pair. autoBurnLiquidityPairTokens() burns the pair's GGG inventory and calls sync(), yet never clears removePoolAmount. That makes the burn action replayable by any ordinary seller and lets a permissionless attacker rewrite pool reserves to an attacker-chosen price.
GGGTOKEN is not a plain ERC-20. Its transfer flow contains liquidity-detection branches, fee logic, and an LP punishment feature named autoBurnLiquidityPairTokens(). When the contract believes liquidity was removed too soon, it accumulates a penalty amount in removePoolAmount.
The vulnerable pair is the PancakeSwap pool at 0xf43fd71f404cc450c470d42e3f478a6d38c96311, and the token contract is . The exploit matters because the token contract is allowed to move balances belonging to the pair itself during an ordinary sell flow. That is a direct coupling between user-triggerable token logic and AMM reserve accounting.
0x31bfa137c76561ef848c2af9ca301b60451caac0The vulnerability is a replayable reserve-destruction bug in the token's sell hook. In GGGTOKEN, _transfer() enters the sell branch whenever the recipient is the Pancake pair and the transfer is not classified as add-liquidity. Inside that branch, the code checks whether removePoolAmount > 0 and, if so, calls autoBurnLiquidityPairTokens().
autoBurnLiquidityPairTokens() transfers removePoolAmount tokens from the pair to 0xdead and immediately calls pair.sync(). That is already dangerous because the burn amount is not bounded by the seller's current transfer amount. The fatal defect is that removePoolAmount is never decremented or reset after the burn, so every later sell can replay the same reserve reduction. Once a public pre-state exists with removePoolAmount armed, any permissionless trader can submit dust sells to trigger repeated pair burns and force a distorted exchange rate before dumping inventory into the manipulated pool.
The key victim-side logic is visible in the verified GGGTOKEN source:
} else if(!_isAddLiquidity()&& getSellFee() > 0 && to==_uniswapV2Pair){//sell
require(amount <= _maxSellTax, "Transfer limit");
amount = takeSell(from,amount);
if (removePoolAmount > 0) {
autoBurnLiquidityPairTokens();
}
}
function autoBurnLiquidityPairTokens() internal returns (bool) {
if (removePoolAmount > 0) {
_basicTransfer(_uniswapV2Pair, address(0xdead), removePoolAmount);
}
IUniswapV2Pair pair = IUniswapV2Pair(_uniswapV2Pair);
pair.sync();
emit AutoNukeLP();
return true;
}
The exploitable pre-state already existed before the attacker acted. At block 35702095, the forkable state exposed removePoolAmount == 847550203936044874707, meaning the penalty accumulator had been armed by earlier liquidity-removal behavior and was publicly readable on chain. The same pre-state still showed deep USDT liquidity in the Pancake pair, making a profitable manipulation possible.
The attacker then used transaction 0xf477089602fefcfc1dbdce15834476267914d64a1e6a52f07d3f135f091e1d27 to realize the bug end-to-end. The trace shows a public flash loan from DODO pool 0x6098a5638d8d7e9ed2f952d35b2b67c34ec6b476, followed by a router buy of GGG through Pancake router 0x10ed43c718714eb63d5aa57b78b54704e256024e. After acquiring GGG, the attacker helper contract repeatedly called GGGTOKEN.transfer(pair, 1). Because each dust transfer satisfied the sell-path condition to == _uniswapV2Pair, each one re-entered the same vulnerable branch and re-executed autoBurnLiquidityPairTokens().
The trace records a long run of repeated AutoNukeLP() events during this loop, which is consistent with the attacker replaying the same stored burn amount on every dust sell. Each replay burned 847550203936044874707 raw GGG units from the pair and then synchronized the pair reserves. This repeatedly reduced the pair's GGG side without the attacker needing to contribute an equivalent amount in that step. Once the pair price was sufficiently distorted, the attacker sold 306516339154211173165709 raw GGG units back into the pair and extracted 151722572974878450156452 raw USDT units, enough to repay the flash loan and keep a large surplus.
The relevant on-chain consequences are directly observable in the balance diff. The Pancake pair lost 61665549059439142332926 raw USDT units during the transaction, while the attacker EOA gained 51722572974878450156452 raw USDT units after loan repayment. Those balance changes match the claimed invariant violation: an LP penalty bucket that should have been consumed once instead became a public, replayable reserve destruction primitive.
The externally owned account 0xc4f82210c2952fcec77efe734ab2d9b14e858469 sent the exploit transaction and ultimately received the profit. The transaction first deployed or invoked the helper contract 0xe16befae75edf6760f2f4be5cb78331a4da42d16, which handled the flash-loan callback and swap orchestration.
The execution flow was:
100000000000000000000000 raw USDT units from DODO pool 0x6098a5638d8d7e9ed2f952d35b2b67c34ec6b476.0x10ed43c718714eb63d5aa57b78b54704e256024e.0xf43fd71f404cc450c470d42e3f478a6d38c96311, causing repeated AutoNukeLP() emissions and repeated burns of the pair's GGG balance.151722572974878450156452 raw USDT units.51722572974878450156452 raw USDT units to the originating attacker EOA.The exploit is ACT because every dependency was public and permissionless: the flash-loan source was open, the vulnerable pre-state was readable on chain, the sell path had no privileged gate, and the attacker only used ordinary swaps and transfers available to any trader.
The directly measured victim-side loss was depletion of the Pancake pair's USDT reserves. The pair's USDT balance fell by 61665549059439142332926 raw units, leaving only 116880749382901799296 raw USDT units in the pool after the exploit transaction. This effectively destroyed the stablecoin side of the market and imposed the loss on liquidity providers exposed to that pair.
The attacker realized 51722572974878450156452 raw USDT units of post-repayment profit. The damage was not limited to temporary price movement; the token hook permanently burned pair inventory and synchronized the manipulated reserves on chain.
0xf477089602fefcfc1dbdce15834476267914d64a1e6a52f07d3f135f091e1d270x31bfa137c76561ef848c2af9ca301b60451caac00xf43fd71f404cc450c470d42e3f478a6d38c963110x6098a5638d8d7e9ed2f952d35b2b67c34ec6b4760x10ed43c718714eb63d5aa57b78b54704e256024eGGGTOKEN::_transfer() and GGGTOKEN::autoBurnLiquidityPairTokens()