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

When claiming and swapping WETH to alETH, _minOut should be at least greater than or equal to _amountClaim.

Summary

When the keeper claims and swaps WETH to alETH, the _minOut parameter should be at least greater than or equal to _amountClaim.

Vulnerability Details

It is possible that when the keeper swaps an amount of WETH equivalent to alETH (i.e., the conversion rate between WETH and alETH is 1:1), the function will revert if _minOut is set to _amountClaim. This happens because the function requires the output amount to strictly exceed _minOut. As a result, users will not be able to claim and swap until the conversion rate changes from 1:1.

Impact

If the conversion rate between WETH and alETH is exactly 1:1, the keeper will not be able to successfully claim and swap. This could lead to operational delays or interruptions until the conversion rate changes.

Tools Used

Manuel Review and foundry

Change the test function function "test_claim_and_swap(uint256 _amount)" of Operation.t.sol

function test_claim_and_swap(uint256 _amount) public {
......
if (block.chainid == 1) {
// Mainnet
IStrategyInterface(address(strategy)).claimAndSwap(
claimable,
- claimable * 103 / 100,
+ claimable * 100 / 100,
0
);
} else if (block.chainid == 10) {
// NOTE on OP we swap directly to WETH
IVeloRouter.route[] memory veloRoute = new IVeloRouter.route[]();
veloRoute[0] = IVeloRouter.route(address(underlying), address(asset), true, 0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a);
// Velo Iterface
- IStrategyInterfaceVelo(address(strategy)).claimAndSwap(claimable, claimable * 103 / 100, veloRoute);
+ IStrategyInterfaceVelo(address(strategy)).claimAndSwap(claimable, claimable * 100 / 100, veloRoute);
} else if (block.chainid == 42161) {
// ARB
// NOTE we swap first to eFrax and then to WETH
IRamsesRouter.route[] memory ramsesRoute = new IRamsesRouter.route[]();
address eFrax = 0x178412e79c25968a32e89b11f63B33F733770c2A;
ramsesRoute[0] = IRamsesRouter.route(address(underlying), eFrax, true);
ramsesRoute[1] = IRamsesRouter.route(eFrax, address(asset), true);
- IStrategyInterfaceRamses(address(strategy)).claimAndSwap(claimable, claimable * 103 / 100, ramsesRoute);
+ IStrategyInterfaceRamses(address(strategy)).claimAndSwap(claimable, claimable * 100 / 100, ramsesRoute);
} else {
revert("Chain ID not supported");
}
....
}

The test revert with eeror ""minOut too low""

Recommendations

Update the function claimAndSwap in all file StrategyArb, StrategyMainnet, StrategyOP

MAINET

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");
+ require(_minOut >= _amountClaim, "minOut too low");
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));
}

ARB

function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IRamsesRouter.route[] calldata _path) external onlyKeepers {
transmuter.claim(_amountClaim, address(this)); //@audit check that this part don't fail
uint256 balBefore = asset.balanceOf(address(this));
_swapUnderlyingToAsset(_amountClaim, _minOut, _path); //@audit check that this part don't fail
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
function _swapUnderlyingToAsset(uint256 _amount, uint256 minOut, IRamsesRouter.route[] calldata _path) internal {
// TODO : we swap WETH to ALETH -> need to check that price is better than 1:1
// uint256 oraclePrice = 1e18 * 101 / 100;
+ require(minOut >= _amount, "minOut too low");
- require(minOut > _amount, "minOut too low");
uint256 underlyingBalance = underlying.balanceOf(address(this));
require(underlyingBalance >= _amount, "not enough underlying balance");
IRamsesRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
}

OPS

function claimAndSwap(uint256 _amountClaim, uint256 _minOut, IVeloRouter.route[] calldata _path ) external onlyKeepers {
transmuter.claim(_amountClaim, address(this));
uint256 balBefore = asset.balanceOf(address(this));
_swapUnderlyingToAsset(_amountClaim, _minOut, _path);
uint256 balAfter = asset.balanceOf(address(this));
require((balAfter - balBefore) >= _minOut, "Slippage too high");
transmuter.deposit(asset.balanceOf(address(this)), address(this));
}
/**
@dev internal function for swapping WETH to alETH via Velo Router
*/
function _swapUnderlyingToAsset(uint256 _amount, uint256 minOut, IVeloRouter.route[] calldata _path) internal {
// TODO : we swap WETH to ALETH -> need to check that price is better than 1:1
// uint256 oraclePrice = 1e18 * 101 / 100;
- require(minOut > _amount, "minOut too low");
+ require(minOut >= _amount, "minOut too low");
uint256 underlyingBalance = underlying.balanceOf(address(this));
require(underlyingBalance >= _amount, "not enough underlying balance");
IVeloRouter(router).swapExactTokensForTokens(_amount, minOut, _path, address(this), block.timestamp);
}
Updates

Appeal created

inallhonesty Lead Judge 5 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.