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

Lack of liquidity in Curve pools and predictability of Keeper's swap will cause DoS of `claimAndSwap` function or cause the Keeper to receive less than intended amounts

Summary

The claimAndSwap function in StrategyMainnet.sol claims wETH from the Transmuter contract and attempts to swap this wETH to alETH utilizing Curve's pools. Due to the lack of liquidity in the Curve pools, it is easily manipulateable. Any user with enough wETH can "sell" wETH into the pool to cause a spike in alETH price in order to DoS the claimAndSwap function. Knowing the keeper will only make swaps at a premium, users can easily front-run this swap to benefit from this premium for themselves while causing the keepers transaction to revert or achieve less of a profit.

Vulnerability Details

Taking a look at the claimAndSwap function:

/**
* @dev Function called by keeper to claim WETH from transmuter & swap to alETH at premium
* we ensure that we are always swapping at a premium (i.e. keeper cannot swap at a loss)
* @param _amountClaim The amount of WETH to claim from the transmuter
* @param _minOut The minimum amount of alETH to receive after swap
* @param _routeNumber Calls mapping to the params to be passed into curve router
*/
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");
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));
}

It is observed that this function will claim wETH from the transmuter and swap this wETH for alETH to be deposited back into the transmuter contract. This function only swaps at a premium, meaning that amount of alETH received should be higher than the amount of wETH sent to the pool.

If we observe the alETH/wETH pool in Curve, we can see the following reserve amounts:

alETH: 935.91256
wETH: 235.75423

The low liquidity in the pool means that, a swap of 350.07917 wETH for 351.544 alETH will bring the alETH price in this pool to be greater than wETH, making it impossible to swap at a premium from now on. In this scenario, claimAndSwap function will no longer be swapping at a premium and revert due to the following check:

require((balAfter - balBefore) >= _minOut, "Slippage too high");

Attackers can now use their alETH to either swap back in Curve (a sandwich attack to cause DoS) or any other exchange or deposit into the transmuter contract to create an incentive for their attack. Even if the Curve swap happens through multiple pools, the swap route is set with the contract and is predictable, still making it easy to manipulate the pools that will be used.

This same attack is also possible in the other contracts in Arbitrum or Optimism, however, due to how hard it is to front-run in these chains and use of Ramses Exchange utilizing many pools that aren't always predicatable, makes it less likely. In short, malicious attacker can basically take advantage of the same trade that the keeper was intending to do while causing the keepers transaction to revert or in smaller swap amounts, cause it to benefit less from it's swap.

Another issue that arises due to the predictability of the Keeper's swap is that any user will be aware that this swap is going to increase alETH prices compared to wETH, user's can front-run the Keeper's swap to buy alETH from the same pool, cause Keeper to receive less alETH than it intended (while still satisfying the _minOut check) and back-run the Keeper's transaction to sell their alETH after Keeper's swap raises the alETH/wETH price.

Impact

The claimAndSwap function will fail to achieve it's purpose, can be DoS'd without much cost for the attacker.

Alternatively, users can benefit from the predictable nature of this swap to benefit themselves and cause the Keeper to receive less alETH than it thought it would receive.

Tools Used

Manual review

Recommendations

Utilize more ways of swapping wETH for alETH rather than only relying on the unstable Curve pools.

Additionally, add a buffer according to the amount being swapped that must be received on top of the _minOut

Updates

Appeal created

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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