positionChecker
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)
Please register or sign in to comment