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

Ignored Return Value in ICurveRouterNG.exchange Function

Summary:

The exchange function in the ICurveRouterNG interface does not specify or enforce the handling of a return value (uint256), which represent critical information of the received amount of the final output token by the swap.

Vulnerability Details

The CurveRouterNG documentation says

Router.exchange(_route: address[11], _swap_params: uint256[5][5], _amount: uint256, _expected: uint256, _pools: address[5] = empty(address[5]), _receiver: address = msg.sender) -> uint256

Function to perform a token exchange with up to 5 swaps in a single transaction.

Returns: received amount of the final output token (uint256).

Function to perform a token exchange with up to 5 swaps in a single transaction.

Returns: received amount of the final output token (uint256).

Ignoring the return value increases the risk of vulnerabilities due to faulty or malicious router behavior in claimAndSwap::StrategyMainnet.sol

function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
require(_minOut > _amountClaim, "minOut too low");
//@q no return value used
@> router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this)
);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}

Impact

Unverified Swap Success: Since router.exchange does not return or validate the actual amount received from the swap, there’s no guarantee that the swap executed as intended.

Tools Used

Manual review

Recommendations:

  1. Validate Return Value of exchange: router.exchange function is updated to return the actual amount received, use it for validation

  2. Balance Check as a Backup: Retain the balance validation (balAfter - balBefore) >= _minOut as a secondary check to ensure consistency.

function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
require(_minOut > _amountClaim, "minOut too low");
//@q no return statement used
+ uint256 amountOut = router.exchange(
routes[_routeNumber],
swapParams[_routeNumber],
_amountClaim,
_minOut,
pools[_routeNumber],
address(this)
);
+ // Primary validation using return value
+ require(amountOut >= _minOut, "Slippage too high");
+ // Backup validation using balance check
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Balance check failed");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
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

Support

FAQs

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