The Standard

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

minimumAmountOut calculation opens users of the vault to sandwich attack when swapping

Summary

Swap Pools normally allow a user to specifiy a minimum amount out for a swap as a way to mitigate sandwich attacks. SmartVaultV3 calculates this amount for the user. This minimumAmountOut is calculated to be the minimum amount for which, once the swap goes through, the vault will not be undercollateralized. In reality unless a user is borrowing close to their maxMintable(), this calculated amount will be very low, opening the opportunity for sandwich attacks and the user having their funds stolen.

Vulnerability Details

A user can swap some of their collateral, directly through the Vault. This is done by calling the swap() function.
As we can see the user specifies the _inToken, _outToken and _amount.

https://github.com/Cyfrin/2023-12-the-standard/blob/main/contracts/SmartVaultV3.sol#L214

function swap(bytes32 _inToken, bytes32 _outToken, uint256 _amount) external onlyOwner {
uint256 swapFee = _amount * ISmartVaultManagerV3(manager).swapFeeRate() / ISmartVaultManagerV3(manager).HUNDRED_PC();
address inToken = getSwapAddressFor(_inToken);
uint256 minimumAmountOut = calculateMinimumAmountOut(_inToken, _outToken, _amount);
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);
}

The minimumAmountOut required as input by UniSwap to mitigate sandwich attacks is calculated separately and cannot be directly specified by the user. The calculation is used as a means to check if the vault will be undercollateralized after the swap. The calculateMinimumAmountOut() function looks as follows:

https://github.com/Cyfrin/2023-12-the-standard/blob/main/contracts/SmartVaultV3.sol#L206

function calculateMinimumAmountOut(bytes32 _inTokenSymbol, bytes32 _outTokenSymbol, uint256 _amount) private view returns (uint256) {
ISmartVaultManagerV3 _manager = ISmartVaultManagerV3(manager);
uint256 requiredCollateralValue = minted * _manager.collateralRate() / _manager.HUNDRED_PC();
uint256 collateralValueMinusSwapValue = euroCollateral() - calculator.tokenToEur(getToken(_inTokenSymbol), _amount);
return collateralValueMinusSwapValue >= requiredCollateralValue ?
0 : calculator.eurToToken(getToken(_outTokenSymbol), requiredCollateralValue - collateralValueMinusSwapValue);
}

Let's take a look at an example calculation to understand it. Suppose we have the following state and transaction data:

  • Rate tokenToEur for _inToken:EUROs = 1:1

  • Rate tokenToEur for _outToken:EUROs = 1:5

  • Total collateral in vault in EUROs = 1000000(This is returned by euroCollateral())

  • Collateral in vault for _inToken in EUROs = 10000

  • minted = 10000

  • collateralRate = 150%

  • Swap 1000 _inToken

After plugging the numbers, we get:

requiredCollateralValue = 10000 * 150% = 15000

This means after the swap, the vault must have at least 15000 EUROs in collateral value. Let's go to the next one:

uint256 collateralValueMinusSwapValue = 100000 - 1000 = 99000

As a result we have that 99000>15000, which will make the function return 0. So the minimumAmountOut passed to UniSwap will be 0. This will create an opening for a sandwich attack. In reality, unless the user has minted very close to their maximum allowed borrow, the function will not return a reasonable number for minimumAmountOut, meaning most swaps will be susceptible to this attack.

Impact

Stolen funds.

Tools Used

Manual Review.

Recommendations

Allow a user to specify minimumAmountOut as input for the function and use that to check if the vault will be undercollateralized after the swap.

Note

This was somewhat reported in the known issues, but the vulnerability to sandwich attacks it creates is not explicitly mentioned, thus am making this report.

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Slippage-issue

hrishibhat Lead Judge over 1 year 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.