This is a lower bound: only assets with reliable historical USD prices are counted, so the actual loss may be higher.
0x258e53526e5a48feb1e4beadbf7ee53e07e816681ea297332533371032446bfd0x1f415255f7e2a8546559a553e962de7bc60d7942BSC0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cdBSCOn BSC block 29891710, transaction 0x258e53526e5a48feb1e4beadbf7ee53e07e816681ea297332533371032446bfd exploited AiWGPTToken at 0x1f415255f7e2a8546559a553e962de7bc60d7942 and its WGPT/USDT Pancake pair at 0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cd. The adversary used public DODO flash liquidity, bought WGPT to push down the pair's live WGPT balance, then sold WGPT back through PancakeRouter so each sell entered AiWGPTToken's public sell hook and removed protocol-owned LP from the pair.
The root cause is an attacker-manipulable LP-removal formula on the public sell path. AiWGPTToken computes sell-side LP removal from the pair's instantaneous WGPT balance rather than from a manipulation-resistant price source, so any unprivileged seller can force protocol LP withdrawal and deplete both pair reserves whenever isSwap=true.
AiWGPTToken is not a plain fee-on-transfer token. Its transferFrom() sell branch treats transfers into a listed swap pair as a privileged event and, when burnToken is enabled, calls internal LP-removal logic before finishing the transfer.
The relevant public market is the WGPT/USDT Pancake pair 0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cd. Before the exploit transaction, the token contract still held 93671278471332372231927 LP tokens for that pair, and the pair reserves were 152344224829762814626477 WGPT and 246891982286046691958845 USDT.
Public DODO pools on BSC provide permissionless flash liquidity. That matters because the exploit only needs temporary capital to distort the pair balance and then sell back through the public router; it does not require privileged keys, attacker-owned on-chain artifacts from the original incident, or any protocol admin capability.
The vulnerability is an attack-class logic flaw in AiWGPTToken's sell hook. The contract exposes protocol-owned LP withdrawal on an unprivileged sell path and sizes that withdrawal using a price proxy derived from IERC20(address(this)).balanceOf(swapLPAddr), which is the pair's instantaneous WGPT balance. That value is directly movable by any trader who buys from the pair in the same transaction. Once the attacker lowers the pair's visible WGPT balance, callPrice() falls and (_value * 1e18 / callPrice()) + lpNumber grows, causing removeLp() to burn more protocol LP than intended. The withdrawn USDT and WGPT are then consumed by usdtToToken() and burnToekn(), leaving the pair with lower reserves and the token contract with far less LP. The broken invariant is that an unprivileged seller must never be able to trigger protocol LP removal in an amount derived from a transient, attacker-manipulable spot balance.
The vulnerable code path in the verified AiWGPTToken source is:
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
...
if(_swapPairList[_to]){
if(isSwap || _WhiteList[_from]){
if(burnToken){
sellburnToken(callBurn(_value));
}
...
}
}
}
function callPrice() private returns(uint256){
uint256 tokenBalance = IERC20(address(this)).balanceOf(swapLPAddr);
tokenBalance = tokenBalance * 10 ** 18;
uint256 lpBalance = IERC20(swapLPAddr).totalSupply();
lpPrice = tokenBalance / lpBalance;
return lpPrice * 2;
}
function sellburnToken(uint256 _value) private returns(bool){
(uint amountA, uint amountB) = removeLp((_value * 10**18 / callPrice()) + lpNumber);
usdtToToken(_value - amountB);
burnToekn(amountB);
return true;
}
The exploit sequence follows directly from that code:
29891709, where isSwap=true, burnToken=true, swapLPAddr already points to the public WGPT/USDT pair, and the token contract still owns a large LP position.IERC20(address(this)).balanceOf(swapLPAddr), which is the exact input used by callPrice().transferFrom() with _to equal to the pair, so the token invokes sellburnToken().callPrice() is now artificially depressed, removeLp() withdraws excessive protocol LP from the pair.usdtToToken() and burnToekn(), while the attacker exits the sell path with positive USDT.The incident trace shows the repeated LP-removal breakpoint clearly. During the exploit transaction, PancakeRouter repeatedly executes:
PancakeRouter::removeLiquidity(
BEP20USDT,
AiWGPTToken,
4448262008380174093090,
10,
10,
AiWGPTToken,
1689146288
)
...
PancakeRouter::removeLiquidity(
BEP20USDT,
AiWGPTToken,
2137068849583674609242,
10,
10,
AiWGPTToken,
1689146288
)
The balance-diff evidence confirms the final state change caused by that path:
{
"token": "0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cd",
"holder": "0x1f415255f7e2a8546559a553e962de7bc60d7942",
"before": "93671278471332372231927",
"after": "5023736168813373194",
"delta": "-93666254735163558858733"
}
The same balance diff shows both pair reserves draining materially:
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cd",
"delta": "-105212230996130493027690"
}
{
"token": "0x1f415255f7e2a8546559a553e962de7bc60d7942",
"holder": "0x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cd",
"delta": "-81017855558625925684802"
}
The adversary cluster consists of EOA 0xdc459596aed13b9a52fb31e20176a7d430be8b94 and helper contract 0x5336a15f27b74f62cc182388c005df419ffb58b8. The EOA deployed the helper in transaction 0xfcf00c61684f0883a8311dd6f1c1a5c7160ef0ad47686a25c21ed709d94bb171, approved an auxiliary token in transaction 0x868c39fedb968ce2bc30224c2cb6b57c01010c6de193c42ce09476738e9eee75, and then sent the exploit transaction.
In the exploit transaction:
sellburnToken() path, which repeatedly removes protocol LP from the pair.The validator reproduction on a mainnet fork reaches the same semantic outcome: LP falls from 93671278471332372231927 to 81571368139716878044820, reserves fall to 135984835161388795441113 WGPT and 243788242260001548332149 USDT, and the attacker retains 3623222550076100488813 USDT after repaying the flash loan principal.
The incident drained protocol-owned LP and reduced both reserves in the public WGPT/USDT pool. The measured losses reported in the root-cause package are:
1052122309961304930276908101785555862592568480293666254735163558858733Operationally, the protocol lost nearly all LP held by the token contract for the victim pair, burned a large amount of WGPT, and ended with materially depleted public market liquidity.
0x258e53526e5a48feb1e4beadbf7ee53e07e816681ea297332533371032446bfd0x1f415255f7e2a8546559a553e962de7bc60d79420x5a596eae0010e16ed3b021fc09bbf0b7f1b2d3cdremoveLiquidity evidence0xdc459596aed13b9a52fb31e20176a7d430be8b94 used for adversary clustering