2

Does anyone know how to get the current liquidity per Token and the pool percentage from a uniswap v3 position via code, ideally javascript?

I had found something similar using eth_call for getting the unlcaimed fees number, but not sure if we can get those numbers, too somehow either via polygon RPC or via polygonscan. UniSwap V3 API : How to track **unclaimed fees** programmatically?

I need it for the Polygon chain, checked the working subgraph but couldn't spot any relevant options on this one, https://api.thegraph.com/subgraphs/name/ianlapham/uniswap-v3-polygon

Thanks

enter image description here

Thanks

2 Answers 2

5

Here is some code to get the position LQ amounts (excluding fees). The math is fully based on the liquidity, the current sqrtPrice, and the upper and lower ticks of the position. Three possible states, above current range, below current range, and if in range. All that's needed is to change the position ID in the start(). If you have detailed questions reach out to me in the uniswap discord

https://discord.gg/7UTZpQbTcZ

To get the pool fee you can call your Position ID on the NFT manager read function positions, and it returns the fee as dictionary return. In the contract the fee is recorded as multiplied by 10000 so the pools fees are (1% == 10000, .3% == 3000, .05% == 500, .01 == 100)

    let NFTContract =  new ethers.Contract(NFTmanager, IUniswapV3NFTmanagerABI, provider);
    let position = await NFTContract.positions(tokenID);
    let fee = position.fee / 10000;

This will log both human readable and lowest decimal format

    const { JSBI } = require("@uniswap/sdk");
    const { ethers } = require('ethers')
    const fs = require('fs');
    
    
        // ERC20 json abi file
    let ERC20Abi = fs.readFileSync('<file path>/SOLS/ERC20.json');
    const ERC20 = JSON.parse(ERC20Abi);
    
        // V3 pool abi json file
    let pool = fs.readFileSync('<file path>/SOLS/V3PairAbi.json');
    const IUniswapV3PoolABI = JSON.parse(pool);
    
        // V3 factory abi json
    let facto = fs.readFileSync('<file path>/SOLS/V3factory.json');
    const IUniswapV3FactoryABI = JSON.parse(facto);
    
    
    let NFT = fs.readFileSync('<file path>/SOLS/UniV3NFT.json');
    const IUniswapV3NFTmanagerABI = JSON.parse(NFT);
    
    const provider = new ethers.providers.JsonRpcProvider("<RPC_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 Decimal0 = await token0contract.decimals();
        let Decimal1 = 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 PoolInfo = {"SqrtX96" : slot0.sqrtPriceX96.toString(), "Pair": pairName, "Decimal0": Decimal0, "Decimal1": Decimal1, "tickLow": position.tickLower, "tickHigh": position.tickUpper, "liquidity": position.liquidity.toString()}
    
        return PoolInfo
    }
    
    
    
    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,Decimal0,Decimal1){
        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 = (Math.abs(amount0wei/(10**Decimal0))).toFixed(Decimal0);
        let amount1Human = (Math.abs(amount1wei/(10**Decimal1))).toFixed(Decimal1);
    
        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 PoolInfo = await getData(positionID);
        let tokens = await getTokenAmounts(PoolInfo.liquidity, PoolInfo.SqrtX96, PoolInfo.tickLow, PoolInfo.tickHigh, PoolInfo.Decimal0, PoolInfo.Decimal1);
    }
    
    
    start(1)

