DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: low
Valid

Missing Deadline Check in StrategyMainnet Curve Exchange

Summary

The StrategyMainnet.sol contract's claimAndSwap function using Curve's exchange lacks a deadline check, while the other two similar contracts (StrategyOp and StrategyArb) do include timestamps. This inconsistency could lead to stale transactions being executed at unfavorable prices.

Root Cause

StrategyMainnet.sol:

// @audit - No deadline parameter
router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this) // Note: Missing deadline parameter
);

Compared to StrategyOp.sol and StrategyArb.sol which both have deadlines:

IVeloRouter(router).swapExactTokensForTokens(
_amount,
minOut,
_path,
address(this),
block.timestamp // Has deadline parameter
);

The root cause is:

  1. Curve's router accepts a deadline parameter but it's not being used

  2. Other strategies enforce transaction expiry but StrategyMainnet doesn't

  3. No expiry check means transactions can be executed at any future time

Impact

Let's walk through a real scenario:

  1. Initial State:

  • WETH/alETH price is 1:1.5

  • Keeper submits claimAndSwap for 100 WETH with minOut = 101 alETH

  1. Transaction Gets Stuck:

// Keeper submits:
strategy.claimAndSwap(
100 ether, // _amountClaim
101 ether, // _minOut (just above 1:1)
0 // _routeNumber
);
  1. Price Changes:

  • Two hours later, WETH/alETH price moves to 1:1.8

  • Old transaction is still valid because no deadline

  • MEV bot can now execute the old transaction

  1. Result:

  • Strategy gets only 101 alETH instead of 180 alETH at current price

  • 79 alETH in value lost


    This vulnerability is rated as HIGH severity because:

    1. Direct financial loss possible through stale execution

    2. Inconsistent with other strategy implementations

    3. No protection against unfavorable price movements

    4. High probability of occurrence during market volatility

Proof of Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract ExploitTest is Test {
StrategyMainnet strategy;
function testStaleExecution() public {
// Setup
vm.warp(1000); // Set initial time
// Keeper submits swap
vm.prank(keeper);
strategy.claimAndSwap(
100 ether, // 100 WETH
101 ether, // Minimum 101 alETH out
0 // Route number
);
// Time passes, price changes
vm.warp(block.timestamp + 2 hours);
// Price now 1:1.8 but old tx still valid
// MEV bot executes old transaction
vm.prank(mevBot);
strategy.executeStaleSwap();
// Check loss
assertEq(
aleth.balanceOf(address(strategy)),
101 ether, // Only got minimum instead of 180 ether
"Significant value lost from stale execution"
);
}
}

Recommendations

  1. Add deadline parameter to StrategyMainnet:

function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber,
uint256 _deadline
) external onlyKeepers {
require(block.timestamp <= _deadline, "Transaction expired");
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
router.exchange_with_deadline(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this),
_deadline
);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
  1. Enforce reasonable deadline windows:

require(_deadline <= block.timestamp + 3 minutes, "Deadline too far");

Files to update:

  • StrategyMainnet.sol: Lines 85-110

Updates

Lead Judging Commences

inallhonesty Lead Judge
6 months ago

Appeal created

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

claimAndSwap should have slippage on Mainnet

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.