code-423n4 / 2024-04-panoptic-findings

9 stars 4 forks source link

Precision Loss in tickLower Calculation Leads to Incorrect Initialization of Liquidity Positions #549

Closed c4-bot-3 closed 7 months ago

c4-bot-3 commented 7 months ago

Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticFactory.sol#L392

Vulnerability details

Impact

An incorrect liquidity range can lead to higher slippage than expected for traders interacting with the pool, potentially resulting in less favorable prices

Misaligned ticks could also create unintended arbitrage opportunities, potentially leading to exploitation where traders could benefit from discrepancies in expected versus actual liquidity ranges.

This line intends to calculate tickLower by aligning Constants.MIN_V3POOL_TICK to the nearest lower tick that is divisible by tickSpacing. In Solidity, integer division truncates any remainder, which means that when Constants.MIN_V3POOL_TICK is not an exact multiple of tickSpacing, the result of the division and subsequent multiplication results in a value that is less than Constants.MIN_V3POOL_TICK.

Loc

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticFactory.sol#L392

tickLower = (Constants.MIN_V3POOL_TICK / tickSpacing) * tickSpacing;

Proof of Concept

For example, if Constants.MIN_V3POOL_TICK is -887273 and tickSpacing is 10, the division of -887273 by 10 yields -88727 (truncated, discarding the remainder), and multiplying back by 10 gives -887270, which is 3 ticks higher than -887273.

This could result in setting the lower tick limit to a value slightly higher than intended, thereby not fully utilizing the possible range.

Tools Used

Manual

Recommended Mitigation Steps

Consider adjusting the calculation to ensure it rounds down to the nearest tick that's fully compliant with the tick spacing. If the intention is always to round down to the nearest valid tick, you could adjust the logic to account for negative numbers correctly. Here’s how you could modify the calculation:

tickLower = (Constants.MIN_V3POOL_TICK / tickSpacing) * tickSpacing;
if (Constants.MIN_V3POOL_TICK % tickSpacing != 0) {
    tickLower -= tickSpacing;
}

This adjustment checks if there is a remainder in the division. If there is, it subtracts one tickSpacing from tickLower, ensuring that the result is always rounded down to the nearest valid tick, not rounded towards zero, which is the default behavior in Solidity for integer division.

Assessed type

Math

c4-judge commented 7 months ago

Picodes marked the issue as unsatisfactory: Invalid

Picodes commented 7 months ago

Isn't this the lowest possible tick that is divisible by the spacing?