Skip to content
Request an audit

‹ All findings

Leverager::deposit, does not support multi-hop swaps with exactOutput

MediumYieldoor·Sherlock · CLMM · 24th February 2025CodebaseM-10

Summary

When depositing into the leverager, if the borrowed token is not token0 or token1, the function has to perform an exactOutput swap, to receive the borrowed token0/token1.

solidity
function openLeveragedPosition(LeverageParams calldata lp) external nonReentrant returns (uint256 _id) {
        ...
        {
            // we first borrow the maximum amount the user is willing to borrow. Any unused within the swaps is later repaid.
            ILendingPool(lendingPool).borrow(lp.denomination, lp.maxBorrowAmount);

            IMainnetRouter.ExactOutputParams memory swapParams;

            if (a0 > lp.amount0In && up.token0 != lp.denomination) {
                swapParams = abi.decode(lp.swapParams1, (IMainnetRouter.ExactOutputParams));

                address tokenIn = _getTokenIn(swapParams.path);
                require(tokenIn == lp.denomination, "token should be denomination");

                IERC20(tokenIn).forceApprove(swapRouter, swapParams.amountInMaximum);

                swapParams.amountOut = a0 - lp.amount0In;
                IMainnetRouter(swapRouter).exactOutput(swapParams);
                IERC20(tokenIn).forceApprove(swapRouter, 0);
            }

            if (a1 > lp.amount1In && up.token1 != lp.denomination) {
                swapParams = abi.decode(lp.swapParams2, (IMainnetRouter.ExactOutputParams));
                address tokenIn = _getTokenIn(swapParams.path);
                require(tokenIn == lp.denomination, "token should be denomination 2 ");
                IERC20(tokenIn).forceApprove(swapRouter, swapParams.amountInMaximum);

                swapParams.amountOut = a1 - lp.amount1In;
                IMainnetRouter(swapRouter).exactOutput(swapParams);
                IERC20(tokenIn).forceApprove(swapRouter, 0);
            }
        }

        if (delta0 > 0) ILendingPool(lendingPool).pushFunds(up.token0, delta0);
        if (delta1 > 0) ILendingPool(lendingPool).pushFunds(up.token1, delta1);
        ...
    }

In order to validate that the denomination token is the tokenIn, we call the internal _getTokenIn.

solidity
    function _getTokenIn(bytes memory path) internal pure returns (address) {
        while (path.hasMultiplePools()) {
            path.skipToken();
        }

        (, address tokenIn,) = path.decodeFirstPool();
        return tokenIn;
    }

The function intends to skip, the pools until only the initial one is available, to get the tokenIn.

However, path is never changed which causes an infinite loop when the user is trying to do a multi-hop swaps.

Root Cause

When, skipping tokens, path is never changed, causing an infinite loop, for users trying to do multihop swaps.

Internal Pre-conditions

None

External Pre-conditions

  1. User tries to do a multi-hop swaps

Attack Path

None

Impact

The Leverager::deposit function fails to support multi-hop swaps as intended

Mitigation

Consider overwriting the path when calling skipToken

diff
 while (path.hasMultiplePools()) {
-          path.skipToken();
+         path  = path.skipToken();
        }