Calculated from recorded token losses using historical USD prices at the incident time.
0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a30x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8cBSC0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348BSCOn BSC block 23106507, transaction 0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3 exploited BBOXToken at 0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c and its Pancake pair 0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348. The attacker borrowed 3043124720737281210661 wei of WBNB from the DODO pool 0x0fe261aee0d1c4dfddee4102e82dd425999065f4, bought BBOX, routed the purchased BBOX through an attacker-controlled helper contract, and sold back into the manipulated pool.
The root cause is a token-side accounting bug inside BBOXToken._transfer. When pairAmountChange is enabled, any transfer whose sender is not marked as an AMM pair can burn pairAmount directly from uniswapV2Pair and immediately call sync(). That lets an unprivileged attacker reduce pair-held BBOX without paying the corresponding WBNB cost, distort the AMM price, and extract WBNB. Collector balance diffs and receipt logs show a final WBNB profit transfer of 38225164664108721893 wei to the attacker profit sink, while the attacker EOA paid 3838390000000000 wei in gas.
BBOXToken is a fee-on-transfer token that tracks an accumulator named . On buys and sells involving the Pancake pair, the token increments this accumulator by . The accumulator is not settled inside the same AMM trade that created it.
pairAmountamount * 3 / 100The pair at 0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b348 is a standard constant-product pool over BBOX and WBNB. Its price depends on live reserves after token balances are synchronized. Receipt log 0x149 shows the pair sync after the buy leg, and receipt log 0x14c shows a second sync after the unauthorized burn path. The second sync reduces the BBOX-side reserve while leaving the WBNB reserve unchanged at that moment.
The attack did not depend on privileged access. The adversary used a normal EOA (0x32b639c180b130ed6b785d622b5c56ff48bbdf83), a predeployed exploit executor (0xa792a5f442b07b3cb8da5867c4e6940aa85c5bb5), a helper contract created inside the exploit transaction (0xcc195b501507824b3449b3f43f2f30916fed7150), Pancake router 0x10ed43c718714eb63d5aa57b78b54704e256024e, and the permissionless DODO flash-loan pool.
The vulnerability is an attack-class token accounting flaw, not a flash-loan issue. The invariant that should hold is: a user transfer that does not originate from the AMM pair must not be able to reduce the pair's token inventory or move the pool price, except through legitimate AMM entrypoints.
BBOXToken._transfer breaks that invariant by coupling deferred pairAmount settlement to the next transfer sent by any non-AMM address. When the guarded branch executes, it subtracts pairAmount from _tOwned[uniswapV2Pair], credits the zero address, emits a burn Transfer, and calls IUniswapV2Pair(uniswapV2Pair).sync(). This means a regular helper-to-attacker transfer can directly mutate third-party pool balances and force reserve recalculation.
The design is exploitable because the attacker can first make a pair trade that grows pairAmount, then trigger the deferred branch from a non-AMM helper address, and finally sell against the manipulated price. The code-level breakpoint is the branch guarded by pairAmountChange && !isAddLiquidity && pairAmount > 0 && !ammPairs[from] && pairAmount < balanceOf(uniswapV2Pair) inside _transfer.
The relevant victim-side code is:
if (
pairAmountChange
&& !isAddLiquidity
&& pairAmount > 0
&& !ammPairs[from]
&& pairAmount < balanceOf(uniswapV2Pair)
) {
uint v = pairAmount;
pairAmount = 0;
_tOwned[uniswapV2Pair] = _tOwned[uniswapV2Pair].sub(v);
_tOwned[address(0)] = _tOwned[address(0)].add(v);
emit Transfer(uniswapV2Pair, address(0), v);
IUniswapV2Pair(uniswapV2Pair).sync();
}
The same function later increases pairAmount on AMM-facing trades:
if (takeFee && ammPairs[from]) {
pairAmount += amount * 3 / 100;
}
if (takeFee && ammPairs[to]) {
pairAmount += amount * 3 / 100;
require(amount <= balanceOf(from) * sellSwapLimitRate / 100, "sell limit amount");
}
The exploit sequence is deterministic:
3043124720737281210661 wei WBNB from the DODO pool.1302098097375127777618 wei WBNB to buy BBOX into a helper contract. This trade increases pairAmount.0x149 is a Sync with BBOX reserve 0x4b96d4baae9 and WBNB reserve 0x48cdbefd9aeae61170 after the buy leg. Log 0x14c is the next Sync with BBOX reserve 0x39039c9a2a while the WBNB reserve remains 0x48cdbefd9aeae61170.0x15a, and transfers profit at receipt log 0x15c.The collector balance diff confirms the economic effect. The pair's BBOX balance falls from 170180295995008 to 123576092647681, a net loss of 46604203347327 raw BBOX units. The attacker's profit sink receives 38225164664108721893 wei WBNB, matching the success predicate in root_cause.json.
The adversary cluster contains:
0x32b639c180b130ed6b785d622b5c56ff48bbdf83, which deployed the exploit contracts and sent the exploit transaction.0xa792a5f442b07b3cb8da5867c4e6940aa85c5bb5, created in tx 0x4f7fc31a16058af040b1c888e141ff12ce617c6895d7cf7a8693b44481e1b2dc.0xcc195b501507824b3449b3f43f2f30916fed7150, created inside the exploit transaction.0xfc6d885c09f66724ba7b0e12a012d6b6bff7c84f, created in tx 0x4a3179044033b0ed91c728b839730b1288e3f37d02e68e6a7870ecec34941f66.The end-to-end on-chain flow is:
EOA -> Executor::fallback/entrypoint
Executor -> DODO::flashLoan(WBNB)
Executor -> PancakeRouter::swap WBNB -> BBOX (recipient = Helper)
Helper -> BBOXToken::transfer(Executor) // triggers pair burn + sync
Executor -> PancakeRouter::swap BBOX -> WBNB
Executor -> DODO pool (repay principal)
Executor -> Profit sink (transfer residual WBNB)
The receipt corroborates each stage. Logs 0x140 to 0x14a cover the flash-loan funding and buy leg. Logs 0x14d to 0x150 show the helper release and token-side burn activity. Logs 0x151 to 0x15d show the sell leg, flash-loan repayment, and profit transfer.
The measurable loss is WBNB extracted from the BBOX/WBNB pool.
WBNB3822516466410872189318This value was realized in the same exploit transaction after the principal was repaid. The pair also suffered a large BBOX inventory reduction: collector state diffs show the pair's BBOX balance decreased by 46604203347327 raw units. The final post-exploit reserves observed in the validated PoC were 123599336714446 BBOX and 2360523045633513214 wei WBNB, confirming severe reserve depletion.
0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a30x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c0x7a2d2ec352ae6d5e4b5d74918d594e2a0a80b3480x0fe261aee0d1c4dfddee4102e82dd425999065f4artifacts/collector/seed/56/0x5dfc7f3ebbb9cbfe89bc3fb70f750ee229a59f8c/src/Contract.solartifacts/collector/seed/56/0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3/metadata.jsonartifacts/collector/seed/56/0xac57c78881a7c00dfbac0563e21b5ae3a8e3f9d1b07198a27313722a166cc0a3/balance_diff.jsonartifacts/auditor/iter_0/receipt.jsonartifacts/validator/forge-test.log