APIG Self-Transfer Mint Drain
Exploit Transactions
0x66dee84591aeeba6e5f31e12fe728f2ddc79a06426036793487a980c3b952947Victim Addresses
0xdc630fb4f95faaee087e0ce45d5b9c4fc9888888BSC0xefbf31b0ca397d29e9ba3fb37fe3c013ee32871dBSC0xb920456aec6e88c68c16c8294688b2b63c81b2ceBSCLoss Breakdown
Similar Incidents
BRAToken self-transfer tax bug inflates pool and drains USDT
39%PHIL Public Mint Drain
38%Public Mint Drains USDT Pair
37%LAYER3 Oracle-Mint Drain
37%EEECOIN Public Helper LP Drain
35%BankrollNetworkStack self-buy dividend inflation exploit
35%Root Cause Analysis
APIG Self-Transfer Mint Drain
1. Incident Overview TL;DR
On BNB Smart Chain block 31562012, transaction 0x66dee84591aeeba6e5f31e12fe728f2ddc79a06426036793487a980c3b952947 was submitted by EOA 0x73d80500b30a6ca840bfab0234409d98cf588089. Inside that single transaction, the attacker deployed helper contracts, flash-borrowed 500 USDT from pair 0xadad973f8920bc511d94aade2762284f621f1467, acquired an initial APIG balance from the APIG/USDT pair, repeatedly self-transferred APIG to mint additional APIG, sold the inflated balance into the APIG/USDT and APIG/ETH pools, repaid the flash loan plus fee, and forwarded 72127075425255057150292 USDT and 59500000000000000000 ETH to the originating EOA.
The root cause is a balance-update bug in APIG token 0xdc630fb4f95faaee087e0ce45d5b9c4fc9888888. Its transfer primitive reads both balances before either write occurs. When from == to, the debit write is overwritten by a second write that stores oldBalance + amount back to the same slot, so a self-transfer becomes a mint operation.
2. Key Background
APIG was trading against at least two liquid pools at the time of the incident: APIG/USDT pair 0xefbf31b0ca397d29e9ba3fb37fe3c013ee32871d and APIG/ETH pair 0xb920456aec6e88c68c16c8294688b2b63c81b2ce. The exploit did not require privileged roles, private keys, or governance control. It only required a public source of bootstrap capital, a positive APIG balance, and permissionless access to APIG's transfer(address,uint256) entrypoint.
The APIG contract was not verified in the collected artifacts, so the code-level analysis relies on decompilation of its on-chain runtime bytecode and corroboration from the execution trace. That is sufficient here because the exploit condition is local and deterministic: if the token writes stale recipient state when sender and recipient alias, balance conservation fails immediately on-chain.
3. Vulnerability Analysis & Root Cause Summary
This incident is an ATTACK case, not a pure MEV opportunity. The vulnerable component is APIG's transfer primitive in the runtime bytecode of 0xdc630fb4f95faaee087e0ce45d5b9c4fc9888888. The critical invariant is simple: for an ERC20 self-transfer, the holder's post-transfer balance must equal its pre-transfer balance, and the transfer must not mint supply. APIG violates that invariant because it loads both balance[from] and balance[to] before updating storage.
The decompiled transfer path shows the exact breakpoint. It computes senderNew = senderOld - amount and writes it to the sender slot, then computes recipientNew = recipientOld + amount from the stale pre-write recipient value and stores that to the recipient slot. When from and to are the same address, both writes target the same slot. The second write therefore overwrites the debit and leaves oldBalance + amount in storage. For a self-transfer of the full balance, the balance doubles.
That behavior is not hypothetical. The seed trace shows the helper contract self-transferring 960928352699517099745 APIG to itself and ending the call with 1921856705399034199490 APIG. Repeating the same public call multiplies the attacker's APIG balance until it is large enough to drain both liquidity pools.
4. Detailed Root Cause Analysis
The decompiled APIG transfer primitive exposes the storage-aliasing bug directly:
function func_275C(var arg0, var arg1, var arg2) {
memory[0x00:0x20] = arg0 & ((1 << 160) - 1);
memory[0x20:0x40] = 0x01;
var temp1 = storage[keccak256(memory[0x00:0x40])];
var var0 = temp1;
memory[0x00:0x20] = arg1 & ((1 << 160) - 1);
var var1 = storage[keccak256(memory[0x00:0x40])];
if (var0 >= arg2) {
var var2 = func_26FF(var0, arg2);
storage[keccak256(...arg0...)] = var2;
var2 = func_2319(var1, arg2);
storage[keccak256(...arg1...)] = var2;
}
}
Both balances are read before either slot is updated. For ordinary transfers that is harmless, but for arg0 == arg1 both reads observe the same original balance b. The first write stores b - amount; the second write stores b + amount to the same slot. That is the code-level breakpoint where the token stops conserving balances.
The seed transaction trace confirms the exploit path at runtime:
0xDc630Fb4F95FaAeE087E0CE45d5b9c4fc9888888::transfer(
0xd4a93119152D3981db692a4dE31565DE514348d8,
960928352699517099745
)
emit Transfer(
from: 0xd4a93119152D3981db692a4dE31565DE514348d8,
to: 0xd4a93119152D3981db692a4dE31565DE514348d8,
value: 960928352699517099745
)
← [Return] 1921856705399034199490
After obtaining initial APIG from the APIG/USDT pair, the attacker helper repeats this exact public self-transfer. The trace then shows a long sequence of self-transfer Transfer events with exponentially increasing values, followed by APIG sales into the USDT and ETH pairs. Because the flaw is inside the token's public transfer logic, any unprivileged actor who can first obtain a non-zero APIG balance can realize the same mint primitive.
The ACT pre-state was reconstructible from public data at block 31562011, immediately before the incident transaction. The relevant conditions were: flash-loan liquidity existed in pair 0xadad973f8920bc511d94aade2762284f621f1467, APIG/USDT and APIG/ETH both had liquid reserves, and APIG's runtime bytecode already contained the stale-read transfer bug. No private information or privileged contract path was needed to exploit those conditions.
5. Adversary Flow Analysis
The adversary cluster is identifiable from the seed transaction. EOA 0x73d80500b30a6ca840bfab0234409d98cf588089 submitted the only external transaction. That transaction created orchestrator 0xfdc6a621861ed2a846ab475c623e13764f6a5ad0, which in turn created and invoked helper 0xd4a93119152d3981db692a4de31565de514348d8.
The execution flow is:
- The helper records the flash-pair and victim-pair balances.
- It flash-borrows
500USDT from pair0xadad973f8920bc511d94aade2762284f621f1467. - It transfers the borrowed USDT into APIG/USDT and swaps for an initial APIG position.
- It repeatedly calls
APIG.transfer(helper, helperBalance), which doubles the helper's APIG balance on each iteration because of the self-transfer mint bug. - It sells the inflated APIG into APIG/USDT, extracting almost all USDT liquidity from that pair.
- It repays the flash pair with
501500000000000000000USDT, proving the strategy is self-financing and permissionless. - It sells the remaining APIG into APIG/ETH, extracting almost all ETH liquidity from that pair.
- It forwards the resulting USDT and ETH balances to the originating EOA.
The end of the seed trace shows the monetization leg clearly:
emit Transfer(from: 0xd4a931..., to: 0xaDaD973f..., value: 501500000000000000000)
emit Transfer(from: 0xd4a931..., to: 0x73d80500..., value: 72127075425255057150292)
emit Transfer(from: 0xd4a931..., to: 0xb920456A..., value: 51984210526315789473684210523)
emit Transfer(from: 0xb920456A..., to: 0xd4a931..., value: 59500000000000000000)
emit Transfer(from: 0xd4a931..., to: 0x73d80500..., value: 59500000000000000000)
Those transfers match the balance-diff artifact: the EOA finished the transaction with 72127075425255057150292 USDT and 59500000000000000000 ETH, while the flash-loan pair gained the 1.5 USDT fee.
6. Impact & Losses
The direct victims were the APIG liquidity venues rather than the flash-loan source. Pair 0xefbf31b0ca397d29e9ba3fb37fe3c013ee32871d lost 72128575425255057150292 USDT and pair 0xb920456aec6e88c68c16c8294688b2b63c81b2ce lost 59500000000000000000 ETH. The flash-loan pair was repaid in full and retained its fee.
The measured attacker profit was 72127075425255057150292 USDT and 59500000000000000000 ETH at EOA 0x73d80500b30a6ca840bfab0234409d98cf588089, with an additional native gas outlay of 534860600000000000 wei. No off-chain valuation is required to establish success because both tracked profit assets increased on-chain and the bootstrap loan was repaid inside the same transaction.
7. References
- Seed transaction
0x66dee84591aeeba6e5f31e12fe728f2ddc79a06426036793487a980c3b952947on BNB Smart Chain block31562012. - Seed transaction metadata collected for the incident transaction, including sender, block, and raw calldata.
- Seed execution trace showing helper deployment, flash-loan bootstrap, APIG self-transfer doubling, pair drains, repayment, and profit transfers.
- Seed balance diff showing the USDT and ETH losses at the victim pairs and the matching profit at the attacker EOA.
- Decompilation excerpt of APIG runtime bytecode demonstrating the stale-read transfer bug in the vulnerable balance-update path.