Skip to content
Request an audit

Incorrect inside fees calculation for uninitialized uniswap ticks causes positions funds being stuck in the contract

TitleIncorrect inside fees calculation for uninitialized uniswap ticks causes positions funds being stuck in the contract
Reward$4432, Unique
ContestAmmplify - 2 Sep 2025 on Sherlock
Authorpanprog
ContextUniswap Fee Logic

PoolLib.getInsideFees calculates the inside fees, when the current price sits within the position range, as:

solidity
(, , uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128, , , , ) = poolContract.ticks(
        lowerTick
    );
    (, , uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128, , , , ) = poolContract.ticks(
        upperTick
    );
...
            uint256 feeGrowthGlobal0X128 = poolContract.feeGrowthGlobal0X128();
            uint256 feeGrowthGlobal1X128 = poolContract.feeGrowthGlobal1X128();
            feeGrowthInside0X128 = feeGrowthGlobal0X128 - lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128;
            feeGrowthInside1X128 = feeGrowthGlobal1X128 - lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128;

The issue is that this code does not handle uninitialized ticks. poolContract.ticks returns correct lowerFeeGrowthOutside amounts only for initialized ticks; for uninitialized ones it returns 0. That makes the feeGrowthInside amounts equal to feeGrowthGlobal.

As a result:

  • If a maker position is created on uninitialized Uniswap ticks while the current price is inside the position range, any further action on that position reverts, and the funds in it are stuck permanently.
  • An attacker can steal all the contract's funds by collecting inflated fees.

The first scenario happens when node.liq.feeGrowthInside0X128 and 1X128 were set to inflated values while the ticks were uninitialized, but newFeeGrowthInside0X128 and 1X128 return the post-initialization values of 0. The subtraction underflows and the position is stuck.

solidity
(uint256 newFeeGrowthInside0X128, uint256 newFeeGrowthInside1X128) = PoolLib.getInsideFees(
        data.poolAddr,
        iter.lowTick,
        iter.highTick
    );
    uint256 feeDiffInside0X128 = newFeeGrowthInside0X128 - node.liq.feeGrowthInside0X128;
    uint256 feeDiffInside1X128 = newFeeGrowthInside1X128 - node.liq.feeGrowthInside1X128;

Alpha: study Uniswap properly before integrating with it. Here, the fact that uninitialized ticks return zero fee-growth values was not handled, and the consequences are severe.

Conclusion

This finding would earn you $4432, and deep Uniswap knowledge is what gets you there.

Full Report
Codebase

Message on Telegram All 62 posts