Commit 54dd5476 authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

fetch fees directly from collect via callStatic (#1500)

* fetch fees directly from collect via callStatic

* don't clear state
parent 57786335
import { useSingleCallResult } from 'state/multicall/hooks' import { useSingleCallResult } from 'state/multicall/hooks'
import { useMemo } from 'react' import { useEffect, useState } from 'react'
import { PositionDetails } from 'types/position' import { PositionDetails } from 'types/position'
import { useV3Pool } from './useContract' import { useV3NFTPositionManagerContract } from './useContract'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { computePoolAddress, Pool } from '@uniswap/v3-sdk' import { Pool } from '@uniswap/v3-sdk'
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3'
import { useActiveWeb3React } from 'hooks'
import { TokenAmount } from '@uniswap/sdk-core' import { TokenAmount } from '@uniswap/sdk-core'
import { useBlockNumber } from 'state/application/hooks'
// TODO port these utility functions to the SDK const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
function subIn256(x: BigNumber, y: BigNumber): BigNumber {
const difference = x.sub(y)
return difference.lt(0) ? BigNumber.from(2).pow(256).add(difference) : difference
}
function getCounterfactualFees(
feeGrowthGlobal: BigNumber,
feeGrowthOutsideLower: BigNumber,
feeGrowthOutsideUpper: BigNumber,
feeGrowthInsideLast: BigNumber,
pool: Pool,
liquidity: BigNumber,
tickLower: number,
tickUpper: number
) {
let feeGrowthBelow: BigNumber
if (pool.tickCurrent >= tickLower) {
feeGrowthBelow = feeGrowthOutsideLower
} else {
feeGrowthBelow = subIn256(feeGrowthGlobal, feeGrowthOutsideLower)
}
let feeGrowthAbove: BigNumber
if (pool.tickCurrent < tickUpper) {
feeGrowthAbove = feeGrowthOutsideUpper
} else {
feeGrowthAbove = subIn256(feeGrowthGlobal, feeGrowthOutsideUpper)
}
const feeGrowthInside = subIn256(subIn256(feeGrowthGlobal, feeGrowthBelow), feeGrowthAbove)
return subIn256(feeGrowthInside, feeGrowthInsideLast).mul(liquidity).div(BigNumber.from(2).pow(128))
}
// compute current + counterfactual fees for a v3 position // compute current + counterfactual fees for a v3 position
export function useV3PositionFees( export function useV3PositionFees(
pool?: Pool, pool?: Pool,
positionDetails?: PositionDetails positionDetails?: PositionDetails
): [TokenAmount, TokenAmount] | [undefined, undefined] { ): [TokenAmount, TokenAmount] | [undefined, undefined] {
const { chainId } = useActiveWeb3React() const positionManager = useV3NFTPositionManagerContract()
const owner = useSingleCallResult(positionDetails?.tokenId ? positionManager : null, 'ownerOf', [
const poolAddress = useMemo(() => { positionDetails?.tokenId,
try { ]).result?.[0]
return chainId && V3_CORE_FACTORY_ADDRESSES[chainId] && pool && positionDetails
? computePoolAddress({ const tokenId = positionDetails?.tokenId?.toHexString()
factoryAddress: V3_CORE_FACTORY_ADDRESSES[chainId] as string, const latestBlockNumber = useBlockNumber()
tokenA: pool.token0,
tokenB: pool.token1, // TODO find a way to get this into multicall
fee: positionDetails.fee, // because fees don't ever go down, we don't actually need to clear this state
// latestBlockNumber is included to ensure data stays up-to-date fresh
const [amounts, setAmounts] = useState<[BigNumber, BigNumber]>()
useEffect(() => {
if (positionManager && tokenId && owner && typeof latestBlockNumber === 'number') {
positionManager.callStatic
.collect(
{
tokenId,
recipient: owner, // some tokens might fail if transferred to address(0)
amount0Max: MAX_UINT128,
amount1Max: MAX_UINT128,
},
{ from: owner } // need to simulate the call as the owner
)
.then((results) => {
setAmounts([results.amount0, results.amount1])
}) })
: undefined
} catch {
return undefined
} }
}, [chainId, pool, positionDetails]) }, [positionManager, tokenId, owner, latestBlockNumber])
const poolContract = useV3Pool(poolAddress)
// data fetching
const feeGrowthGlobal0: BigNumber | undefined = useSingleCallResult(poolContract, 'feeGrowthGlobal0X128')?.result?.[0]
const feeGrowthGlobal1: BigNumber | undefined = useSingleCallResult(poolContract, 'feeGrowthGlobal1X128')?.result?.[0]
const { feeGrowthOutside0X128: feeGrowthOutsideLower0 } = (useSingleCallResult(poolContract, 'ticks', [
positionDetails?.tickLower,
])?.result ?? {}) as { feeGrowthOutside0X128?: BigNumber }
const { feeGrowthOutside1X128: feeGrowthOutsideLower1 } = (useSingleCallResult(poolContract, 'ticks', [
positionDetails?.tickLower,
])?.result ?? {}) as { feeGrowthOutside1X128?: BigNumber }
const { feeGrowthOutside0X128: feeGrowthOutsideUpper0 } = (useSingleCallResult(poolContract, 'ticks', [
positionDetails?.tickUpper,
])?.result ?? {}) as { feeGrowthOutside0X128?: BigNumber }
const { feeGrowthOutside1X128: feeGrowthOutsideUpper1 } = (useSingleCallResult(poolContract, 'ticks', [
positionDetails?.tickUpper,
])?.result ?? {}) as { feeGrowthOutside1X128?: BigNumber }
// calculate fees
const counterfactualFees0 =
positionDetails && pool && feeGrowthGlobal0 && feeGrowthOutsideLower0 && feeGrowthOutsideUpper0
? getCounterfactualFees(
feeGrowthGlobal0,
feeGrowthOutsideLower0,
feeGrowthOutsideUpper0,
positionDetails.feeGrowthInside0LastX128,
pool,
positionDetails.liquidity,
positionDetails.tickLower,
positionDetails.tickUpper
)
: undefined
const counterfactualFees1 =
positionDetails && pool && feeGrowthGlobal1 && feeGrowthOutsideLower1 && feeGrowthOutsideUpper1
? getCounterfactualFees(
feeGrowthGlobal1,
feeGrowthOutsideLower1,
feeGrowthOutsideUpper1,
positionDetails.feeGrowthInside1LastX128,
pool,
positionDetails.liquidity,
positionDetails.tickLower,
positionDetails.tickUpper
)
: undefined
if ( if (pool && positionDetails && amounts) {
pool &&
positionDetails?.tokensOwed0 &&
positionDetails?.tokensOwed1 &&
counterfactualFees0 &&
counterfactualFees1
) {
return [ return [
new TokenAmount(pool.token0, positionDetails.tokensOwed0.add(counterfactualFees0).toString()), new TokenAmount(pool.token0, positionDetails.tokensOwed0.add(amounts[0]).toString()),
new TokenAmount(pool.token1, positionDetails.tokensOwed1.add(counterfactualFees1).toString()), new TokenAmount(pool.token1, positionDetails.tokensOwed1.add(amounts[1]).toString()),
] ]
} else { } else {
return [undefined, undefined] return [undefined, undefined]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment