GoldCoin Pair Reserve Overwrite
Exploit Transactions
0x199c4b88cab6b4b495b9d91af98e746811dd8f82f43117c48205e6332db9f0e0Victim Addresses
0x80121da952a74c06adc1d7f85a237089b57af347BSC0x7a3adf2f6b239e64dab1738c695cf48155b6e152BSCLoss Breakdown
Similar Incidents
Carson Pair Reserve Siphon
39%CS Pair Balance Burn Drain
37%LinkdaoDex USDT Pair Drain
37%STOToken Sell-Hook Reserve Manipulation Drains the STO/WBNB Pancake Pair
36%Public Mint Drains USDT Pair
36%StarlinkCoin Pair Drain
35%Root Cause Analysis
GoldCoin Pair Reserve Overwrite
1. Incident Overview TL;DR
On BSC block 30113118, transaction 0x199c4b88cab6b4b495b9d91af98e746811dd8f82f43117c48205e6332db9f0e0 drained the FFIST/USDT PancakeSwap pool by corrupting the token-side reserve before a sell. The attacker first bought FFIST, then sent a zero-value FFIST transfer that overwrote the pair's FFIST balance to 1, called public sync(), and finally sold 24 raw FFIST units into the now-desynchronized pool. The exploit was possible because GoldCoin's _airdrop logic lets any non-whitelisted transfer write _balances[airdropAddress] = 1 for attacker-controlled addresses, including the LP pair. The seed transaction produced a deterministic adversary portfolio gain of 219.154800816693701658 BNB-equivalent and left the pair short 53198433607272063480945 raw USDT units.
2. Key Background
This incident depends on the interaction between a broken token and a standard constant-product pair. PancakeSwap V2 pairs trust token balanceOf(pair) when sync() or swap() updates reserves, so any token that can arbitrarily rewrite the pair balance can corrupt AMM pricing. GoldCoin exposes exactly that condition because _airdrop runs on any transfer where both endpoints are not fee-whitelisted, including zero-value transfers.
Snippet origin: verified GoldCoin source.
if (!_feeWhiteList[from] && !_feeWhiteList[to]) {
uint256 maxSellAmount = balance * 999999 / 1000000;
if (amount > maxSellAmount) {
amount = maxSellAmount;
}
_airdrop(from, to, amount);
}
function _airdrop(address from, address to, uint256 tAmount) private {
uint256 seed = (uint160(lastAirdropAddress) | block.number) ^ (uint160(from) ^ uint160(to));
address airdropAddress = address(uint160(seed | tAmount));
_balances[airdropAddress] = 1;
}
The public pre-state before the seed transaction was also favorable to the attacker. Immediately before the exploit, the FFIST/USDT pair 0x7a3adf2f6b239e64dab1738c695cf48155b6e152 held 55420690999928645538098 raw USDT units and 5696780254392656566076 raw FFIST units, and GoldCoin.lastAirdropAddress() was 0x1E14a05467B0624D7542d61dCFBF27fff56efeDd. That state made the overwrite recipient fully derivable from public data.
3. Vulnerability Analysis & Root Cause Summary
This is an ATTACK-class incident caused by attacker-controlled arbitrary balance overwrite in the GoldCoin token, not by a flaw in PancakeSwap math. The violated invariant is simple: the LP pair's token reserve must always equal tokens legitimately transferred into or out of the pair. GoldCoin breaks that invariant because _airdrop writes 1 directly into a user-chosen _balances slot before normal transfer accounting finishes. Since lastAirdropAddress, block.number, from, to, and amount are either public or attacker-controlled, an unprivileged adversary can solve for a recipient that makes the first airdrop target equal the LP pair. Once the pair's token balance is forced to 1, public PancakePair::sync() commits that forged balance as the official reserve. The next sell then prices FFIST against almost the entire USDT reserve, which deterministically drains the pool.
4. Detailed Root Cause Analysis
- The attacker starts from the public pre-state at block
30113118. The victim token contract is GoldCoin0x80121da952a74c06adc1d7f85a237089b57af347, and the victim pool is the FFIST/USDT pair0x7a3adf2f6b239e64dab1738c695cf48155b6e152. - The attacker buys FFIST first. This serves two purposes: it acquires a small FFIST balance and updates
lastAirdropAddressto a new public value. After this leg, the helper contract holds237053613464325871raw FFIST units. - The attacker then chooses a zero-transfer recipient such that:
to = (lastAirdropAddress | block.number) ^ from ^ pair
With amount = 0, the first _airdrop candidate equals the pair address itself. In the seed transaction, that recipient is 0xd3c0e4d4ae359e0ac217f235bd46175fc60cd757.
Snippet origin: seed exploit trace.
GoldCoin::transfer(0xd3c0e4d4ae359e0ac217f235bd46175fc60cd757, 0)
...
@ pair-balance-slot: 0x...0134cf47dc3cc4d537ae -> 1
PancakePair::sync()
emit Sync(reserve0: 55423099372855156505378, reserve1: 1)
- After
sync(), the pair reserves become(55423099372855156505378 USDT, 1 FFIST). At that point the AMM believes the entire FFIST side is only1wei, even though the pair still holds enough USDT liquidity to pay out a large trade. - The attacker sells
24raw FFIST units into the corrupted pool. The trace shows the pair paying53200841980198574448225raw USDT to the intermediate USDT/WBNB route, and the balance diff shows the pair's net USDT loss as53198433607272063480945raw units. - The attacker then converts the drained USDT into WBNB and forwards it to the controlling EOA. The final WBNB transfer in the trace is
219169869136693701658raw WBNB units. Combined with the native BNB delta from the balance diff, the adversary portfolio moved from161.148686511492538427BNB-equivalent before the transaction to380.303487328186240085BNB-equivalent after it. The root cause artifact records fees paid in the reference asset as0.01506832BNB, which is already reflected in the net portfolio increase of219.154800816693701658.
5. Adversary Flow Analysis
The adversary cluster contains two addresses:
- EOA
0xcc8617331849962c27f91859578dc91922f6f050, which signed the exploit transaction, funded the helper deployment with0.01BNB, and received the final WBNB. - Helper contract
0xb31c7b7bdf69554345e47a4393f53c332255c9fb, which executed the entire on-chain strategy inside the same transaction.
The on-chain flow is fully public and permissionless:
- The EOA deploys the helper and funds it.
- The helper wraps
0.01BNB into WBNB, approves the Pancake router0x10ed43c718714eb63d5aa57b78b54704e256024e, and buys FFIST along the pathWBNB -> USDT -> FFIST. - The helper computes the overwrite recipient, calls
GoldCoin.transfer(recipient, 0), and forces the pair balance to1. - The helper calls public
PancakePair::sync()on the FFIST/USDT pair. - The helper sells
24raw FFIST units along the pathFFIST -> USDT -> WBNB. - The helper transfers the resulting WBNB back to the EOA, realizing profit in the same transaction.
The exploit is ACT because every step uses canonical public state, verified or publicly callable contracts, and public transaction inclusion. No private keys, allowlists, attacker bytecode reuse, or privileged sequencing are required.
6. Impact & Losses
The measurable loss fell on the FFIST/USDT pool and, economically, on GoldCoin liquidity.
- Victim pool loss:
53198433607272063480945raw USDT units (53,198.433607272063480945USDT if interpreted with 18 decimals as collected in the artifacts). - Pair state corruption: the FFIST reserve was forced down to
1during the attack and ended the transaction at only25raw FFIST units. - Adversary gain:
219169869136693701658raw WBNB transferred out of the helper, equal to a net portfolio increase of219.154800816693701658BNB-equivalent after native gas and funding effects.
The affected public components are GoldCoin 0x80121da952a74c06adc1d7f85a237089b57af347 and the FFIST/USDT PancakePair 0x7a3adf2f6b239e64dab1738c695cf48155b6e152.
7. References
- Seed exploit transaction:
0x199c4b88cab6b4b495b9d91af98e746811dd8f82f43117c48205e6332db9f0e0on BSC block30113118 - GoldCoin contract:
0x80121da952a74c06adc1d7f85a237089b57af347 - FFIST/USDT pair:
0x7a3adf2f6b239e64dab1738c695cf48155b6e152 - Supporting artifacts: seed metadata, seed trace, seed balance diff, and the refreshed resolution evidence under
artifacts/auditor/iter_1/resolution_evidence.json - Verified source confirmation: GoldCoin verified source in the collected artifacts; PancakePair verification confirmed via Etherscan v2 / BscScan code page