Calculated from recorded token losses using historical USD prices at the incident time.
0x27539b1dee647b38e1b987c41c5336b1a8dce663BSCBXH staking pool 0x27539b1dee647b38e1b987c41c5336b1a8dce663 paid USDT bonuses by converting pending BXH rewards through the live reserves of BXH/USDT pair 0x919964b7f12a742e3d33176d7af9094ea4152e6f. In exploit transaction 0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f4 on BSC, the adversary flash-borrowed USDT, distorted that pair's reserves in the same transaction, then called deposit(0,0) to harvest an inflated USDT payout directly from the staking pool. The collected balance diff shows the staking pool lost 39885623577085020521681 USDT units net and the attacker contract ended with 31794192560957275136775 USDT units net profit.
The root cause is a manipulable spot-price oracle inside value-transfer logic. getITokenBonusAmount trusts same-transaction AMM reserves, depositIToken and withdrawIToken convert pending BXH rewards through that function, and safeBonusTransfer sends the computed USDT amount with no manipulation resistance and no payout cap.
Pool 0 of TokenStakingPoolDelegate accepts vUSDT deposits, accrues BXH rewards, and can optionally pay rewards in a separate bonus token. For this pool, bonus payout was enabled with USDT as the bonus token and the BXH/USDT pair as the price source.
A PancakeSwap-style pair exposes reserves through getReserves() and supports flash-swaps. That means an unprivileged contract can borrow USDT, change the pair's reserve ratio, perform additional calls while the manipulated reserves remain live, and repay before transaction end.
0xecac48848d39efb86967ad442314a12e422f79036286066affc2aac97582787e0x1f2993eaaf776c18be3ca544eb2ca6f32671b159d00db3210b4ce0da6ccc435c0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f4The relevant victim-side pricing code is below:
function getITokenBonusAmount(uint256 _pid, uint256 _amountInToken) public view returns (uint256) {
PoolInfo storage pool = poolInfo[_pid];
(uint112 _reserve0, uint112 _reserve1, ) = IUniswapV2Pair(pool.swapPairAddress).getReserves();
uint256 amountTokenOut = 0;
uint256 _fee = 0;
if (IUniswapV2Pair(pool.swapPairAddress).token0() == address(iToken)) {
amountTokenOut = getAmountOut(_amountInToken, _reserve0, _reserve1, _fee);
} else {
amountTokenOut = getAmountOut(_amountInToken, _reserve1, _reserve0, _fee);
}
return amountTokenOut;
}
Because deposit(0,0) is also the reward-claim path, a user who already has stake can trigger payout without adding more principal.
The issue is an attack-class protocol bug, not pure MEV. BXH let reward payout logic trust the instantaneous state of a manipulable AMM pair during the same transaction that performs the payout. The contract treated the pair reserve ratio as a fair BXH-to-USDT conversion rate even though any sufficiently funded attacker could move that ratio immediately before claiming. The implementation also set feeFactor to zero in getAmountOut, which made the conversion even more favorable to the claimer than a normal swap path. Once the manipulated amount was computed, the staking pool did not cap or sanity-check the resulting USDT transfer. Instead, it sent the computed amount directly from pool inventory. The exploit therefore converts a small pending BXH reward into a very large USDT withdrawal by transiently changing the pair state at the exact payout breakpoint.
The vulnerable payout path is straightforward in the staking contract:
function depositIToken(uint256 _pid, uint256 _amount, address _user) private {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
updatePool(_pid);
if (user.amount > 0) {
uint256 pendingAmount = user.amount.mul(pool.accITokenPerShare).div(1e12).sub(user.rewardDebt);
if (pendingAmount > 0) {
if (pool.enableBonus == false) {
safeITokenTransfer(_user, pendingAmount);
} else {
pendingAmount = getITokenBonusAmount(_pid, pendingAmount);
safeBonusTransfer(_pid, _user, pendingAmount);
}
}
}
}
function safeBonusTransfer(uint256 _pid,address _to, uint256 _amount) internal {
PoolInfo storage pool = poolInfo[_pid];
IERC20(pool.bonusToken).transfer(_to, _amount);
}
The safety invariant is: an unprivileged claimer must not be able to decide the USDT reward amount by transiently changing the reserves of the AMM pair consulted inside the same claim transaction. BXH breaks that invariant because the payout breakpoint is exactly the live getReserves() read inside getITokenBonusAmount.
The on-chain exploit trace shows the full sequence. The attacker flash-swapped 3178800000000000000000000 USDT from pair 0x16b9..., swapped 3148800000000000000000000 USDT into BXH on the configured BXH/USDT pair, then claimed rewards while the pair remained distorted:
0x16b9...::swap(3178800000000000000000000, 0, attacker, 0x00)
BEP20USDT::transfer(attacker, 3178800000000000000000000)
attacker::pancakeCall(...)
0x6A1A...::swapExactTokensForTokensSupportingFeeOnTransferTokens(
3148800000000000000000000,
0,
[USDT, BXH],
attacker,
1664375098
)
0x27539...::deposit(0, 0)
BEP20USDT::transfer(attacker, 40085623577085020521681)
That trace aligns with the victim-code breakpoint. Before manipulation, the forked PoC measured a fair bonus of only 2599385079683094565 USDT units for the accrued BXH reward. After reserve distortion, getITokenBonusAmount returned 36840242649124830773953 USDT units in the validator execution log, which is more than four orders of magnitude larger. The seed exploit trace paid 40085623577085020521681 USDT units in the real transaction, and the seed balance diff shows the staking contract finished with a net USDT loss of 39885623577085020521681.
The exploit required no privileged keys and no protocol admin action. The attacker only needed a reward-bearing position, access to a flash-swapable USDT source, and a claim path that consulted live pair reserves. Those conditions make the issue an ACT opportunity.
The adversary lifecycle observed in the collected window is:
0x4e77df7b9cdcecec4115e59546f3eacba095a89f in tx 0x9e121c8d61a2a4992e3b9f0fc9805bc352127d16885a6752af790fbdb124399c.0x326c15f335215ce84ae1f2ebdb0b46cf460cd545040f6468de5f23cb3fe329a4 and 0xecac48848d39efb86967ad442314a12e422f79036286066affc2aac97582787e.deposit(0, stakeAmount) in tx 0x1f2993eaaf776c18be3ca544eb2ca6f32671b159d00db3210b4ce0da6ccc435c.0xf66f8916e0985d0c6b4d79a2f35b7336879af9b05ea77ea706463671d1472fad and 0x08d9a68c81651c709964b98e33563c60994bb5ff35a9903d9a3aa4dff9e93027.0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f4 by flash-borrowing USDT, buying BXH to skew the pair, optionally topping up the pool, calling deposit(0,0), swapping BXH back to USDT, repaying the flash-swap, and retaining profit.The attacker tx window ties the EOA 0x81c63d821b7cdf70c61009a81fef8db5949ac0c9 to the deployment, funding, priming, failed attempts, and successful exploit. The seed balance diff ties the profit realization to the attacker contract:
{
"token": "0x55d398326f99059ff775485246999027b3197955",
"holder": "0x4e77df7b9cdcecec4115e59546f3eacba095a89f",
"before": "0",
"after": "31794192560957275136775",
"delta": "31794192560957275136775"
}
The impact was direct treasury depletion from the staking contract's USDT inventory. The seed exploit transaction transferred 40085623577085020521681 USDT units out of the staking pool during the manipulated claim. After accounting for the attacker's temporary top-up and the rest of the transaction flow, the collected balance diff shows the pool's net loss was 39885623577085020521681 USDT units. The attacker contract retained 31794192560957275136775 USDT units after repaying the flash-swap.
The affected victim component is the BXH staking pool at 0x27539b1dee647b38e1b987c41c5336b1a8dce663. The manipulated BXH/USDT pair at 0x919964b7f12a742e3d33176d7af9094ea4152e6f was instrumental to the exploit, but the economic loss was borne by the staking pool inventory.
0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f40x9e121c8d61a2a4992e3b9f0fc9805bc352127d16885a6752af790fbdb124399c0x27539b1dee647b38e1b987c41c5336b1a8dce6630x919964b7f12a742e3d33176d7af9094ea4152e6f0x16b9a82891338f9ba80e2d6970fdda79d1eb0daeartifacts/collector/iter_1/contract/56/0x27539b1dee647b38e1b987c41c5336b1a8dce663/TokenStakingPoolDelegate.solartifacts/collector/iter_1/address/56/0x81c63d821b7cdf70c61009a81fef8db5949ac0c9/txlist_relevant_window.jsonartifacts/collector/seed/56/0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f4/trace.cast.logartifacts/collector/seed/56/0xa13c8c7a0c97093dba3096c88044273c29cebeee109e23622cd412dcca8f50f4/balance_diff.jsonartifacts/validator/forge-test.log