Uniswap V3 initializing pools, adding liquidity and tools for calculations - Swiss knife post

Warning, this post is only useful if you tried out adding liquidity both with UI and with code, read about V3 most of the info, and want to have more clearer idea of what is what.

There are lots of books and posts about how to use Uniswap V3 and I will reference the best at the end of the article, yet some info and code on how to set proper values I didn’t find and had to dig out in various places. So let’s begin with some short snippets on how to get ticks and SqrtPriceX96 values depending on what you want to set and your liquidity plans. I could recode this in some solidity but actually, I use this with chatGPT and Python just to get quick results

Price to tick formula

import math

def price_to_tick(p):
    return math.floor(math.log(p, 1.0001))

price_to_tick(1000000)

Price to sqrtp

q96 = 2**96def price_to_sqrtp(p):    
return int(math.sqrt(p) * q96)

price_to_sqrtp(5000)

From sqrtp to price

def sqrtp_to_price(sqrtp):
return (sqrtp / q96) ** 2

Sqrtp to tick

q96 = 2**96

def sqrtp_to_tick(sqrtp):
    return math.floor(2 * math.log(sqrtp / q96, 1.0001))

So with some practical examples Price = y/x  or token1/token0 so if we want to add 1 eth and 1 000 000 of meme token then it is 1/1000000 or  0.00000001 and if we want to find a tick value for that price we need to put that into the formula above which will become −138,164  and if we convert that to sqrtp value it will be 79224253767016489810214999 when we put it into formula. This 1 / 1000 000 will also give us starting liquidity values, so we put 1 eth and 1 million meme tokens with that starting price, we want to put 2 eth, then calculate 2 / 1000000 and this value will be used in the createAndInitializePoolIfNecessary of NonfungiblePositionManager contract

     pool = nfpm.createAndInitializePoolIfNecessary(
            token0,
            token1,
            fee,
            sqrtPriceX96
        );

also, we can just use this solidity function to calculate sqrtPriceX96 from amounts of tokens we want to add

function calculateSqrtPriceX96(uint256 tokenAmount, uint256 wethAmount )

Code for that you can find here

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library SqrtPricex96 {
    uint256 constant Q96 = 2 ** 96;

    function calculateSqrtPriceX96(
        uint256 tokenAmount,
        uint256 wethAmount
    ) internal pure returns (uint160 sqrtPriceX96) {
        // Ensure non-zero values to prevent division by zero
        require(
            tokenAmount > 0 && wethAmount > 0,
            "Amounts must be greater than zero"
        );

        // First, multiply wethAmount by Q96^2 to scale it up before division
        // Then, divide by tokenAmount and finally calculate the square root
        // The multiplication by Q96^2 is done first to avoid loss of precision before the division
        uint256 scaledWethAmount = wethAmount * Q96 ** 2;
        uint256 priceRatio = scaledWethAmount / tokenAmount;

        // Now compute the square root
        sqrtPriceX96 = uint160(sqrt(priceRatio));
    }

    // Basic integer square root function
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        } else {
            z = 0;
        }
        return z;
    }

    // This function is to illustrate how you would reverse the calculation to find the price
    function calculatePriceFromSqrtPriceX96(
        uint256 sqrtPriceX96
    ) internal pure returns (uint256 price) {
        uint256 squaredPrice = uint256(sqrtPriceX96) ** 2;
        uint256 scaledPrice = squaredPrice / Q96 ** 2;

        price = uint160(scaledPrice);
    }
}

After that when doing the minting of V3 NFT and adding first liquidity w

nfpm.mint(INonfungiblePositionManager.MintParams({....

we will probably use this parameter to get the full range of ticks and depending on the fee pool type we choose to set these settings

int24 MIN_TICK = -887272;   
int24 MAX_TICK = 887272;    
uint24 fee = 3000;    
int24 TICK_SPACING = 60;    
int24 minTick = (MIN_TICK / TICK_SPACING) * TICK_SPACING;   
int24 maxTick = (MAX_TICK / TICK_SPACING) * TICK_SPACING;

tick spacing and fees are correlated with this table

Tick spacing and fees
Tick spacing and fees

Some other useful info

Uniswap pool will be created with multicall if you use their UI, to read that multicall use this
The first line is the init function
The second line adds liquidity
The third line is returning of extra eth

Reading multicall values, help here https://mixbytes.io/blog/how-to-add-new-pool-uniswap-v3
Also more help on reading multicalls here https://ethereum.stackexchange.com/questions/125052/decode-multicall-bytes-into-readable-format

All of this was done assuming tokens have the same decimal spaces if not it should be converted as in y/x should have also decimals multiplied for each of the tokens and then divided to get the final value of P. Can do the same with calculateSqrtPriceX96 function, just input token values with decimal places for both

Here is some extra information for more math and understanding
https://uniswapv3book.com/milestone_1/calculating-liquidity.html https://blog.uniswap.org/uniswap-v3-math-primer

Subscribe to N00b21337
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.