The Standard

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

Some of the hardcoded parameters in swap() may lead to suboptimal swaps

Summary

In SmartVaultV3::swap function the fee is hardcoded at "3000" or 0.3%, but this will significantly reduce the possibilities for swap and will lead to non optimal routes, as well as the deadline is hardcoded to block.timestamp which isn't a valid deadline.

Vulnerability Details

Uniswap V3 pools can be at three fee levels: 0.05%, 0.30%, and 1% according to Uniswap Docs: https://docs.uniswap.org/concepts/protocol/fees.

In the Proof of Stake model, validators know in advance if they will propose one or multiple blocks ahead of time. In such a scenario, a malicious validator can hold back the transaction and execute it at a later block number.

By hardcoding the fee to 0.30% in the swap function and setting the deadline to block.timestamp:

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

fee: We significantly reduce the possibilities of swaps. That would mean that pools with less liquidity will be used which can impact the amountOut causing suboptimal price from less popular pools with less liquidity in them. Generally we wouldn't always choose the best path when it comes to swapping.

The fee is critical in determining the correct pool / best path: "The fee tier of the pool, used to determine the correct pool contract in which to execute the swap". as per Uniswap docs: https://docs.uniswap.org/contracts/v3/guides/swaps/single-swaps

Example:

We want to swap ETH for WBTC -> currently the best order routing as per Uniswap v3 is using the 0.05% ETH/WBTC pool, but since we've hardcoded it to 0.3%, we would have to use that pool which might be suboptimal both in terms the fee percentage, gas costs, etc.

More complex swaps like ARB to PAXG would suffer even greater losses as these utilize multiple pools, for instance it uses a 0.3% fee pool to swap ARB to ETH, then it uses a 1% fee pool to swap the ETH to PAXG.

Deadline: Setting the deadline as "block.timestamp" offers no protection as block.timestamp will have the value of whichever block the transaction is inserted into, hence the transaction can be held indefinitely by malicious validators OR be inserted in a block which may favor them.

Impact

The hardcoded fee leads to suboptimal routes which impact gas prices, increased fees (when compared to 0.05% pools) as well as token price fluctuations due to using suboptimal pools.

The hardcoded block.timestamp deadline offers no real protection as it can be set to whatever block timestamp the transaction is executed at.

Tools Used

  • Manual Review

Recommendations

  • Don't hardcode the fee, consider making it a function argument / input parameter, same goes for the deadline.

Updates

Lead Judging Commences

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

fixed-uni-fee

hrishibhat Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

deadline-check

hardcoded-fee

Support

FAQs

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

Give us feedback!