The Standard

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

Swap tokens that were not listed as collateral will be stuck in vault forever

Summary

The SmartVaultV3::swap() is used to swap collateral in the vault to allow the adjustment of risk. In the swap function the SmartVaultV3::calculateMinimumAmountOut() is called, user can swap any tokens that are not whitelisted as collateral for the vault if the collateralValueMinusSwapValue is more than requiredCollateralValue.

The SmartVaultV3::calculateMinimumAmountOut() is used to check if the collateral in the vault is in excess, which means that the amount of collateral is higher than the threshold of collateral.

Thus the main issue is that there will not be any checks for the output of tokens swapped if it's above required collateral amount and users will lose tokens since you cannot withdraw and remove collateral in the vault due to the Token Manager getToken function.

Vulnerability Details

// Even though is mocked, the actual function in TokenManager remains the same
// TokenManagerMock.sol
function getToken(bytes32 _symbol) external view returns (Token memory token) {
for (uint256 i = 0; i < acceptedTokens.length; i++) if (acceptedTokens[i].symbol == _symbol) token = acceptedTokens[i];
require(token.symbol == _symbol, "err-invalid-token"); //@audit will cause the token to revert if not listed
}
// SmartVaultV3.sol
function removeCollateral(bytes32 _symbol, uint256 _amount, address _to) external onlyOwner {
ITokenManager.Token memory token = getTokenManager().getToken(_symbol); // checks if token is whitelisted
require(canRemoveCollateral(token, _amount), UNDER_COLL);
IERC20(token.addr).safeTransfer(_to, _amount);
emit CollateralRemoved(_symbol, _amount, _to);
}
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);
//@audit user will lose tokens
return collateralValueMinusSwapValue >= requiredCollateralValue ?
0 : calculator.eurToToken(getToken(_outTokenSymbol), requiredCollateralValue - collateralValueMinusSwapValue); //@audit-issue
}
...
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); // @audit
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);
}

Impact

Users will lose tokens if the collateralized amount is higher than the collateral threshold during the swapping of collateral.

Tools Used

Manual Review

Recommendations

Check for the output of tokens swapped regardless of the collateral amount that is higher or lower than threshold,

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.