Calculated from recorded token losses using historical USD prices at the incident time.
0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c9050x560d3973ee82a318d381c49fcbf3ce9d6cf1250bBSC0x9d2d5cc00c8cf233aefd8a177b904f8d3abb5d46BSCIn BSC block 80395411, transaction 0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c905 used a PancakeSwap V2 flash swap plus an unverified liquidity-manager contract at 0x560d3973ee82a318d381c49fcbf3ce9d6cf1250b to create a deterministic extraction path into USDT. The attacker-controlled call path caused 0x560d... to move large AFX/AHT amounts (via transferFrom) from a third-party treasury address and to perform liquidity operations, enabling the attacker to drain USDT from the AFX/USDT Pancake pair.
The root cause is that 0x560d...::addLiquidityUsdt(uint256) (selector 0xb1a87f2c) is callable by a non-owner and performs privileged treasury-moving operations and transfers tokens to msg.sender. An unprivileged caller can force liquidity actions that, when composed with flash swaps and router swaps, results in deterministic USDT profit.
PancakeSwap V2 pairs support flash swaps by allowing swap(..., to, data) to invoke to.pancakeCall(...) when data is non-empty. This enables a borrower (here the attacker-controlled contract 0x129b803f5e8e36e2d6e705d84bbe7995b02fc0cb) to receive tokens, run arbitrary logic, and repay the pair within the same transaction.
The attack involves these on-chain components on BSC (chainid 56):
0xb6761b4d7b913ef048c92e3bb1305883422e819a0xcd1ec887b081cfba30c8003e8ad1b67f92236c7b0x55d398326f99059ff775485246999027b31979550x10ed43c718714eb63d5aa57b78b54704e256024e0x9d2d5cc00c8cf233aefd8a177b904f8d3abb5d460x63e97b4f292b6cd059fc5f7621291c7ad5b94ce00x01b3beeea8d6892e4dd9f9cfe8045e49889eb4890x560d3973ee82a318d381c49fcbf3ce9d6cf1250bAHT is fee-on-transfer. The verified AHT source shows _buyDestroyFee = 45 and _swapFee = 5, applied on buys from swap pairs:
uint256 public _buyDestroyFee = 45;
uint256 public _swapFee = 5;
...
} else if (_swapPairList[sender]) {
// buy
destroyFeeAmount = (tAmount * _buyDestroyFee) / 100;
swapFeeAmount = (tAmount * _swapFee) / 100;
}
This is an ATTACK-class ACT opportunity caused by missing access control on a privileged liquidity-manager routine.
The vulnerable contract 0x560d... exposes an externally callable function addLiquidityUsdt(uint256) which:
0x146933f2692f5ff3b62441ab3c2a65ddcaca753c) via transferFrom, implying a standing allowance from that address to the liquidity manager.msg.sender (the attacker-controlled caller), rather than to a protocol-controlled recipient.The net effect is that an unprivileged caller can convert a tiny USDT input (100 USDT base units in the seed transaction) into a large AFX position sourced from third-party allowances, use that AFX to repay the flash swap, and finally swap residual AFX into USDT to realize profit.
At block 80395411, the liquidity manager’s owner() is 0x1301602a6B70fC0eB86A9aADeC9298d313628b3A (distinct from the attacker-controlled EOA/contract addresses used in the transaction), yet addLiquidityUsdt(100) executes successfully during the exploit transaction. The on-chain trace shows the attacker-controlled contract calling:
0x560d3973EE82a318d381c49fcbF3ce9d6CF1250B::addLiquidityUsdt(100)
Within that same call, the trace shows two critical categories of effects:
AFX::transferFrom(0x146933F2692F5fF3b62441AB3C2a65dDCAca753c, 0x560d3973EE82a318d381c49fcbF3ce9d6CF1250B, 511965516492998991427845)
AHT::transferFrom(0x146933F2692F5fF3b62441AB3C2a65dDCAca753c, 0x560d3973EE82a318d381c49fcbF3ce9d6CF1250B, 12604468958762124545)
AFX::transfer(0x129b803F5E8e36e2d6e705D84BBe7995b02FC0CB, 511965516492998991426086)
BEP20USDT::transfer(0x129b803F5E8e36e2d6e705D84BBe7995b02FC0CB, 1)
These two behaviors are sufficient to characterize the root cause at a code-level invariant:
addLiquidityUsdt(uint256) executes for an attacker-controlled non-owner caller and routes assets to that caller while pulling large balances from a third-party address.After using the liquidity-manager side effects to obtain AFX and complete the flash-swap repayment, the attacker swaps remaining AFX into USDT via Pancake router, sending proceeds to the EOA sender 0x236f08d8962e1f29700e3d91009bfa8d37d71e53. The trace shows the final USDT transfer to the EOA:
PancakePair::swap(10699618671790673809279, 0, 0x236f08d8962e1F29700e3D91009bfa8D37D71e53, 0x)
BEP20USDT::transfer(0x236f08d8962e1F29700e3D91009bfa8D37D71e53, 10699618671790673809279)
The prestate balance-diff confirms the corresponding USDT depletion from the AFX/USDT pool and USDT gain by the attacker EOA (amounts in USDT base units, 18 decimals):
{"token":"0x55d398326f99059ff775485246999027b3197955","holder":"0x9d2d5cc00c8cf233aefd8a177b904f8d3abb5d46","before":"100612192638198552496590","after":"89912573966407878687395","delta":"-10699618671790673809195","contract_name":"BEP20USDT"}
{"token":"0x55d398326f99059ff775485246999027b3197955","holder":"0x236f08d8962e1f29700e3d91009bfa8d37d71e53","before":"15506610461385075122126","after":"26206229133175748931405","delta":"10699618671790673809279","contract_name":"BEP20USDT"}
The attacker contract 0x129b... also spends a negligible net amount of USDT during the same transaction (100 -> 1, net -99 base units), consistent with the exploit requiring only a tiny USDT input:
{"token":"0x55d398326f99059ff775485246999027b3197955","holder":"0x129b803f5e8e36e2d6e705d84bbe7995b02fc0cb","before":"100","after":"1","delta":"-99","contract_name":"BEP20USDT"}
The exploit is realized in a single adversary-crafted transaction:
0x236f08d8962e1f29700e3d91009bfa8d37d71e53 calls the attacker-controlled contract 0x129b803f5e8e36e2d6e705d84bbe7995b02fc0cb.0x129b... triggers a PancakeSwap V2 flash swap on 0x01b3beeea8d6892e4dd9f9cfe8045e49889eb489::swap(...) to borrow a large amount of AFX, which causes the pair to call back into 0x129b...::pancakeCall(...).pancakeCall, 0x129b... approves USDT/AFX/AHT for router use and swaps AFX -> AHT using swapExactTokensForTokensSupportingFeeOnTransferTokens, paying AHT’s transfer fees along the way.pancakeCall, 0x129b... calls 0x560d...::addLiquidityUsdt(100). This is the root-cause breakpoint: the unprivileged call executes and, during execution, the liquidity manager pulls large AFX/AHT balances from 0x146933... via transferFrom and routes assets back to the caller (0x129b...).0x129b... swaps AHT -> AFX to acquire enough AFX to repay the flash swap plus fees, and transfers repayment AFX back to the flash-swap pair.0x129b... swaps AFX -> USDT through the Pancake router and directs the USDT output to the EOA 0x236f..., completing the profit predicate.The measurable impact in the seed transaction is a direct depletion of USDT from the AFX/USDT Pancake pair and a corresponding USDT increase for the attacker EOA:
0x9d2d5cc00c8cf233aefd8a177b904f8d3abb5d46: -10699618671790673809195 USDT base units (=-10699.618671790673809195 USDT, 18 decimals).0x236f08d8962e1f29700e3d91009bfa8d37d71e53: +10699618671790673809279 USDT base units (=+10699.618671790673809279 USDT, 18 decimals).The attacker EOA pays gas in BNB; the native balance diff shows -54547150000000 wei (0.00005454715 BNB) for this transaction. The report does not compute a USD valuation for gas fees.
0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c905 at block 80395411.0x560d3973ee82a318d381c49fcbf3ce9d6cf1250b, function addLiquidityUsdt(uint256) (selector 0xb1a87f2c).0x236f08d8962e1f29700e3d91009bfa8d37d71e530x129b803f5e8e36e2d6e705d84bbe7995b02fc0cb0xb6761b4d7b913ef048c92e3bb1305883422e819a0xcd1ec887b081cfba30c8003e8ad1b67f92236c7b0x55d398326f99059ff775485246999027b31979550x10ed43c718714eb63d5aa57b78b54704e256024e0x9d2d5cc00c8cf233aefd8a177b904f8d3abb5d460x63e97b4f292b6cd059fc5f7621291c7ad5b94ce00x01b3beeea8d6892e4dd9f9cfe8045e49889eb489artifacts/collector/seed/56/0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c905/metadata.json, artifacts/collector/seed/56/0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c905/trace.cast.log, artifacts/collector/seed/56/0x380cd298a607d4422edc640b7f5a907ec0792841ee5fc963d265b1189397c905/balance_diff.json, artifacts/collector/seed/56/0xcd1ec887b081cfba30c8003e8ad1b67f92236c7b/src/Contract.sol, artifacts/auditor/iter_0/owner_0x560d.txt.