The Standard

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

Vault `swap()` swaps amount - fees but expects minAmountOut calculated with fees included

Summary

Vault#swap() swaps amountIn: _amount - fees but expects amountOutMinimum equal to _amount converted to the other token without fees subtracted, hence swapping less and expecting more.

Vulnerability Details

When performing a swap() in a SmartVault itself, in the call to the swap router, it can be observed the following:

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);
}

minimumAmountOut - the amount we expect to get out of the swap is either 0 or is calculated by converting _amount amount of _inToken to _outToken:

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);
}

It doesn't take the swap fee by no means, meaning the swaps are prone to fail in the case when collateralValueMinusSwapValue < requiredCollateralValue (in calculateMinimumAmountOut()) as when otherwise, minimumAmountOut will be 0.

Impact

Swaps within a vault between two tokens are guaranteed to fail as the contract is swapping less tokens (_amount - swapFee) while expecting more (_amount of _inToken converted to _outToken without swap fees subtracted).

Tools Used

Manual review

Recommendations

When calculating minimumAmountOut subtract swapFee from _amount passing the value: calculateMinimumAmountOut(_inToken, _outToken, _amount - swapFee)

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.