Commit e0c62567 authored by Justin Domingue's avatar Justin Domingue Committed by GitHub

improve liquidity depth hooks and data fetching (#1989)

parent aa3c8672
...@@ -7,7 +7,6 @@ import computeSurroundingTicks from 'utils/computeSurroundingTicks' ...@@ -7,7 +7,6 @@ import computeSurroundingTicks from 'utils/computeSurroundingTicks'
import { useAllV3TicksQuery } from 'state/data/enhanced' import { useAllV3TicksQuery } from 'state/data/enhanced'
import { skipToken } from '@reduxjs/toolkit/query/react' import { skipToken } from '@reduxjs/toolkit/query/react'
import ms from 'ms.macro' import ms from 'ms.macro'
import cloneDeep from 'lodash/cloneDeep'
import { AllV3TicksQuery } from 'state/data/generated' import { AllV3TicksQuery } from 'state/data/generated'
const PRICE_FIXED_DIGITS = 8 const PRICE_FIXED_DIGITS = 8
...@@ -33,7 +32,7 @@ export function useAllV3Ticks( ...@@ -33,7 +32,7 @@ export function useAllV3Ticks(
currencyA && currencyB && feeAmount ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount) : undefined currencyA && currencyB && feeAmount ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount) : undefined
//TODO(judo): determine if pagination is necessary for this query //TODO(judo): determine if pagination is necessary for this query
const { isLoading, isError, data } = useAllV3TicksQuery( const { isLoading, isError, error, isUninitialized, data } = useAllV3TicksQuery(
poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken, poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken,
{ {
pollingInterval: ms`2m`, pollingInterval: ms`2m`,
...@@ -42,8 +41,10 @@ export function useAllV3Ticks( ...@@ -42,8 +41,10 @@ export function useAllV3Ticks(
return { return {
isLoading, isLoading,
isUninitialized,
isError, isError,
ticks: data?.ticks, error,
ticks: data?.ticks as AllV3TicksQuery['ticks'],
} }
} }
...@@ -51,21 +52,21 @@ export function usePoolActiveLiquidity( ...@@ -51,21 +52,21 @@ export function usePoolActiveLiquidity(
currencyA: Currency | undefined, currencyA: Currency | undefined,
currencyB: Currency | undefined, currencyB: Currency | undefined,
feeAmount: FeeAmount | undefined feeAmount: FeeAmount | undefined
): { ) {
isLoading: boolean
isError: boolean
activeTick: number | undefined
data: TickProcessed[]
} {
const [ticksProcessed, setTicksProcessed] = useState<TickProcessed[]>([]) const [ticksProcessed, setTicksProcessed] = useState<TickProcessed[]>([])
const pool = usePool(currencyA, currencyB, feeAmount) const pool = usePool(currencyA, currencyB, feeAmount)
const { isLoading, isError, ticks } = useAllV3Ticks(currencyA, currencyB, feeAmount)
// Find nearest valid tick for pool in case tick is not initialized. // Find nearest valid tick for pool in case tick is not initialized.
const activeTick = useMemo(() => getActiveTick(pool[1]?.tickCurrent, feeAmount), [pool, feeAmount]) const activeTick = useMemo(() => getActiveTick(pool[1]?.tickCurrent, feeAmount), [pool, feeAmount])
const { isLoading, isUninitialized, isError, error, ticks } = useAllV3Ticks(currencyA, currencyB, feeAmount)
useEffect(() => {
// reset local ticks processed
setTicksProcessed([])
}, [currencyA, currencyB, feeAmount])
useEffect(() => { useEffect(() => {
if (!currencyA || !currencyB || !activeTick || pool[0] !== PoolState.EXISTS || !ticks || ticks.length === 0) { if (!currencyA || !currencyB || !activeTick || pool[0] !== PoolState.EXISTS || !ticks || ticks.length === 0) {
setTicksProcessed([]) setTicksProcessed([])
...@@ -75,13 +76,10 @@ export function usePoolActiveLiquidity( ...@@ -75,13 +76,10 @@ export function usePoolActiveLiquidity(
const token0 = currencyA?.wrapped const token0 = currencyA?.wrapped
const token1 = currencyB?.wrapped const token1 = currencyB?.wrapped
const sortedTickData = cloneDeep(ticks as AllV3TicksQuery['ticks'])
sortedTickData.sort((a, b) => a.tickIdx - b.tickIdx)
// find where the active tick would be to partition the array // find where the active tick would be to partition the array
// if the active tick is initialized, the pivot will be an element // if the active tick is initialized, the pivot will be an element
// if not, take the previous tick as pivot // if not, take the previous tick as pivot
const pivot = sortedTickData.findIndex(({ tickIdx }) => tickIdx > activeTick) - 1 const pivot = ticks.findIndex(({ tickIdx }) => tickIdx > activeTick) - 1
if (pivot < 0) { if (pivot < 0) {
// consider setting a local error // consider setting a local error
...@@ -93,13 +91,13 @@ export function usePoolActiveLiquidity( ...@@ -93,13 +91,13 @@ export function usePoolActiveLiquidity(
liquidityActive: JSBI.BigInt(pool[1]?.liquidity ?? 0), liquidityActive: JSBI.BigInt(pool[1]?.liquidity ?? 0),
tickIdx: activeTick, tickIdx: activeTick,
liquidityNet: liquidityNet:
sortedTickData[pivot].tickIdx === activeTick ? JSBI.BigInt(sortedTickData[pivot].liquidityNet) : JSBI.BigInt(0), Number(ticks[pivot].tickIdx) === activeTick ? JSBI.BigInt(ticks[pivot].liquidityNet) : JSBI.BigInt(0),
price0: tickToPrice(token0, token1, activeTick).toFixed(PRICE_FIXED_DIGITS), price0: tickToPrice(token0, token1, activeTick).toFixed(PRICE_FIXED_DIGITS),
} }
const subsequentTicks = computeSurroundingTicks(token0, token1, activeTickProcessed, sortedTickData, pivot, true) const subsequentTicks = computeSurroundingTicks(token0, token1, activeTickProcessed, ticks, pivot, true)
const previousTicks = computeSurroundingTicks(token0, token1, activeTickProcessed, sortedTickData, pivot, false) const previousTicks = computeSurroundingTicks(token0, token1, activeTickProcessed, ticks, pivot, false)
const newTicksProcessed = previousTicks.concat(activeTickProcessed).concat(subsequentTicks) const newTicksProcessed = previousTicks.concat(activeTickProcessed).concat(subsequentTicks)
...@@ -108,7 +106,9 @@ export function usePoolActiveLiquidity( ...@@ -108,7 +106,9 @@ export function usePoolActiveLiquidity(
return { return {
isLoading: isLoading || pool[0] === PoolState.LOADING, isLoading: isLoading || pool[0] === PoolState.LOADING,
isError: isError || pool[0] === PoolState.INVALID, isUninitialized,
isError: isError,
error,
activeTick, activeTick,
data: ticksProcessed, data: ticksProcessed,
} }
......
...@@ -15,7 +15,7 @@ export const api = createApi({ ...@@ -15,7 +15,7 @@ export const api = createApi({
query: ({ poolAddress, skip = 0 }) => ({ query: ({ poolAddress, skip = 0 }) => ({
document: gql` document: gql`
query allV3Ticks($poolAddress: String!, $skip: Int!) { query allV3Ticks($poolAddress: String!, $skip: Int!) {
ticks(first: 1000, skip: $skip, where: { poolAddress: $poolAddress }) { ticks(first: 1000, skip: $skip, where: { poolAddress: $poolAddress }, orderBy: tickIdx) {
tickIdx tickIdx
liquidityNet liquidityNet
price0 price0
......
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { Token } from '@uniswap/sdk-core'
import computeSurroundingTicks from './computeSurroundingTicks'
import JSBI from 'jsbi'
import { AllV3TicksQuery } from 'state/data/generated'
const getV3Tick = (tickIdx: number, liquidityNet: number) => ({
tickIdx,
liquidityNet: JSBI.BigInt(liquidityNet),
price0: '1',
price1: '2',
})
describe('#computeSurroundingTicks', () => {
it('correctly compute active liquidity', () => {
const token0 = new Token(1, '0x2170ed0880ac9a755fd29b2688956bd959f933f8', 18)
const token1 = new Token(1, '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', 18)
const feeAmount = FeeAmount.LOW
const spacing = TICK_SPACINGS[feeAmount]
const activeTickProcessed = {
tickIdx: 1000,
liquidityActive: JSBI.BigInt(300),
liquidityNet: JSBI.BigInt(100),
price0: '100',
}
const pivot = 3
const ascending = true
const sortedTickData: AllV3TicksQuery['ticks'] = [
getV3Tick(activeTickProcessed.tickIdx - 4 * spacing, 10),
getV3Tick(activeTickProcessed.tickIdx - 2 * spacing, 20),
getV3Tick(activeTickProcessed.tickIdx - 1 * spacing, 30),
getV3Tick(activeTickProcessed.tickIdx * spacing, 100),
getV3Tick(activeTickProcessed.tickIdx + 1 * spacing, 40),
getV3Tick(activeTickProcessed.tickIdx + 2 * spacing, 20),
getV3Tick(activeTickProcessed.tickIdx + 5 * spacing, 20),
]
const previous = computeSurroundingTicks(token0, token1, activeTickProcessed, sortedTickData, pivot, !ascending)
const subsequent = computeSurroundingTicks(token0, token1, activeTickProcessed, sortedTickData, pivot, ascending)
expect(previous.length).toEqual(3)
expect(previous.map((t) => [t.tickIdx, parseFloat(t.liquidityActive.toString())])).toEqual([
[activeTickProcessed.tickIdx - 4 * spacing, 150],
[activeTickProcessed.tickIdx - 2 * spacing, 170],
[activeTickProcessed.tickIdx - 1 * spacing, 200],
])
expect(subsequent.length).toEqual(3)
expect(subsequent.map((t) => [t.tickIdx, parseFloat(t.liquidityActive.toString())])).toEqual([
[activeTickProcessed.tickIdx + 1 * spacing, 340],
[activeTickProcessed.tickIdx + 2 * spacing, 360],
[activeTickProcessed.tickIdx + 5 * spacing, 380],
])
})
})
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