NFT Web3.py UniswapV3 – How to Get the Value of a Position on Uniswap V3 NFT

liquidity-providernftuniswapv3web3.py

I'm trying to get the amount of each token currently held in a liquidity pool position on Uniswap V3 (in Pyhton) based on the token ID of the NFT. I was able to get info about the position by calling :

contract = web3.eth.contract(address=addr_uni_nft_pos,abi=abi_uni_pos)    
contract.functions.positions(nft_token_id).call() 

from which I'm getting:

    {'nonce': 0,
     'operator': '0x0000000000000000000000000000000000000000',
     'token0': '0x4d224452801ACEd8B2F0aebE155379bb5D594381',
     'token1': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
     'fee': 3000,
     'tickLower': -56700,
     'tickUpper': -55740,
     'liquidity': 0,
     'feeGrowthInside0LastX128': 115792089237316195423570985008687907853246859851719844967496612553656335272574,
     'feeGrowthInside1LastX128': 115792089237316195423570985008687907853269894723013691862125737150933929444085,
     'tokensOwed0': 0,
     'tokensOwed1': 0}

However I want to get the total amount (deposited + rewards) of each token currently held on the pool, independently if the position is or not on range. Is there a way to do this? I was thinking in simply looking at the minting event of the NFT itself, but surely there is a simpler way.

Thanks in advance.

Best Answer

I wrote a function that perform a static call passing the maximum collectable value in other to see how much is pending to be collected.


export const calculatePendingFees = async (
  tokenId: number,
  recipient: string,
  amount0Max: BigNumber,
  amount1Max: BigNumber,
  chainId: number
) => {
  try {
    const params = {
      tokenId: tokenId,
      recipient: recipient,
      amount0Max: amount0Max,
      amount1Max: amount1Max,
    }

    const nonfungible = new ethers.Contract(
      NONFUNGIBLE_POSITION_MANAGER_ADDRESS[chainId],
      Nonfungiblepositionmanager.abi,
      PROVIDER[chainId]
    )

    const feesGenerated = await nonfungible.callStatic.collect(params)
    return feesGenerated
  } catch (error) {
    console.log(error.message, "calculate fees error")
  }
}

For amount0Maxand amount1Maxyou can use this:

const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)

This function will return to you the uncollected fees :)

The number of token0and token1can be retrieved through these functions:


export const calcAmount0 = (
  liquidity: number,
  currentPrice: number,
  priceUpper: number,
  token0Decimals: number,
  token1Decimals: number
) => {
  const decimalAdjustment = 10 ** (token0Decimals - token1Decimals)
  const mathCurrentPrice = currentPrice / decimalAdjustment
  const mathPriceUpper = priceUpper / decimalAdjustment

  const math =
    liquidity *
    ((Math.sqrt(mathPriceUpper) - Math.sqrt(mathCurrentPrice)) /
      (Math.sqrt(mathCurrentPrice) * Math.sqrt(mathPriceUpper)))
  const adjustedMath = math > 0 ? math : 0
  return adjustedMath
}

export const calcAmount1 = (
  liquidity: number,
  currentPrice: number,
  priceLower: number,
  token0Decimals: number,
  token1Decimals: number
) => {
  const decimalAdjustment = 10 ** (token0Decimals - token1Decimals)
  const mathCurrentPrice = currentPrice / decimalAdjustment
  const mathPriceLower = priceLower / decimalAdjustment

  const math =
    liquidity * (Math.sqrt(mathCurrentPrice) - Math.sqrt(mathPriceLower))
  const adjustedMath = math > 0 ? math : 0
  return adjustedMath
}

The price lower and price upper can be retrieved through this:

  const priceLower = tickToPrice(tickLower, decimalsA, decimalsB)
  const priceUpper = tickToPrice(tickUpper, decimalsA, decimalsB)

Till i know unfortunatelly we only have V3-SDK on npm so you will have plenty work writing anything.

Related Topic