1

I've been experimenting with Uniswap V3 and aimed to mint a position covering the full price range possible within a pool, using the MIN_TICK (-887272) and MAX_TICK (887272) as my position's bounds. However, my attempts to mint this position consistently failed, with the transactions reverting without providing an error message. Can someone explain why this is?

1 Answer 1

2

The issue stems from a key design feature of Uniswap V3: tickSpacing. Uniswap V3 pools use tickSpacing to determine the minimum interval between ticks that can be active/initialized. This parameter constrains possible tick ranges (from MIN_TICK to MAX_TICK) that are usable for liquidity positions.

Calculating the Closest Valid Ticks

To mint a position covering as wide a range as possible, you need to calculate the nearest valid tick indexes to MIN_TICK and MAX_TICK that comply with the pool's tickSpacing. Here's a straightforward method to achieve that:

function findNearestValidTick(tickSpacing, nearestToMin) {
  const MIN_TICK = -887272;
  const MAX_TICK = 887272;

  if (nearestToMin) {
    // Adjust to the nearest valid tick above MIN_TICK
    return Math.ceil(MIN_TICK / tickSpacing) * tickSpacing;
  } else {
    // Adjust to the nearest valid tick below MAX_TICK
    return Math.floor(MAX_TICK / tickSpacing) * tickSpacing;
  }
}

Or an equivalent solidity implementation

/**
 * @dev Finds the nearest valid tick to either MIN_TICK or MAX_TICK based on the tickSpacing.
 * This function accounts for edge cases to ensure the returned tick is within valid range.
 * @param tickSpacing The spacing between valid ticks, must be a positive integer.
 * @param nearestToMin If true, finds the nearest valid tick greater than or equal to MIN_TICK.
 *                     If false, finds the nearest valid tick less than or equal to MAX_TICK.
 * @return The nearest valid tick as an integer, ensuring it falls
   within the valid tick range.
 */
function findNearestValidTick(int256 tickSpacing, bool nearestToMin) public pure returns (int256) {
    require(tickSpacing > 0, "Tick spacing must be positive");

    if (nearestToMin) {
        // Adjust to find a tick greater than or equal to MIN_TICK.
        int256 adjustedMinTick = TickMath.MIN_TICK + (tickSpacing - 1);
        // Prevent potential overflow.
        if (TickMath.MIN_TICK < 0 && adjustedMinTick > 0) {
            adjustedMinTick = TickMath.MIN_TICK;
        }
        int256 adjustedTick = (adjustedMinTick / tickSpacing) * tickSpacing;
        // Ensure the adjusted tick does not fall below MIN_TICK.
        return (adjustedTick > TickMath.MIN_TICK) ? adjustedTick - tickSpacing : adjustedTick;
    } else {
        // Find the nearest valid tick less than or equal to MAX_TICK, straightforward due to floor division.
        return (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
    }
}

This function takes into account the tickSpacing to adjust the bounds to the nearest valid ticks. For the minimum tick range, it rounds up to the next tick that aligns with tickSpacing, ensuring the position starts at a valid point. Conversely, for the maximum range, it rounds down, ensuring the position does not exceed the maximum allowable tick.

5
  • this seems wrong? return (adjustedTick > TickMath.MIN_TICK) ? adjustedTick - tickSpacing : adjustedTick
    – kfx
    Feb 13 at 8:16
  • @kfx can you explain your reasoning or at least provide a test case that fails?
    – MShakeG
    Feb 13 at 13:24
  • Well it looks like it returns correct values for normal tick spacing values, but for 900000 it returns -900000, not zero. More to the point, I think you could just replace the whole if branch with return (MIN_TICK / tickSpacing) * tickSpacing; and get correct values eveywhere
    – kfx
    Feb 17 at 14:05
  • @kfx so you called findNearestValidTick(900_000, false)? if so that isn't even a valid tick
    – MShakeG
    Feb 18 at 8:50
  • I agreed with you that it does return correct values for sensing tick spacing values. That was my oversight initially, due to the complex logic in the code. Please consider the second part of the previous comment!
    – kfx
    Feb 18 at 18:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.