1

This is my approach to increase a tick by a certain percentage:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.4;

import {TickMath} from "./TickMath.sol";
import {FullMath} from "./FullMath.sol";
import {PRBMathUD60x18} from "@prb/math/contracts/PRBMathUD60x18.sol";

contract TickPercentage {
    using TickMath for int24;

    function increaseTickByPercentage(int24 _tick, uint24 _tickSpacing, uint16 _percentage)
        external 
        pure
        returns (int24)
    {
        // Convert Tick to sqrtPriceX96
        uint160 currentPriceX96 = _tick.getSqrtRatioAtTick();
        
        // Convert SqrtPriceX96 to Wei price
        uint256 currentPriceX96ToUint = sqrtPriceX96ToUint(currentPriceX96, 18);

        // New Wei price increased by percentage amount
        uint256 newWeiPrice = currentPriceX96ToUint + (currentPriceX96ToUint * _percentage) / 10000;

        // Convert Wei price to SqrtPriceX96
        uint160 newPriceX96 = uint160(PRBMathUD60x18.sqrt(newWeiPrice) * 2 ** 96) / 1e18;

        // Convert SqrtPriceX96 to the nearest Tick and return
        return TickMath.nearestUsableTick(TickMath.getTickAtSqrtRatio(newPriceX96), _tickSpacing);       
    }

    function sqrtPriceX96ToUint(uint160 sqrtPriceX96, uint8 decimalsToken0)
        public 
        pure
        returns (uint256)
    {
        uint256 numerator1 = uint256(sqrtPriceX96) * uint256(sqrtPriceX96);
        uint256 numerator2 = 10**decimalsToken0;
        return FullMath.mulDiv(numerator1, numerator2, 1 << 192);
    }

For precision, I'm converting the Tick to its sqrtPriceX96 value, then converting that to a Wei value. Then I perform the percentage operation on the Wei value, and convert it back to sqrtPriceX96 then Tick.

It works, but it would be much more efficient to perform the percent operation directly on the Tick value, without having to do the conversions, I just don't know how to do it.

Is there a more efficient way to do this?

1 Answer 1

1

The good thing about the tick math is that a price increase (or decrease) by some ratio is equal to adding (or subtracting) a fixed number of ticks. For example, price increase by 5% is increase by delta_t ticks:

delta_t = log(1.05, 1.0001) = 487.9260363698946

which is approximately equal to 488 ticks.

The steps to compute a price increase from tick t by x%:

  1. Transform the percentage x to a ratio r;
  2. Transform the ratio r to a number of ticks delta_t;
  3. Add the number of ticks: t_new = t + t_delta.

The bad thing is that in order to translate between percentages and ticks you'd still need to compute logarithm at some point, which could be complex and expensive in Solidity. In the general case, you can use Uniswap's getTickAtSqrtRatio function for this. But if you have some fixed range of percentages, then a better option might be an immutable lookup table, which is precomputed using Python or another language, and maps between the percentages and the number of ticks:

...
delta_t[4] = 392;
delta_t[5] = 488;
delta_t[6] = 582;
...

Edit: to stress the point about gas optimizations more clearly, the precomputed table should be immutable (or constant, when possible). Using a table in the storage actually gives no cost savings over calling the simply calling the getTickAtSqrtRatio method. It's amazing, but all this complex math really does cost less gas than a single SLOAD instruction.

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.