Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Insufficiant slippage check in `dexAdapter`.

Summary

In dexAdapter, there is insufficiant slippage check.

Vulnerability Details

BaseAdapter.sol
95: function getExpectedOutput(
address tokenIn,
address tokenOut,
uint256 amountIn
)
public
view
returns (uint256 expectedAmountOut)
{
// fail fast for zero input
if (amountIn == 0) revert Errors.ZeroExpectedSwapOutput();
// get token prices
UD60x18 priceTokenInX18 = IPriceAdapter(swapAssetConfigData[tokenIn].priceAdapter).getPrice();
UD60x18 priceTokenOutX18 = IPriceAdapter(swapAssetConfigData[tokenOut].priceAdapter).getPrice();
// convert input amount from native to internal zaros precision
UD60x18 amountInX18 = Math.convertTokenAmountToUd60x18(swapAssetConfigData[tokenIn].decimals, amountIn);
// calculate the expected amount out in native precision of output token
expectedAmountOut = Math.convertUd60x18ToTokenAmount(
swapAssetConfigData[tokenOut].decimals, amountInX18.mul(priceTokenInX18).div(priceTokenOutX18)
);
// revert when calculated expected output is zero; must revert here
// otherwise the subsequent slippage bps calculation will also
// return a minimum swap output of zero giving away the input tokens
if (expectedAmountOut == 0) revert Errors.ZeroExpectedSwapOutput();
}
128:function calculateAmountOutMin(uint256 amountOutMinExpected) public view returns (uint256 amountOutMin) {
// calculate the amount out min
amountOutMin =
(amountOutMinExpected * (Constants.BPS_DENOMINATOR - slippageToleranceBps)) / Constants.BPS_DENOMINATOR;
}

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/utils/dex-adapters/UniswapV3Adapter.sol#L98

function executeSwapExactInputSingle(SwapExactInputSinglePayload calldata swapPayload)
external
returns (uint256 amountOut)
{
// transfer the tokenIn from the send to this contract
IERC20(swapPayload.tokenIn).transferFrom(msg.sender, address(this), swapPayload.amountIn);
// cache uniswap v3 swap strategy router
IUniswapV3RouterInterface swapRouter = IUniswapV3RouterInterface(uniswapV3SwapStrategyRouter);
// approve the tokenIn to the swap router
IERC20(swapPayload.tokenIn).approve(address(swapRouter), swapPayload.amountIn);
// get the expected output amount
uint256 expectedAmountOut = getExpectedOutput(swapPayload.tokenIn, swapPayload.tokenOut, swapPayload.amountIn);
// Calculate the minimum acceptable output based on the slippage tolerance
98: uint256 amountOutMin = calculateAmountOutMin(expectedAmountOut);
return swapRouter.exactInputSingle(
IUniswapV3RouterInterface.ExactInputSingleParams({
tokenIn: swapPayload.tokenIn,
tokenOut: swapPayload.tokenOut,
fee: feeBps,
recipient: swapPayload.recipient,
deadline: deadline,
amountIn: swapPayload.amountIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
})
);
}

As we can see, expectedAmountOut and amountOutMin are calculated in the same transaction.
If the prices from the priceAdapter and the dexAdapter are both subject to slippage, then this slippage check may not be necessary

Impact

Insufficient slippage checks for users can result in the loss of funds due to price fluctuations.

Recommendations

Consider allowing users to enter an amountOutMin.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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