Lucene search

K
code423n4Code4renaCODE423N4:2023-12-PARTICLE-FINDINGS-ISSUES-44
HistoryDec 21, 2023 - 12:00 a.m.

impossible to open a position with a large marginTo

2023-12-2100:00:00
Code4rena
github.com
3
vulnerability details
particlepositionmanager
marginto limit
underflow
workaround
test case
mitigation.

7.2 High

AI Score

Confidence

Low

Lines of code

Vulnerability details

Description

marginTo/From is a way to both cover your position and increase your premium when opening a position. There is however a unintended limit on how much marginTo you can provide when opening a position.

When doing the swap to increase leverage, the amountToMinimum (minimum received amount) is calculated:

ParticlePositionManager::openPosition:

File: contracts/protocol/ParticlePositionManager.sol

212:            collateralTo - cache.amountToBorrowed - params.marginTo, // amount needed to meet requirement

As you see here, a marginTo > collateralTo - cache.amountToBorrowed will underflow this calculation. Thus there is no way to provide a bigger margin than this.

Impact

A user cannot supply more margin than collateralTo - amountToBorrowed before the opening of the position reverts.

There is a workaround to supply no marginTo and then use addPremium to increase the premium but I believe this issue still breaks the intended purpose of marginTo in the openPosition call.

Proof of Concept

Test in OpenPosition.t.sol:

    function testOpenLongPositionTooMuchMargin() public {
        // test based on testBaseOpenLongPosition and _borrowToLong
        uint128 borrowerLiquidity = _liquidity / _borrowerLiquidityPorition;
        (uint256 amount0ToBorrow, uint256 amount1ToBorrow) = LiquidityAmounts.getAmountsForLiquidity(
            _sqrtRatioX96,
            _sqrtRatioAX96,
            _sqrtRatioBX96,
            borrowerLiquidity
        );
        (, uint256 requiredEth) = particleInfoReader.getRequiredCollateral(borrowerLiquidity, _tickLower, _tickUpper);
        uint256 amountNeeded = QUOTER.quoteExactOutputSingle(
            address(USDC),
            address(WETH),
            FEE,
            requiredEth - amount1ToBorrow,
            0
        );
        uint256 amountFrom = amountNeeded + amountNeeded / 1e6 - amount0ToBorrow;
        uint256 amountSwap = amountFrom + amount0ToBorrow;
        amountFrom += (amountSwap * FEE_FACTOR) / (BASIS_POINT - FEE_FACTOR);

        // user supplies a large marginTo to get a big premium
        uint256 marginTo = requiredEth - amount1ToBorrow + 1;

        vm.startPrank(WHALE);
        USDC.transfer(SWAPPER, amountFrom);
        WETH.transfer(SWAPPER, marginTo);
        vm.stopPrank();

        vm.startPrank(SWAPPER);
        TransferHelper.safeApprove(address(USDC), address(particlePositionManager), amountFrom);
        TransferHelper.safeApprove(address(WETH), address(particlePositionManager), marginTo);

        // impossible as it will revert on underflow
        vm.expectRevert(stdError.arithmeticError);
        particlePositionManager.openPosition(
            DataStruct.OpenPositionParams({
                tokenId: _tokenId,
                marginFrom: amountFrom,
                marginTo: marginTo,
                amountSwap: amountSwap,
                liquidity: borrowerLiquidity,
                tokenFromPremiumPortionMin: 0,
                tokenToPremiumPortionMin: 0,
                marginPremiumRatio: type(uint8).max,
                zeroForOne: true,
                data: new bytes(0) // will not make it to the swap
            })
        );
        vm.stopPrank();
    }

Tools Used

Manual audit

Recommended Mitigation Steps

Consider not counting marginTo towards expected output from the swap. As shown in another issue, there are further problems with this. marginTo is the premium for the β€œto”-side of the position. Hence should not be part of the expected output of the swap as it is the safety for the position.

Assessed type

Under/Overflow


The text was updated successfully, but these errors were encountered:

All reactions

7.2 High

AI Score

Confidence

Low