DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Potential loss of funds if swaps execute with incorrect parameters

Summary

StrategyMainnet.claimAndSwap() The function lacks validation that _routeNumber < nRoutes before accessing route data from the mappings. This allows:

  1. Access to uninitialized or invalid route configurations

  2. Potential manipulation of swap paths by passing invalid route numbers

  3. No guarantee that the accessed route data is properly configured

Because the code assumes route numbers will be valid without explicit validation, allowing access to undefined mapping values that could corrupt swap execution.

function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber // <-- No bounds checking against nRoutes
) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
require(_minOut > _amountClaim, "minOut too low");
router.exchange(
routes[_routeNumber], // <-- Can access invalid route data
swapParams[_routeNumber], // <-- Can access invalid swap params
_amountClaim,
_minOut,
pools[_routeNumber], // <-- Can access invalid pool data
address(this)
);
}

it could lead to execution of swaps with incorrect/undefined routes, access to uninitialized pool addresses and potential loss of funds through failed or corrupted swaps.

Vulnerability Details

The issue is in all three strategy implementations (Mainnet, Optimism, and Arbitrum) in their claimAndSwap functions.

contract StrategyMainnet is BaseStrategy {
// State variables tracking routes
uint256 public nRoutes = 0;
mapping(uint256 => address[11]) public routes;
mapping(uint256 => uint256[5][5]) public swapParams;
mapping(uint256 => address[5]) public pools;
function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
// BUG: No validation that _routeNumber < nRoutes
// This allows accessing undefined routes/params/pools
transmuter.claim(_amountClaim, address(this));
router.exchange(
routes[_routeNumber], // Can access uninitialized route
swapParams[_routeNumber], // Can access uninitialized params
_amountClaim,
_minOut,
pools[_routeNumber], // Can access uninitialized pools
address(this)
);
}
}

The bug is from missing validation of route numbers against the nRoutes counter. When routes are added via addRoute(), nRoutes is incremented, but claimAndSwap() never verifies that the provided route number is within bounds.

Impact

Root Cause:

  1. Routes are added incrementally with nRoutes tracking the count

  2. claimAndSwap accepts any uint256 as _routeNumber

  3. No validation that _routeNumber < nRoutes

  4. Allows access to uninitialized mapping values

Recommendations

// For StrategyMainnet.sol
function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
+ // Validate route number is within bounds of configured routes
+ require(_routeNumber < nRoutes, "Invalid route number");
+
+ // Validate route configuration exists
+ require(routes[_routeNumber][0] != address(0), "Route not configured");
+ require(pools[_routeNumber][0] != address(0), "Pool not configured");
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
+ // Additional slippage protection
+ require(_minOut > _amountClaim, "Insufficient minimum output");
router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this)
);
+ // Verify swap execution
+ uint256 balAfter = asset.balanceOf(address(this));
+ require(balAfter > balBefore, "Swap failed");
}
+ // Add route validation helper
+ function validateRoute(uint256 _routeNumber) public view returns (bool) {
+ if (_routeNumber >= nRoutes) return false;
+ if (routes[_routeNumber][0] == address(0)) return false;
+ if (pools[_routeNumber][0] == address(0)) return false;
+ return true;
+ }
// Similar protections for StrategyOp.sol and StrategyArb.sol
function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
IVeloRouter.route[] calldata _path
) external onlyKeepers {
+ // Validate path configuration
+ require(_path.length > 0, "Empty path");
+ require(_path[0].from == address(underlying), "Invalid start token");
+ require(_path[_path.length-1].to == address(asset), "Invalid end token");
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
+ // Validate underlying balance before swap
+ require(underlying.balanceOf(address(this)) >= _amountClaim, "Insufficient balance");
_swapUnderlyingToAsset(_amountClaim, _minOut, _path);
+ // Verify swap results
+ uint256 balAfter = asset.balanceOf(address(this));
+ require((balAfter - balBefore) >= _minOut, "Insufficient output");
}
Updates

Appeal created

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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