Here is a script to get the unclaimed owed fees of a position. This like my other script is in .mjs format js/typescript which should be easily convertible to either.

    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){
        var FactoryContract = new ethers.Contract(factory, IUniswapV3FactoryABI, provider);
    
        var NFTContract =  new ethers.Contract(NFTmanager, IUniswapV3NFTmanagerABI, provider);
        var position = await NFTContract.positions(tokenID);
        
        var token0contract =  new ethers.Contract(position.token0, ERC20, provider);
        var token1contract =  new ethers.Contract(position.token1, ERC20, provider);
        var Decimal0 = await token0contract.decimals();
        var Decimal1 = await token1contract.decimals();
        
        var token0sym = await token0contract.symbol();
        var token1sym = await token1contract.symbol();
        
        var V3pool = await FactoryContract.getPool(position.token0, position.token1, position.fee);
        var poolContract = new ethers.Contract(V3pool, IUniswapV3PoolABI, provider);
    
        var slot0 = await poolContract.slot0();
        var tickLow = await poolContract.ticks(position.tickLower.toString());
        var tickHi = await poolContract.ticks(position.tickUpper.toString());
        
        var feeGrowthGlobal0 = await poolContract.feeGrowthGlobal0X128();
        var feeGrowthGlobal1 = await poolContract.feeGrowthGlobal1X128();
        
        var pairName = token0sym +"/"+ token1sym;
        
        var PoolInfo = {
        "Pair": pairName, 
        "Decimal0": Decimal0, 
        "Decimal1": Decimal1, 
        "tickCurrent": slot0.tick, 
        "tickLow": position.tickLower, 
        "tickHigh": position.tickUpper, 
        "liquidity": position.liquidity.toString(), 
        "feeGrowth0Low": tickLow.feeGrowthOutside0X128.toString(),  
        "feeGrowth0Hi": tickHi.feeGrowthOutside0X128.toString(), 
        "feeGrowth1Low": tickLow.feeGrowthOutside1X128.toString(),  
        "feeGrowth1Hi": tickHi.feeGrowthOutside1X128.toString(),
        "feeGrowthInside0LastX128": position.feeGrowthInside0LastX128.toString(),
        "feeGrowthInside1LastX128": position.feeGrowthInside1LastX128.toString(),
        "feeGrowthGlobal0X128": feeGrowthGlobal0.toString(), 
        "feeGrowthGlobal1X128": feeGrowthGlobal1.toString()}
        
        return PoolInfo
    }
    
    
    const ZERO = JSBI.BigInt(0);
    const Q128 = JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(128));
    const Q256 = JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(256));
    
    
    function toBigNumber(numstr){
      let bi = numstr;
      if (typeof sqrtRatio !== 'bigint') {
        bi = JSBI.BigInt(numstr);
      }
      return bi;
    };
    
    
    function subIn256(x, y){
      const difference = JSBI.subtract(x, y)
    
      if (JSBI.lessThan(difference, ZERO)) {
        return JSBI.add(Q256, difference)
      } else {
        return difference
      }
    }
    
    
    async function getFees(feeGrowthGlobal0, feeGrowthGlobal1, feeGrowth0Low, feeGrowth0Hi, feeGrowthInside0, feeGrowth1Low, feeGrowth1Hi, feeGrowthInside1, liquidity, decimals0, decimals1, tickLower, tickUpper, tickCurrent){
        
        let feeGrowthGlobal_0 = toBigNumber(feeGrowthGlobal0);
        let feeGrowthGlobal_1 = toBigNumber(feeGrowthGlobal1);
    
        
        let tickLowerFeeGrowthOutside_0 = toBigNumber(feeGrowth0Low);
        let tickLowerFeeGrowthOutside_1 = toBigNumber(feeGrowth1Low);
    
        
        let tickUpperFeeGrowthOutside_0 = toBigNumber(feeGrowth0Hi);
        let tickUpperFeeGrowthOutside_1 = toBigNumber(feeGrowth1Hi);
    
        
        let tickLowerFeeGrowthBelow_0 = ZERO;
        let tickLowerFeeGrowthBelow_1 = ZERO;
        let tickUpperFeeGrowthAbove_0 = ZERO;
        let tickUpperFeeGrowthAbove_1 = ZERO;
    
        
        if (tickCurrent >= tickUpper){
            tickUpperFeeGrowthAbove_0 = subIn256(feeGrowthGlobal_0, tickUpperFeeGrowthOutside_0);
            tickUpperFeeGrowthAbove_1 = subIn256(feeGrowthGlobal_1, tickUpperFeeGrowthOutside_1);
        }else{
            tickUpperFeeGrowthAbove_0 = tickUpperFeeGrowthOutside_0
            tickUpperFeeGrowthAbove_1 = tickUpperFeeGrowthOutside_1
        }
    
        
        if (tickCurrent >= tickLower){
            tickLowerFeeGrowthBelow_0 = tickLowerFeeGrowthOutside_0
            tickLowerFeeGrowthBelow_1 = tickLowerFeeGrowthOutside_1
        }else{
            tickLowerFeeGrowthBelow_0 = subIn256(feeGrowthGlobal_0, tickLowerFeeGrowthOutside_0);
            tickLowerFeeGrowthBelow_1 = subIn256(feeGrowthGlobal_1, tickLowerFeeGrowthOutside_1);
        }
    
            
        let fr_t1_0 = subIn256(subIn256(feeGrowthGlobal_0, tickLowerFeeGrowthBelow_0), tickUpperFeeGrowthAbove_0);
        let fr_t1_1 = subIn256(subIn256(feeGrowthGlobal_1, tickLowerFeeGrowthBelow_1), tickUpperFeeGrowthAbove_1);
    
        
        let feeGrowthInsideLast_0 = toBigNumber(feeGrowthInside0);
        let feeGrowthInsideLast_1 = toBigNumber(feeGrowthInside1);
    
        
        let uncollectedFees_0 = (liquidity * subIn256(fr_t1_0, feeGrowthInsideLast_0)) / Q128;
        let uncollectedFees_1 = (liquidity * subIn256(fr_t1_1, feeGrowthInsideLast_1)) / Q128;
        console.log("Amount fees token 0 in lowest decimal: "+Math.floor(uncollectedFees_0));
        console.log("Amount fees token 1 in lowest decimal: "+Math.floor(uncollectedFees_1));
        
        
        let uncollectedFeesAdjusted_0 = (uncollectedFees_0 / toBigNumber(10**decimals0)).toFixed(decimals0);
        let uncollectedFeesAdjusted_1 = (uncollectedFees_1 / toBigNumber(10**decimals1)).toFixed(decimals1);
        console.log("Amount fees token 0 Human format: "+uncollectedFeesAdjusted_0);
        console.log("Amount fees token 1 Human format: "+uncollectedFeesAdjusted_1);
    }
    
    
    async function Start(positionID){
        var PoolInfo = await getData(positionID);
    
        
        var Fees = await getFees(PoolInfo.feeGrowthGlobal0X128, PoolInfo.feeGrowthGlobal1X128, PoolInfo.feeGrowth0Low, PoolInfo.feeGrowth0Hi, PoolInfo.feeGrowthInside0LastX128, PoolInfo.feeGrowth1Low, PoolInfo.feeGrowth1Hi, PoolInfo.feeGrowthInside1LastX128, PoolInfo.liquidity, PoolInfo.Decimal0, PoolInfo.Decimal1, PoolInfo.tickLow, PoolInfo.tickHigh, PoolInfo.tickCurrent);
    
    }
    
    
    Start(5);
1
  • You awesome. This helped me a lot. Commented Mar 23, 2023 at 22:50
0

You can also try chainbase dataset to fetch a token data.

Dataset blog

enter image description here

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.