Calculated from recorded token losses using historical USD prices at the incident time.
0x5e694707337cca979d18f9e45f40e81d6ca341ed342f1377f563e779a746460d0xbe779d420b7d573c08eee226b9958737b6218888BSC0x36696169c63e42cd08ce11f5deebbcebae652050BSC0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095cBSC0x93fbf6b2d322c6c3e7576814d6f0689e0a333e96BSCProtocol: AIZPT314 bonding-curve token / Pancake V3 WBNB pool on BSC
Category: MEV-style economic exploit (flash-swap arbitrage)
Chain / Block: BSC (chainid 56), block 42,846,998
Primary adversary EOA: 0x3026c464d3bd6ef0ced0d49e80f171b58176ce32
Primary transaction: 0x5e694707337cca979d18f9e45f40e81d6ca341ed342f1377f563e779a746460d
At BSC block 42,846,998, an unprivileged searcher EOA 0x3026...ce32 executed a single transaction to router 0x8408...8320 that performed a flash-swap style arbitrage between:
0x36696169c63e42cd08ce11f5deebbcebae652050 (WBNB / AIZPT314), and0xbe779d420b7d573c08eee226b9958737b6218888.The router borrowed WBNB from the Pancake V3 pool, unwrapped it to BNB, and used that BNB to interact with AIZPT314’s on-chain bonding-curve buy/sell logic. Within the same transaction it then:
From balance_diff.json and the transaction receipt:
The root cause is economic, not technical: AIZPT314 is an ERC314-style bonding-curve token whose price function is based directly on its own BNB reserves and internal token balances. Listing this token against WBNB in a Pancake V3 pool, without any mitigation against flash-swap or large MEV trades, creates a permissionless arbitrage path that allows a searcher to extract BNB reserves from the token contract in a single transaction.
The AIZPT314 token at 0xbe779d420b7d573c08eee226b9958737b6218888 is an ERC314-style bonding-curve token:
address(this).balance (BNB reserves) and _balances[address(this)] (token inventory) to determine price.The core reserve and pricing functions are:
// Collected AIZPT314 token source (Contract.sol)
function getReserves() public view returns (uint256, uint256) {
return (address(this).balance, _balances[address(this)]);
}
function getAmountOut(uint256 value, bool _buy) public view returns (uint256) {
(uint256 reserveETH, uint256 reserveToken) = getReserves();
if (_buy) {
return ((value * reserveToken) / (reserveETH + value)) / 2;
} else {
return (value * reserveETH) / (reserveToken + value);
}
}
function buy() internal {
require(tradingEnable, 'Trading not enable');
uint256 swapValue = msg.value;
uint256 token_amount = (swapValue * _balances[address(this)]) / (address(this).balance);
...
}
function sell(uint256 sell_amount) internal {
require(tradingEnable, 'Trading not enable');
uint256 ethAmount = (sell_amount * address(this).balance) /
(_balances[address(this)] + sell_amount);
...
payable(msg.sender).transfer(ethAmount);
}
Caption: AIZPT314’s bonding-curve logic prices buys and sells using the contract’s own BNB reserves (address(this).balance) and token inventory, and pays BNB directly out of reserves on each sell (source: collected Contract.sol).
This design means:
The Pancake V3 pool at 0x36696169c63e42cd08ce11f5deebbcebae652050 is a standard implementation linking:
token0: WBNB (0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c)token1: AIZPT314 (0xbe779d420b7d573c08eee226b9958737b6218888)Pancake V3 pools support swap callbacks that allow a caller (e.g., a router) to:
The pool’s implementation enforces balance-based repayment and uses a reentrancy lock, but otherwise allows any contract to execute such swaps. There is no protocol-level restriction preventing a router from:
The analyzed opportunity is fully captured by a single adversary-crafted transaction:
0x5e69...460d on BSC (chainid 56), block 42,846,998.0x3026...ce32.0x8408497c18882bfb61be9204cfff530f4ee18320.Per root_cause.json and the seed metadata:
0x69b0f29c), which in turn calls the Pancake V3 pool (selector 0x490e6cbc) to initiate a WBNB flash-swap.The pre-state (pre_state_sigma_B) is the canonical BSC state immediately prior to the block, including:
PancakeV3Pool 0x3669...2050.0xbe77...8888.Within a single transaction, the adversary (via the router) executes:
0x3669...2050.buy() logic.sell() logic many times.The callTracer output for 0x5e69...460d shows the router’s interactions with the pool, WBNB, and AIZPT314. A minimal excerpt:
// Seed transaction trace (debug_traceTransaction callTracer) for tx 0x5e69...460d
{
"calls": [
{
"from": "0x36696169c63e42cd08ce11f5deebbcebae652050",
"to": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"type": "CALL",
"input": "0xa9059cbb...0001b1ae4d6e2ef5000000",
"value": "0x0"
},
{
"from": "0x8408497c18882bfb61be9204cfff530f4ee18320",
"to": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"type": "CALL",
"input": "0x2e1a7d4d...0001b1ae4d6e2ef5000000",
"value": "0x0"
},
{
"from": "0x8408497c18882bfb61be9204cfff530f4ee18320",
"to": "0xbe779d420b7d573c08eee226b9958737b6218888",
"type": "CALL",
"input": "0x",
"value": "0x1b1ae4d6e2ef5000000"
},
{
"from": "0x8408497c18882bfb61be9204cfff530f4ee18320",
"to": "0xbe779d420b7d573c08eee226b9958737b6218888",
"type": "CALL",
"input": "0xa9059cbb...00032c931835d1b6948c0000",
"value": "0x0"
}
]
}
Caption: The pool sends WBNB to the router; the router un-wraps WBNB via withdraw (0x2e1a7d4d), sends BNB into AIZPT314, then repeatedly calls transfer to AIZPT314 with large token amounts, triggering sell() and BNB payouts back to the router (source: callTracer JSON).
Key points from the trace:
0x3669...2050 calls WBNB 0xbb4c...c095c to transfer ~8,000 WBNB to the router.withdraw(uint256) on WBNB, unwrapping that WBNB into BNB with value matching the borrowed amount.CALL with non-zero value and empty input), corresponding to a buy per AIZPT314’s payable fallback / buy() logic.transfer(address(this), amount) (selector 0xa9059cbb to the token contract, with to set to the token address), which by design invokes the sell() path, causing the token contract to transfer BNB back to the router many times in the same transaction.The transaction receipt for 0x5e69...460d confirms:
// Tx receipt snippet for 0x5e69...460d (cast/eth_getTransactionReceipt)
{
"from": "0x3026c464d3bd6ef0ced0d49e80f171b58176ce32",
"to": "0x8408497c18882bfb61be9204cfff530f4ee18320",
"gasUsed": "0x398701",
"effectiveGasPrice": "0xb2d05e00",
"logs": [
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"topics": ["Transfer", "...pool", "...router"],
"data": "0x...0001b1ae4d6e2ef5000000"
},
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"topics": ["Deposit", "...router"],
"data": "0x...0001b3cc032f6b8895bb96"
},
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"topics": ["Transfer", "...router", "...pool"],
"data": "0x...0001b1e7338e75f01a0000"
},
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"topics": ["Transfer", "...router", "...3026c4...ce32"],
"data": "0x...000000000000001e4cfa0f5987bbb96"
}
],
"status": "0x1"
}
Caption: Receipt logs show WBNB transferred from the pool to the router, a WBNB Deposit (wrap of BNB), WBNB transferred back from the router to the pool, and the final WBNB transfer of ~34.9343 WBNB from the router to the adversary EOA (source: tx receipt JSON).
This log pattern matches the narrative:
balance_diff.json for the seed transaction quantifies native BNB flow:
// State delta summary for tx 0x5e69...460d (prestateTracer diff)
{
"native_balance_deltas": [
{
"address": "0x3026c464d3bd6ef0ced0d49e80f171b58176ce32",
"delta_wei": "-11310339000000000"
},
{
"address": "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
"delta_wei": "39034317811221904278"
},
{
"address": "0xbe779d420b7d573c08eee226b9958737b6218888",
"delta_wei": "-39034317811221904278"
}
]
}
Caption: Native balance deltas show AIZPT314 losing 39.0343 BNB, WBNB gaining the same amount, and the adversary EOA only losing gas (≈0.0113 BNB), consistent with reserves being drained from the token into WBNB and then partially paid out as profit (source: balance_diff.json).
From these deltas:
-0.011310339 BNB), meaning the actual value extraction is in WBNB.Combining the balance deltas with ERC20 transfer logs:
sell() calls.The exploit is not due to a reentrancy bug, access-control error, or mis-implementation of the Pancake V3 pool. Instead, it arises from:
sell().Because these mechanics are entirely permissionless and require no privileged role, any MEV searcher can run similar strategies whenever the on-chain state and prices allow profitable trades. The incident is therefore best understood as a deliberate extraction of protocol reserves via MEV arbitrage, rather than an unintended code exploit.
From root_cause.json and balance_diff.json:
0xbe779d420b7d573c08eee226b9958737b6218888.0x8408...8320 to EOA 0x3026...ce32.gasUsed = 3,770,113, effectiveGasPrice = 3 gwei → 0.011310339 BNB.Protocol-level effect:
AIZPT314’s on-contract BNB reserves, which economically back the token and determine its bonding-curve price, are significantly reduced in a single transaction.
User impact:
Holders of AIZPT314 who expected reserves to remain in the contract to support price and redemption value are harmed. The bonding curve after the trade reflects lower reserves, which can lower effective prices for future sells and reduce perceived backing.
Security posture:
[1] Seed transaction metadata and balance diffs
tx 0x5e69...460d (metadata and balance_diff.json).[2] Transaction trace (callTracer) for 0x5e69...460d
debug_traceTransaction callTracer output collected during analysis.withdraw to unwrap BNB.transfer to its own address (triggering sell()), with nested BNB transfers back to the router.[3] Transaction receipt and logs for 0x5e69...460d
gasUsed, effectiveGasPrice).[4] AIZPT314 token source (Contract.sol)
0xbe779d420b7d573c08eee226b9958737b6218888.getReserves, getAmountOut, buy, and sell functions.address(this) as sells and pays BNB from reserves.[5] Pancake V3 pool source (PancakeV3Pool.sol)
0x36696169c63e42cd08ce11f5deebbcebae652050.[6] EOA transaction history for 0x3026c464d3bd6ef0ced0d49e80f171b58176ce32
All referenced artifacts are present under the provided root-cause analysis directory; no required evidence was found missing during this report.