Calculated from recorded token losses using historical USD prices at the incident time.
0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e50x1befe6f3f0e8edd2d4d15cae97baee01e51ea4a4BSC0x3a0d9d7764fae860a659eb96a500f1323b411e68BSCOn BNB Chain block 16798807, transaction 0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e5 let an unprivileged attacker turn discounted GYM v1 into treasury-backed GYMNET v2 and exit with 1327140409926955785497 wei of WBNB before gas. The attacker EOA 0x74298086c94dab3252c5dac979c9755c2eb08e49 called helper contract 0x4e284686fbcc0f2900f638b04c4d4b433c40a345, borrowed WBNB through a PancakeSwap flash swap, bought a large amount of deprecated GYM v1, wrapped part of that position into GYM/WBNB LP, and passed the LP into LiquidityMigrationV2 at 0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4.
The root cause is a public migration function that performs a nominal 1:1 token substitution. LiquidityMigrationV2.migrate(uint256) removes v1 LP into GYM and WBNB, then feeds the removed GYM amount directly into addLiquidityETH for GYMNET without any price check, conversion curve, or cap tied to market value. That design let the attacker redeem underpriced GYM v1 for equal nominal units of GYMNET v2 that were already stocked inside the migration contract.
Two separate PancakeSwap markets mattered in this incident. Deprecated GYM v1 (0xE98D920370d87617eb11476B41BF4BE4C556F3f8) traded against WBNB in pair 0x8dC058bA568f7D992c60DE3427e7d6FC014491dB. The successor token GYMNET v2 (0x3a0d9d7764FAE860A659eb96A500F1323b411e68) traded against WBNB in pair .
0x627F27705c8C283194ee9A85709f7BD9E38A1663The protocol also deployed LiquidityMigrationV2 to help holders migrate from v1 LP into v2 LP. Its verified source hard-codes the v1 token, v2 token, v1 LP, and Pancake router, and exposes a public migrate(uint256) entrypoint. The contract pre-approves the router for both the v1 LP token and the v2 token inventory it holds:
function migrate(uint256 _lpTokens) public nonReentrant {
require(_lpTokens > 0, "zero LP tokens sended");
require(IERC20(lpAddress).transferFrom(_msgSender(), address(this), _lpTokens), "transfer failed");
(uint256 amountTokenRecived,
uint256 amountEthRecived) = Router.removeLiquidityETH(
v1Address,
_lpTokens,
0,
0,
address(this),
block.timestamp
);
(uint256 amountTokenStaked,
uint256 amountEthStaked,
uint256 LpStaked) = Router.addLiquidityETH{value: amountEthRecived}(
v2Address,
amountTokenRecived,
0,
0,
_msgSender(),
block.timestamp
);
}
That code is the critical background fact: the migration contract spends its own GYMNET inventory according to the raw GYM amount removed from the v1 pool, not according to economic equivalence.
This was an ATTACK-category incident rooted in broken economic accounting inside a permissionless migration path. The intended invariant was that migrating deprecated GYM liquidity into GYMNET liquidity should preserve value, or at minimum should not let arbitrary users exchange cheap legacy inventory for equal nominal units of treasury-backed replacement inventory. LiquidityMigrationV2 violates that invariant because it never checks market prices between GYM and GYMNET, never uses an oracle, and never applies a conversion ratio derived from reserves or treasury policy.
The concrete breakpoint is in migrate(uint256): the value amountTokenRecived returned by removeLiquidityETH(v1Address, _lpTokens, ...) is passed unchanged as amountTokenDesired into addLiquidityETH(v2Address, amountTokenRecived, ...). Once that happens, every unit of removed GYM v1 can consume one nominal unit of GYMNET v2 from the migrator's inventory. Because GYM v1 traded at a depressed market price during the exploit, that nominal substitution transferred real value from the protocol's GYMNET inventory to the attacker.
The attacker used a single transaction and did not require any privileged access. The seed metadata shows that EOA 0x74298086c94dab3252c5dac979c9755c2eb08e49 sent the transaction to helper contract 0x4e284686fbcc0f2900f638b04c4d4b433c40a345. The seed trace then shows a flash swap from Pancake pair 0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16, giving the helper 2400 WBNB.
The helper spent 600 WBNB to buy 5942069125039956871572333 GYM from the v1 pool, which made GYM v1 cheap relative to the fixed GYMNET inventory sitting in the migration contract. The helper then added WBNB plus 1400000000000000000000000 GYM into the v1 pair and received 46106308269787700007026 v1 LP tokens.
The seed trace captures the exploit breakpoint directly:
0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4::migrate(46106308269787700007026)
0x10ED43...::removeLiquidityETH(GYM, 46106308269787700007026, ...)
...
Transfer(... amount: 1399999999999999999999838 GYM ...)
...
0x10ED43...::addLiquidityETH{value: 1730629414932105999393}(
GymNetwork,
1399999999999999999999838,
...,
0x4e284686FBCC0F2900F638B04C4D4b433C40a345,
...
)
Those trace lines prove the exact mechanism described in the victim code. The migrator removed v1 LP and received 1399999999999999999999838 GYM plus 1730629414932105999393 wei of WBNB, then immediately spent the same nominal GYM amount as GYMNET input for the v2 pool. The trace also shows that this minted 44760251562411855105534 v2 LP tokens to the attacker helper.
The exploit remained profitable because the migration contract, not the attacker, supplied the GYMNET side of the new liquidity. The contract therefore accumulated the removed v1 GYM while depleting its GYMNET inventory. That is the clearest victim-side state transition: cheap v1 inventory moved into the migrator, valuable v2 inventory moved out of it.
The attacker flow had three stages.
First, the helper flash-borrowed 2400 WBNB and used part of it to skew the GYM/WBNB v1 market by purchasing a large GYM position. This created the discounted v1 inventory needed for the migration abuse.
Second, the helper minted fresh v1 LP from that discounted inventory and passed the LP into LiquidityMigrationV2.migrate. The trace shows the helper approved the migrator, the migrator pulled 46106308269787700007026 v1 LP, removed the LP into GYM plus WBNB, and used its own GYMNET inventory to mint 44760251562411855105534 v2 LP for the helper.
Third, the helper unwound the v2 position and realized profit. The trace shows removeLiquidityETHSupportingFeeOnTransferTokens on the GYMNET/WBNB v2 pair, receipt of 1166737573465332474488617 GYMNET and 1730629414932105999392 wei of WBNB, sales of residual GYM and GYMNET back into WBNB, repayment of 2425 WBNB to the flash-loan pair, and a final transfer of 1327140409926955785497 wei of WBNB to the attacker EOA.
The seed balance diff independently supports the final economics. It records the attacker EOA's gas cost as 12647390000000000 wei and confirms that the transaction's profit-bearing asset was WBNB.
The measurable loss in this incident was depletion of treasury-backed GYMNET value through a permissionless migration path. The attacker realized 1327140409926955785497 wei of WBNB gross profit in a single transaction, and the migration contract was left holding the removed GYM v1 while spending down its GYMNET inventory.
The direct victim component was LiquidityMigrationV2 at 0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4, with supporting protocol exposure across GYM v1, GYMNET v2, the GYM/WBNB pair 0x8dC058bA568f7D992c60DE3427e7d6FC014491dB, and the GYMNET/WBNB pair 0x627F27705c8C283194ee9A85709f7BD9E38A1663.
0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e50x74298086c94dab3252c5dac979c9755c2eb08e490x4e284686fbcc0f2900f638b04c4d4b433c40a3450x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4https://api.etherscan.io/v2/api?chainid=56&module=contract&action=getsourcecode&address=0x1BEfe6f3f0E8edd2D4D15Cae97BAEe01E51ea4A4/workspace/session/artifacts/collector/seed/56/0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e5/trace.cast.log/workspace/session/artifacts/collector/seed/56/0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e5/metadata.json/workspace/session/artifacts/collector/seed/56/0xa5b0246f2f8d238bb56c0ddb500b04bbe0c30db650e06a41e00b6a0fff11a7e5/balance_diff.json