0

I need help with math of Uniswap V3 virtual and real reserves. Need to find the true reserves of a pair.

Example: let's say we have this DAI/WETH pair: 0x60594a405d53811d3BC4766596EFD80fd545A270 balance of DAI was 2591437269710515203595384 and balance of WETH was 2838497576725882781612

As we can see here, reserve's of tokens in this pool disbalanced:

  • DAI value in pool: 2,594,028.706980225 $ (43 %)
  • WETH value in pool: 3,461,888.414526421 $ (57 %)

If I'm not mistaken, because of concentrated liquidity we have a different virtual reserve's in every "concentrated" zone. Also, those virtual reserve's should be balanced (50/50 - value) by x*y=k. Does anyone know how to find those virtual and/or real reserve's?

I've successfully calculated a price of both tokens:

token0Price = sqrtRatioX96 ** 2 / 2 ** 192 = 2267580935849786082246943435 **2 / 2 **192 = 5.141923300629392e+54 / 6.277101735386681e+57 = 0.0008191556417896165

token1Price =  2 ** 192 / sqrtRatioX96 ** 2 = 6.277101735386681e+57 / 2267580935849786082246943435 **2 = 6.277101735386681e+57 / 5.141923300629392e+54 = 1220.7692274636495

And prices are fine.

Reserves token0:

x = L / sqrt(P) 
x = 776218541774739337116335 / sqrt(0.0008191556417896165)
x = 776218541774739337116335 / 0.02862089519
x = 2.7120694046143822e+25
x = 27120694046143822000000000

Reserves token1

y = L * sqrt(P) 
# Suppose that sqrt(P) is sqrt(token1Price) then:
y = 776218541774739337116335 * sqrt(1220.7692274636495)
y = 776218541774739337116335 * 34.9395081171
y = 2.7120694040982028e+25
y = 27120694040982028000000000

Comparing x and y:

x = 27120694046143822000000000 
y = 27120694040982028000000000 

It looks like 50/50, but not by it's token reserves value. I'm not sure, that's my math is correct.

4
  • The virtual reserves in terms of x and y are very rarely needed; liquidity is almost always what you want to calculate and use. Real reserves in a pool are complex to calculate because of the non-uniform liquidity. See my answer here stackoverflow.com/a/71815432/2435820
    – kfx
    Commented Nov 22, 2022 at 11:19
  • In the question, token0Price and token1Price are successfully calculated using sqrtRatioX96 ** 2 / (2 ** 192) and (2 ** 192 )/ sqrtRatioX96 ** 2 respectively. But, in V3, how to get the sqrtRatioX96, as we're having sqrt_price_x96 derived from slot0 as slot0. sqrtPriceX96, so is there a way to calculate sqrtRatioX96 using sqrtPriceX96 ? Commented Apr 13 at 10:13
  • @SYEDASADKAZMI sqrtRatioX96 and sqrtPriceX96 are used as synonyms in the Uniswap v3 code, they're just different names for the same thing.
    – kfx
    Commented Apr 13 at 10:50
  • @kfx, here's my question: ethereum.stackexchange.com/questions/162525/…. Commented Apr 13 at 11:20

2 Answers 2

2

Here is a mjs file I created for the token balance of a V3 position, the math should be easily extractable

import { JSBI } from "@uniswap/sdk";
import { ethers } from 'ethers'
import * as fs from 'fs';


    // ERC20 json abi file
let ERC20Abi = fs.readFileSync('<path>/ERC20.json');
const ERC20 = JSON.parse(ERC20Abi);

    // V3 pool abi json file
let pool = fs.readFileSync('<path>/V3PairAbi.json');
const IUniswapV3PoolABI = JSON.parse(pool);

    // V3 factory abi json
let facto = fs.readFileSync('<path>/V3factory.json');
const IUniswapV3FactoryABI = JSON.parse(facto);

    // V3 NFT manager abi
let NFT = fs.readFileSync('<path>/UniV3NFT.json');
const IUniswapV3NFTmanagerABI = JSON.parse(NFT);

const provider = new ethers.providers.JsonRpcProvider("https://eth-mainnet.alchemyapi.io/v2/<API_Key>")

    // V3 standard addresses (different for celo)
const factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
const NFTmanager = "0xC36442b4a4522E871399CD717aBDD847Ab11FE88";



