Calculated from recorded token losses using historical USD prices at the incident time.
0xeb4eb487f58d39c05778fed30cd001b986d3c52279e44f46b2de2773e7ee1d5e0xb1da08c472567eb0ec19639b1822f578d39f3333BSC0xfeef619a56fce9d003e20bf61393d18f62b0b2d5BSCOn BSC block 30119397, transaction 0xeb4eb487f58d39c05778fed30cd001b986d3c52279e44f46b2de2773e7ee1d5e used a freshly deployed attacker helper contract to manipulate the Utopia/WBNB Pancake pair at 0xfeef619a56fce9d003e20bf61393d18f62b0b2d5 and extract 492088605739133158123 wei of WBNB. The attack was permissionless: the attacker only needed public pair functions, public token state, and normal swap access.
The root cause is in Utopia token contract 0xb1da08c472567eb0ec19639b1822f578d39f3333. During non-whitelisted pair transfers, _transfer calls _airdrop, and _airdrop overwrites the computed address balance with 1 instead of preserving existing balances. By steering that computed address to the Pancake pair itself, the attacker forces the pair token balance to 0, then back to 1, and finally exploits Pancake reserve-based pricing to drain nearly all WBNB liquidity.
Three protocol behaviors matter for this incident.
First, Pancake pairs trust live ERC-20 balances when sync() updates reserves. If a token contract reports a manipulated balanceOf(pair), Pancake records that manipulated value as the pair reserve.
Second, skim(to) transfers any token amount above the currently stored reserve to . Pancake does not validate whether the token contract maintains sane internal bookkeeping for unrelated accounts.
toThird, Utopia injects extra balance mutation into pair transfers. The verified source shows that on non-whitelisted transfers involving a swap pair, _transfer calls _airdrop(from, to, amount) before _tokenTransfer settles the actual transfer:
if (!_feeWhiteList[from] && !_feeWhiteList[to]) {
takeFee = true;
...
_airdrop(from, to, amount);
}
_tokenTransfer(from, to, amount, takeFee, isAddLP);
That extra hook is what makes the pair balance externally forgeable.
This is an ATTACK-class bug in the token’s accounting, not a benign MEV opportunity. The safety invariant is straightforward: a token transfer must only update balances that correspond to the actual transfer or fee logic, and it must not overwrite arbitrary third-party balances. Utopia breaks that invariant in _airdrop.
The relevant code is:
function _airdrop(address from, address to, uint256 tAmount) private {
uint256 seed = (uint160(lastAirdropAddress) | block.number) ^ (uint160(from) ^ uint160(to));
address airdropAddress;
uint256 airdropAmount = 1;
...
airdropAddress = address(uint160(seed | tAmount));
_balances[airdropAddress] = airdropAmount;
...
lastAirdropAddress = airdropAddress;
}
The breakpoint is the assignment _balances[airdropAddress] = 1. It does not mint, increment, or conserve supply. It simply overwrites the chosen account balance slot with 1. Because airdropAddress depends on attacker-influenced inputs (lastAirdropAddress, block.number, from, to, tAmount), the attacker can steer this overwrite into the Pancake pair balance slot.
The exploit starts from BSC state immediately before block 30119397, when the Utopia/WBNB pair still held a large Utopia reserve and 507504915442740086636 wei of WBNB. The attacker EOA 0xe84ef3615b8df94c52e5b6ef21acbf0039b29113 deployed helper contract 0x6191203510c2a6442faecdb6c7bb837a76f02d23 and used it as the adversary execution surface.
The trace first shows a buy into Utopia, giving the helper enough Utopia to manipulate pair state. The critical reserve-poisoning phase then happens in four steps:
1 wei of Utopia into the pair, which updates lastAirdropAddress.lastAirdropAddress() and computes a skim recipient whose address bits cause the next _airdrop call to resolve to the pair address.PancakePair::skim(recipient), the pair transfers 1 wei of Utopia out. Inside Utopia, _airdrop overwrites _balances[pair] = 1, and then the real transfer subtracts 1, leaving the pair balance at 0.sync() records reserve0 as 0. The helper then sends another 1 wei of Utopia to the pair and calls sync() again, making reserve0 exactly 1.The decisive trace segment is:
PancakePair::skim(0xfF2651b286694eD2Fd2fC8907f57dd0Ae9FFF5Fd)
Utopia::transfer(..., 1)
...
PancakePair::sync()
Utopia::balanceOf(pair) -> 0
emit Sync(reserve0: 0, reserve1: 507504915442740086636)
...
Utopia::transfer(pair, 1)
PancakePair::sync()
Utopia::balanceOf(pair) -> 1
emit Sync(reserve0: 1, reserve1: 507504915442740086636)
At that point the pair advertises reserves of (1 Utopia, 507504915442740086636 WBNB). The attacker then transfers 32 wei of Utopia in and uses Pancake’s quote logic against the falsified reserve ratio:
getAmountOut(32, 1, 507504915442740086636) -> 492088605739133158123
PancakePair::swap(0, 492088605739133158123, attackerHelper, 0x)
The pair pays out 492088605739133158123 wei of WBNB, leaving only 15416309703606928513 wei of WBNB and 33 wei of Utopia in the pool. The balance-diff artifact independently confirms the pair’s Utopia balance collapsing from 1768838349283261391642 to 33, while the attacker helper ends with the drained WBNB.
The adversary flow is fully contained in the single transaction 0xeb4eb487f58d39c05778fed30cd001b986d3c52279e44f46b2de2773e7ee1d5e.
EOA 0xe84ef361... sends 0.1 BNB
-> deploy helper 0x6191203510...
-> wrap BNB to WBNB
-> buy Utopia through Pancake router
-> transfer 1 wei Utopia to pair
-> read lastAirdropAddress()
-> skim(craftedRecipient)
-> sync() // reserve0 becomes 0
-> transfer 1 wei Utopia to pair
-> sync() // reserve0 becomes 1
-> transfer 32 wei Utopia to pair
-> swap out 492.088605739133158123 WBNB
The attacker-related accounts are deterministic and evidenced by metadata plus trace:
0xe84ef3615b8df94c52e5b6ef21acbf0039b29113 funded and submitted the exploit transaction.0x6191203510c2a6442faecdb6c7bb837a76f02d23 was created inside that transaction, executed the exploit calls, and retained the WBNB proceeds.No privileged role, private key compromise, or hidden dependency is required. The exploit only relies on public state inspection and public calls to transfer, skim, sync, and swap.
The victimized liquidity pool is the Utopia/WBNB Pancake pair at 0xfeef619a56fce9d003e20bf61393d18f62b0b2d5. The incident drained:
492088605739133158123 wei of WBNB (492.088605739133158123 WBNB)After the swap, the pair was left with only 15416309703606928513 wei of WBNB and 33 wei of Utopia. Economically, the exploit converted an almost fully depleted counterasset reserve into attacker profit by first corrupting the pair’s observed Utopia reserve.
0xeb4eb487f58d39c05778fed30cd001b986d3c52279e44f46b2de2773e7ee1d5eskim, sync, getAmountOut, and swap0xb1da08c472567eb0ec19639b1822f578d39f3333, especially _transfer and _airdrop