The Standard

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

Wrong minimumAmountOut leads to users losing funds

Summary

The amount input used to compute minimumAmountOut in SmartVaultV3::calculateMinimumAmountOut() is different from amountIn used in SmartVaultV3::swap(). This leads to users losing funds when swapping tokens.

Vulnerability Details

To swap tokens, users call the SmartVaultV3::swap function. In this function, the minimum amount is calculated based on the amount they want to swap (_amount). Then the parameters (params) are set to executeNativeSwapAndFee/executeERC20SwapAndFee. params.amountIn is set to the amount the users want to swap minus the swap fee.

Here's the issue :

The _amount argument passed to the smartVaultV3::calculateMinimumAmountOut function is used entirely to calculate the minimum amount of _outToken output that the user should get.
This function is called in the SmartVaultV3::swap function where the input amount used in params.amountIn contains the swap fee as well as the actual amount that will be swapped.

So, the smartVaultV3::calculateMinimumAmountOut function uses _amount as the input amount to calculate the minimum output amount.

File: contracts/smartVaultV3.sol
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);
}

But in the SmartVaultV3::swap function, params.amountIn = _amount - swapFee.

File: contracts/SmartVaultV3.sol
L224: amountIn: _amount - swapFee,

As a result, the actual amount in is less than the amount users want to swap, and so the actual amount out will be less than the minimumAmountOut from smartVaultV3::calculateMinimumAmountOut(). This causes users to lose funds during swaps.

Impact

Users lose funds during swaps.

Tools Used

Manual review

Recommendations

Short term :
Use _amount - swapFee instead of _amount in SmartVaultV3::calculateMinimumAmountOut().

File: contracts/SmartVaultV3.sol
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);
+ uint256 minimumAmountOut = calculateMinimumAmountOut(_inToken, _outToken, _amount - swapFee);
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);
}

Long term :
Properly define the amount to pass in SmartVaultV3::calculateMinimumAmountOut() and the amount to use in params.amountIn in the SmartVaultV3::swap() function.
Return the output amount after the swap been executed and revert whether the returned value is at below the minimumAmountOut or not.

Updates

Lead Judging Commences

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

swapfee-incorrect-calc

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

swapfee-incorrect-calc

Support

FAQs

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