async function getData(tokenID){
    let FactoryContract = new ethers.Contract(factory, IUniswapV3FactoryABI, provider);

    let NFTContract =  new ethers.Contract(NFTmanager, IUniswapV3NFTmanagerABI, provider);
    let position = await NFTContract.positions(tokenID);
    
    let token0contract =  new ethers.Contract(position.token0, ERC20, provider);
    let token1contract =  new ethers.Contract(position.token1, ERC20, provider);
    let token0Decimal = await token0contract.decimals();
    let token1Decimal = await token1contract.decimals();
    
    let token0sym = await token0contract.symbol();
    let token1sym = await token1contract.symbol();
    
    let V3pool = await FactoryContract.getPool(position.token0, position.token1, position.fee);
    let poolContract = new ethers.Contract(V3pool, IUniswapV3PoolABI, provider);

    let slot0 = await poolContract.slot0();

    
    let pairName = token0sym +"/"+ token1sym;
    
    let dict = {"SqrtX96" : slot0.sqrtPriceX96.toString(), "Pair": pairName, "T0d": token0Decimal, "T1d": token1Decimal, "tickLow": position.tickLower, "tickHigh": position.tickUpper, "liquidity": position.liquidity.toString()}

    return dict
}



const Q96 = JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(96));
const MIN_TICK = -887272;
const MAX_TICK = 887272;


function getTickAtSqrtRatio(sqrtPriceX96){
    let tick = Math.floor(Math.log((sqrtPriceX96/Q96)**2)/Math.log(1.0001));
    return tick;
}


async function getTokenAmounts(liquidity,sqrtPriceX96,tickLow,tickHigh,token0Decimal,token1Decimal){
    let sqrtRatioA = Math.sqrt(1.0001**tickLow);
    let sqrtRatioB = Math.sqrt(1.0001**tickHigh);
    
    let currentTick = getTickAtSqrtRatio(sqrtPriceX96);
    let sqrtPrice = sqrtPriceX96 / Q96;
    
    let amount0wei = 0;
    let amount1wei = 0;
    if(currentTick <= tickLow){
        amount0wei = Math.floor(liquidity*((sqrtRatioB-sqrtRatioA)/(sqrtRatioA*sqrtRatioB)));
    }
    else if(currentTick > tickHigh){
        amount1wei = Math.floor(liquidity*(sqrtRatioB-sqrtRatioA));
    }
    else if(currentTick >= tickLow && currentTick < tickHigh){ 
        amount0wei = Math.floor(liquidity*((sqrtRatioB-sqrtPrice)/(sqrtPrice*sqrtRatioB)));
        amount1wei = Math.floor(liquidity*(sqrtPrice-sqrtRatioA));
    }
    
    let amount0Human = (amount0wei/(10**token0Decimal)).toFixed(token0Decimal);
    let amount1Human = (amount1wei/(10**token1Decimal)).toFixed(token1Decimal);

    console.log("Amount Token0 wei: "+amount0wei);
    console.log("Amount Token1 wei: "+amount1wei);
    console.log("Amount Token0 : "+amount0Human);
    console.log("Amount Token1 : "+amount1Human);
    return [amount0wei, amount1wei]
}




async function start(positionID){
    let data = await getData(positionID);
    let tokens = await getTokenAmounts(data.liquidity, data.SqrtX96, data.tickLow, data.tickHigh, data.T0d, data.T1d);
}


start(5)
2
  • Thank's al lot!) Will test!
    – Chenky
    Commented Dec 12, 2022 at 12:52
  • yeah, no problem. feel free to ask questions, and I'm a uniswap friend in the uniswap discord Commented Dec 15, 2022 at 4:09
2

To get the real reserves you can use the pool's balance0 and balance1 functions or the pool tokens' balanceOf function. Mind that these will also include unclaimed fees and removed liquidity that does not participate in market making. For more detailed answer, see here.

To get the virtual reserves, you can use the Uniswap v2 math formulas on the liquidity and price returned by the v3 pool's on-chain functions:

x = L / sqrt(P) and y = L * sqrt(P)

Python code (Web3.py) for this:

# get the liquidity and sqrt of price 
pool_contract = web3.eth.contract(address=pool_address, abi=v3_pool_abi)
slot0 = pool_contract.functions.slot0().call()
sqrt_price_x96 = slot0[0]
liquidity = pool_contract.functions.liquidity().call()

# compute the virtual reserves
Q96 = 2 ** 96
Q192 = 2 ** 192
reserve0_x96 = liquidity * Q192 / sqrt_price_x96
reserve1_x96 = liquidity * sqrt_price_x96

To convert to floating point numbers, divide the result with Q96.

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.