The vault executes swaps with no slippage protection which will cause a loss of funds due of sandwich attacks.
The concept of slippage is often associated with market orders, where the executed price might deviate from the expected price due to market fluctuations. In the context of SmartVaultV3.sol
contract, slippage might occur if the calculated minimumAmountOut
is not set appropriately. If it's set too low, there's a risk that the actual output tokens received might be less than expected, potentially causing the transaction to revert.
This is the swap
function that performs the swap:
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);
}
function setOwner(address _newOwner) external onlyVaultManager {
owner = _newOwner;
}
This is the calculateMinimumAmountOut
function that calculates the minimum amount out:
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);
}
Swaps will be sandwiched that causes loss of funds.
Here's a scenario:
1, Legitimate Transaction: A user initiates a legitimate swap with the contract, and calculateMinimumAmountOut
returns 0 because the collateral after the swap is sufficient.
2, Sandwich Attack: An attacker quickly executes a series of transactions, including one before and one after the legitimate swap. The attacker aims to manipulate prices or take advantage of the legitimate trader's actions.
3, Outcome: The attacker's transactions could impact the price and lead to undesirable results for the legitimate trader.
Manual Review
A slippage tolerance parameter can be used. Ex:
uint256 slippageTolerance = 5; // 5% slippage tolerance
uint256 minimumAmountOut = (expectedAmountOut * (100 - slippageTolerance)) / 100;
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.