good morning!!!!

Skip to content
Snippets Groups Projects

positionChecker

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Jake Johnston
    Edited
    index.ts 7.33 KiB
    import { createPublicClient, http, parseAbi } from "viem"
    import * as dotenv from "dotenv"
    dotenv.config()
    
    const pc = createPublicClient({
      transport: http(
        "https://venn.apiary.software/rootstock",
      )
    })
    
    const tokenPrices = new Map<string, number>()
    
    const getTokenPrice = async (data: any) => {
      if (tokenPrices.has(data.token)) {
        return tokenPrices.get(data.token)
      }
      const result = await fetch(`https://omni.icarus.tools/rootstock`, {
        method: 'POST',
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: `cush_getTokenOverview`,
          params: data,
        }),
      })
      const resp = await result.json()
      if (resp.error) {
        throw new Error(resp.error.message)
      }
      return resp.result.price_deltas ? resp.result.price_deltas.price_usd ? resp.result.price_deltas.price_usd : 0 : 0
    }
    
    const abis = parseAbi([
      `function positions(uint256) public view returns (uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128)`,
      `function factory() public view returns (address)`,
      `function getPool(address,address,uint24) public view returns (address)`,
      `function slot0() public view returns (uint160,int24,uint16,uint16,uint8,bool)`,
      `function decimals() public view returns (uint8)`,
      `function symbol() public view returns (string)`,
      `function feeGrowthGlobal0X128() public view returns (uint256)`,
      `function feeGrowthGlobal1X128() public view returns (uint256)`,
      `function ticks(int24) public view returns (uint128,int128,uint256,uint256,int56,uint160,uint32,bool)`
    ])
    
    type PositionInformation = {
      id: number
      token0Symbol: string
      token1Symbol: string
      amount0: number
      amount1: number
      fees0: number
      fees1: number
      fees0USD: number
      fees1USD: number
      totalFeesUSD: number
      upperTick: number
      lowerTick: number
      currentTick: number
      rangeStatus: string
      token0Value: number
      token1Value: number
      tvl: number
    }
    
    const nftContract = "0x9d9386c042F194B460Ec424a1e57ACDE25f5C4b1"
    
    const getChainData = async (tokenId: bigint) => {
      const position = await pc.readContract({
        abi: abis,
        functionName: "positions",
        args: [tokenId],
        address: nftContract
      })
      const factory = await pc.readContract({
        abi: abis,
        functionName: "factory",
        address: nftContract
      })
      const pool = await pc.readContract({
        abi: abis,
        functionName: "getPool",
        args: [position[2], position[3], position[4]],
        address: factory,
      })
      const token0Decimals = await pc.readContract({
        abi: abis,
        functionName: "decimals",
        address: position[2],
      })
      const token0Symbol = await pc.readContract({
        abi: abis,
        functionName: "symbol",
        address: position[2],
      })
      const token1Decimals = await pc.readContract({
        abi: abis,
        functionName: "decimals",
        address: position[3],
      })
      const token1Symbol = await pc.readContract({
        abi: abis,
        functionName: "symbol",
        address: position[3],
      })
      const slot0 = await pc.readContract({
        abi: abis,
        functionName: "slot0",
        address: pool,
      })
      const feeGrowthGlobal0X128 = await pc.readContract({
        abi: abis,
        functionName: "feeGrowthGlobal0X128",
        address: pool,
      })
      const feeGrowthGlobal1X128 = await pc.readContract({
        abi: abis,
        functionName: "feeGrowthGlobal1X128",
        address: pool,
      })
      const tickLower = await pc.readContract({
        abi: abis,
        functionName: "ticks",
        args: [position[5]],
        address: pool,
      })
      const tickUpper = await pc.readContract({
        abi: abis,
        functionName: "ticks",
        args: [position[6]],
        address: pool,
      })
      const token0Address = position[2]
      const token1Address = position[3]
      const token0Price = await getTokenPrice([token0Address])
      const token1Price = await getTokenPrice([token1Address])
      const liquidity = Number(position[7])
      const tUpper = Number(position[6])
      const tLower = Number(position[5])
      const tick = Number(slot0[1])
    
    
      const fees0 = getFees(
        liquidity,
        tUpper,
        tLower,
        tick,
        Number(feeGrowthGlobal0X128),
        Number(tickLower[2]),
        Number(tickUpper[2]),
        Number(position[8]),
        token0Decimals,
      )
    
      const fees1 = getFees(
        liquidity,
        tUpper,
        tLower,
        tick,
        Number(feeGrowthGlobal1X128),
        Number(tickLower[3]),
        Number(tickUpper[3]),
        Number(position[9]),
        token1Decimals,
      )
    
      const amounts = calculateHoldings(
        liquidity,
        tUpper,
        tLower,
        tick,
        Number(slot0[0]),
      )
      const humanAmounts = amounts.map((amount, i) => amount / 10 ** [token0Decimals, token1Decimals][i])
    
      const t0Value = humanAmounts[0] * token0Price
      const t1Value = humanAmounts[1] * token1Price
    
      const positionInformation: PositionInformation = {
        id: Number(tokenId),
        token0Symbol: token0Symbol,
        token1Symbol: token1Symbol,
        amount0: humanAmounts[0],
        amount1: humanAmounts[1],
        fees0: fees0,
        fees1: fees1,
        fees0USD: fees0 * token0Price,
        fees1USD: fees1 * token1Price,
        totalFeesUSD: (fees0 * token0Price) + (fees1 * token1Price),
        upperTick: tUpper,
        lowerTick: tLower,
        currentTick: tick,
        rangeStatus: tick > tUpper ? "below" : tick < tLower ? "above" : "within",
        token0Value: t0Value,
        token1Value: t1Value,
        tvl: t0Value + t1Value,
      }
      return positionInformation
    }
    
    const getFees = (
      liquidity: number,
      tickUpper: number,
      tickLower: number,
      tick: number,
      feeGrowthGlobalX128: number,
      lowerFeegrowthOutsideX128: number,
      upperFeegrowthOutsideX128: number,
      feeGrowthInsideLastX128: number,
      decimals: number,
    ) => {
      let feesBelowLowerTick = lowerFeegrowthOutsideX128
      if (tick < tickLower) {
        feesBelowLowerTick = feeGrowthGlobalX128 - lowerFeegrowthOutsideX128
      }
      let feesBelowUpperTick = upperFeegrowthOutsideX128
      if (tick >= tickUpper) {
        feesBelowUpperTick = feeGrowthGlobalX128 - upperFeegrowthOutsideX128
      }
      let feesR = feeGrowthGlobalX128 - feesBelowLowerTick - feesBelowUpperTick
      const fees = liquidity * (feesR - feeGrowthInsideLastX128) / 2 ** 128
      const humanFees = fees / 10 ** decimals
      return humanFees
    }
    
    export const calculateHoldings = (liquidity: number, tickUpper: number, tickLower: number, currentTick: number, sqrtPriceX96: number) => {
      const inRange = currentTick >= tickLower && currentTick < tickUpper
      const sqrtRatioL = Math.sqrt(1.0001 ** (tickLower))
      const sqrtRatioU = Math.sqrt(1.0001 ** (tickUpper))
      const sqrtPrice = sqrtPriceX96 / 2 ** 96
    
      if (inRange) {
        const amount0 = liquidity * (sqrtRatioU - sqrtPrice) / (sqrtPrice * sqrtRatioU)
        const amount1 = liquidity * (sqrtPrice - sqrtRatioL)
    
        return [amount0, amount1]
      }
    
      if (sqrtPrice <= sqrtRatioL) {
        const amount0 = liquidity * (sqrtRatioU - sqrtRatioL) / (sqrtRatioL * sqrtRatioU)
        const amount1 = 0
        return [amount0, amount1]
      }
      const amount0 = 0
      const amount1 = liquidity * (sqrtRatioU - sqrtRatioL)
      return [amount0, amount1]
    }
    
    const run = async (tokens: string | undefined) => {
      if (tokens === undefined) {
        console.log("please create a .env file and put the token ids in the following format")
        console.log("TOKEN_ID=1,2,3")
        return
      }
      const allPositionInformation = []
      const tokenIds = tokens.split(",")
      for (const tokenId of tokenIds) {
        const info = await getChainData(BigInt(tokenId))
        allPositionInformation.push(info)
      }
      console.log(JSON.stringify(allPositionInformation, null, 2))
    }
    
    run(process.env.TOKEN_ID)
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment