Commit 568829d3 authored by Justin Domingue's avatar Justin Domingue Committed by GitHub

fix: invalidate subgraph cache with provideTags (#1962)

* invalidate queries using tags

* enhance generated api with provide tags

* clean up
parent b0f4b4c7
import { FeeAmount } from '@uniswap/v3-sdk' import { FeeAmount } from '@uniswap/v3-sdk'
import { Token } from '@uniswap/sdk-core' import { Token } from '@uniswap/sdk-core'
import { usePoolsQuery } from 'state/data/generated' import { useFeeTierDistributionQuery } from 'state/data/enhanced'
import { skipToken } from '@reduxjs/toolkit/query/react' import { skipToken } from '@reduxjs/toolkit/query/react'
import { reduce } from 'lodash' import { reduce } from 'lodash'
import { useBlockNumber } from 'state/application/hooks' import { useBlockNumber } from 'state/application/hooks'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { useMemo } from 'react' import { useMemo } from 'react'
import { FeeTierDistributionQuery } from 'state/data/generated'
import ms from 'ms.macro'
// maximum number of blocks past which we consider the data stale // maximum number of blocks past which we consider the data stale
const MAX_DATA_BLOCK_AGE = 10 const MAX_DATA_BLOCK_AGE = 10
...@@ -62,14 +64,14 @@ export function useFeeTierDistribution(token0: Token | undefined, token1: Token ...@@ -62,14 +64,14 @@ export function useFeeTierDistribution(token0: Token | undefined, token1: Token
function usePoolTVL(token0: Token | undefined, token1: Token | undefined) { function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
const latestBlock = useBlockNumber() const latestBlock = useBlockNumber()
const { isLoading, isFetching, isUninitialized, isError, data } = usePoolsQuery( const { isLoading, isFetching, isUninitialized, isError, data } = useFeeTierDistributionQuery(
token0 && token1 ? { token0: token0.address.toLowerCase(), token1: token1.address.toLowerCase() } : skipToken, token0 && token1 ? { token0: token0.address.toLowerCase(), token1: token1.address.toLowerCase() } : skipToken,
{ {
pollingInterval: 60000, // 1 minute pollingInterval: ms`1m`,
} }
) )
const { asToken0, asToken1, _meta } = data ?? {} const { asToken0, asToken1, _meta } = (data as FeeTierDistributionQuery) ?? {}
return useMemo(() => { return useMemo(() => {
if (!latestBlock || !_meta || !asToken0 || !asToken1) { if (!latestBlock || !_meta || !asToken0 || !asToken1) {
......
...@@ -4,10 +4,11 @@ import JSBI from 'jsbi' ...@@ -4,10 +4,11 @@ import JSBI from 'jsbi'
import { PoolState, usePool } from './usePools' import { PoolState, usePool } from './usePools'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import computeSurroundingTicks from 'utils/computeSurroundingTicks' import computeSurroundingTicks from 'utils/computeSurroundingTicks'
import { useAllV3TicksQuery } from 'state/data/generated' 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 cloneDeep from 'lodash/cloneDeep'
import { AllV3TicksQuery } from 'state/data/generated'
const PRICE_FIXED_DIGITS = 8 const PRICE_FIXED_DIGITS = 8
...@@ -74,7 +75,7 @@ export function usePoolActiveLiquidity( ...@@ -74,7 +75,7 @@ export function usePoolActiveLiquidity(
const token0 = currencyA?.wrapped const token0 = currencyA?.wrapped
const token1 = currencyB?.wrapped const token1 = currencyB?.wrapped
const sortedTickData = cloneDeep(ticks) const sortedTickData = cloneDeep(ticks as AllV3TicksQuery['ticks'])
sortedTickData.sort((a, b) => a.tickIdx - b.tickIdx) 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
......
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { api } from 'state/data/slice' import { api, CHAIN_TAG } from 'state/data/enhanced'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { supportedChainId } from 'utils/supportedChainId' import { supportedChainId } from 'utils/supportedChainId'
import useDebounce from '../../hooks/useDebounce' import useDebounce from '../../hooks/useDebounce'
...@@ -12,7 +12,7 @@ function useQueryCacheInvalidator() { ...@@ -12,7 +12,7 @@ function useQueryCacheInvalidator() {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
useEffect(() => { useEffect(() => {
dispatch(api.util.resetApiState()) dispatch(api.util.invalidateTags([CHAIN_TAG]))
}, [chainId, dispatch]) }, [chainId, dispatch])
} }
......
import { api as generatedApi } from './generated'
// tag that should be applied to queries that need to be invalidated when the chain changes
export const CHAIN_TAG = 'Chain'
// enhanced api to provide/invalidate tags
export const api = generatedApi.enhanceEndpoints({
addTagTypes: [CHAIN_TAG],
endpoints: {
allV3Ticks: {
providesTags: [CHAIN_TAG],
},
feeTierDistribution: {
providesTags: [CHAIN_TAG],
},
},
})
export const { useAllV3TicksQuery, useFeeTierDistributionQuery } = api
import { BaseQueryApi, BaseQueryFn } from '@reduxjs/toolkit/dist/query/baseQueryTypes'
import { createApi } from '@reduxjs/toolkit/query/react' import { createApi } from '@reduxjs/toolkit/query/react'
import { ClientError, gql, GraphQLClient } from 'graphql-request'
import { SupportedChainId } from 'constants/chains' import { SupportedChainId } from 'constants/chains'
import { AppState } from 'state'
import { BaseQueryApi, BaseQueryFn } from '@reduxjs/toolkit/dist/query/baseQueryTypes'
import { DocumentNode } from 'graphql' import { DocumentNode } from 'graphql'
import { ClientError, gql, GraphQLClient } from 'graphql-request'
import { AppState } from 'state'
export const UNISWAP_V3_GRAPH_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3' const UNISWAP_V3_GRAPH_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
export const graphqlRequestBaseQuery = (): BaseQueryFn<
{ document: string | DocumentNode; variables?: any },
unknown,
Pick<ClientError, 'name' | 'message' | 'stack'>,
Partial<Pick<ClientError, 'request' | 'response'>>
> => {
return async ({ document, variables }, { getState }: BaseQueryApi) => {
try {
const chainId = (getState() as AppState).application.chainId
let client: GraphQLClient | null = null
switch (chainId) {
case SupportedChainId.MAINNET:
client = new GraphQLClient(UNISWAP_V3_GRAPH_URL)
break
default:
return {
error: {
name: 'UnsupportedChainId',
message: `Subgraph queries again ChainId ${chainId} are not supported.`,
stack: '',
},
}
}
return { data: await client.request(document, variables), meta: {} }
} catch (error) {
if (error instanceof ClientError) {
const { name, message, stack, request, response } = error
return { error: { name, message, stack }, meta: { request, response } }
}
throw error
}
}
}
export const client = new GraphQLClient(UNISWAP_V3_GRAPH_URL)
export const api = createApi({ export const api = createApi({
reducerPath: 'dataApi', reducerPath: 'dataApi',
baseQuery: graphqlRequestBaseQuery(), baseQuery: graphqlRequestBaseQuery(),
endpoints: (builder) => ({ endpoints: (builder) => ({
getAllV3Ticks: builder.query({ allV3Ticks: builder.query({
query: ({ poolAddress, skip = 0 }) => ({ query: ({ poolAddress, skip = 0 }) => ({
document: gql` document: gql`
query allV3Ticks($poolAddress: String!, $skip: Int!) { query allV3Ticks($poolAddress: String!, $skip: Int!) {
...@@ -67,10 +29,10 @@ export const api = createApi({ ...@@ -67,10 +29,10 @@ export const api = createApi({
}, },
}), }),
}), }),
getFeeTierDistribution: builder.query({ feeTierDistribution: builder.query({
query: ({ token0, token1 }) => ({ query: ({ token0, token1 }) => ({
document: gql` document: gql`
query pools($token0: String!, $token1: String!) { query feeTierDistribution($token0: String!, $token1: String!) {
_meta { _meta {
block { block {
number number
...@@ -105,4 +67,40 @@ export const api = createApi({ ...@@ -105,4 +67,40 @@ export const api = createApi({
}), }),
}) })
export const { useGetFeeTierDistributionQuery } = api // Graphql query client wrapper that builds a dynamic url based on chain id
function graphqlRequestBaseQuery(): BaseQueryFn<
{ document: string | DocumentNode; variables?: any },
unknown,
Pick<ClientError, 'name' | 'message' | 'stack'>,
Partial<Pick<ClientError, 'request' | 'response'>>
> {
return async ({ document, variables }, { getState }: BaseQueryApi) => {
try {
const chainId = (getState() as AppState).application.chainId
let client: GraphQLClient | null = null
switch (chainId) {
case SupportedChainId.MAINNET:
client = new GraphQLClient(UNISWAP_V3_GRAPH_URL)
break
default:
return {
error: {
name: 'UnsupportedChainId',
message: `Subgraph queries again ChainId ${chainId} are not supported.`,
stack: '',
},
}
}
return { data: await client.request(document, variables), meta: {} }
} catch (error) {
if (error instanceof ClientError) {
const { name, message, stack, request, response } = error
return { error: { name, message, stack }, meta: { request, response } }
}
throw error
}
}
}
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