The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of Slippage Protection for Users When Swapping Between Collateral Assets Exposes Users to Frontrunning Attacks

Description:

When users call swap() to swap between their collateral, the function makes a sub call to calculateMinimumAmountOut(), which returns a value for minimumAmountOut. This strategy, commonly employed when interacting with AMM DEXes, ensures that even if a transaction is front-run, it will only execute if the outcome is in the user's favor. However, calculateMinimumAmountOut() returns zero when the amount to be swapped doesn't pose a direct risk to the protocol. This means even if the swap is front-run and the tokenOut is zero, there will still be sufficient collateral in the vault to back the minted tokens. This leaves users unprotected, as the protocol only shields itself but not its users.

Impact:

This could lead to a loss of funds for users and a negative second-order effect on the protocol. The more assets users lose through this attack vector, the lower the total value locked in the protocol, as a consequence of the direct losses and increased apathy toward participating in the network.

Proof of Concept:

  • John locks $100,000 worth of wBTC and ETH collateral each.

  • John has only minted $1000 worth of EUROs.

  • John attempts to swap $20,000 worth of wBTC into ETH.

  • John's trade is seen in the mem pool by MEV bots with no slippage protection.

  • John is frontrunned and gets back $10,000 worth of ETH for his $20,000 worth of wBTC.

  • John is disgruntled and rage quits the protocol.

  • Other users see or hear of John's situation and leave the platform to protect themselves from becoming victims of such an attack.

Tools Used:

  • Manual review

Recommended Mitigation Steps:

Allow users to set their slippage tolerance but ensure their position can be covered by the swap.

+ function swap(bytes32 _inToken, bytes32 _outToken, uint256 _amount, uint256 _minimumAmountOut) external onlyOwner {
uint256 swapFee = _amount * ISmartVaultManagerV3(manager).swapFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
address inToken = getSwapAddressFor(_inToken);
- uint256 minimumAmountOut = calculateMinimumAmountOut(_inToken, _outToken, _amount);
+ uint256 minimumAmountOut = validateMinimumAmountOut(_inToken, _outToken, _amount, _minimumAmountOut);
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: inToken,
tokenOut: getSwapAddressFor(_outToken),
fee: 3000,
recipient: address(this),
deadline: block.timestamp,
amountIn: _amount - swapFee,
amountOutMinimum: minimumAmountOut,
sqrtPriceLimitX96: 0
});
inToken == ISmartVaultManagerV3(manager).weth() ?
executeNativeSwapAndFee(params, swapFee) :
executeERC20SwapAndFee(params, swapFee);
}
  • validateMinimumAmountOut() is a hypothetical function that takes in similar parameters as calculateMinimumAmountOut() with _minimumAmountOut given by the user, to return a value that protects both the user and the protocol.

Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

Slippage-issue

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Slippage-issue

Support

FAQs

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

Give us feedback!