Skip to content

Commit

Permalink
feat: integrate relay (Uniswap#4320)
Browse files Browse the repository at this point in the history
* setup relay compiler
* refactored to use polling interval, fixed PR comments
* fixes, readded uninitialized state for liquidity chart
* updated cypress test
* reorganized graphql files into src/graphql
  • Loading branch information
cartcrom authored Aug 17, 2022
1 parent d6d0a98 commit 91f4892
Show file tree
Hide file tree
Showing 23 changed files with 395 additions and 295 deletions.
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"src/abis/types",
"src/locales/**/*.js",
"src/locales/**/en-US.po",
"src/state/data/generated.ts",
"node_modules",
"coverage",
"build",
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.


# generated contract types
/src/types/v3
/src/abis/types
/src/locales/**/*.js
/src/locales/**/en-US.po
/src/locales/**/pseudo.po
/src/state/data/generated.ts

# generated graphql types
/src/graphql/schema/
__generated__/

# dependencies
/node_modules
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/src/state/data/generated.ts
/src/schema/schema.graphql
8 changes: 2 additions & 6 deletions codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ overrideExisting: true
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
documents: 'src/**/!(*.d).{ts,tsx}'
generates:
./src/state/data/generated.ts:
./src/graphql/schema/schema.graphql:
plugins:
- typescript
- typescript-operations
- typescript-rtk-query:
importBaseApiFrom: './slice'
exportHooks: true
- schema-ast
6 changes: 3 additions & 3 deletions cypress/e2e/add-liquidity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ describe('Add Liquidity', () => {
it('loads fee tier distribution', () => {
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'feeTierDistribution')) {
req.alias = 'feeTierDistributionQuery'
if (hasQuery(req, 'FeeTierDistributionQuery')) {
req.alias = 'FeeTierDistributionQuery'

req.reply({
body: {
Expand All @@ -55,7 +55,7 @@ describe('Add Liquidity', () => {

cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab')

cy.wait('@feeTierDistributionQuery')
cy.wait('@FeeTierDistributionQuery')

cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40%')
Expand Down
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
"graphql:generate": "graphql-codegen --config codegen.yml",
"relay": "relay-compiler",
"graphql:generate": "graphql-codegen --config codegen.yml && yarn relay",
"prei18n:extract": "node prei18n-extract.js",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "yarn i18n:extract && lingui compile",
Expand All @@ -22,6 +23,11 @@
"cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e"
},
"relay": {
"src": "./src",
"language": "typescript",
"schema": "./src/graphql/schema/schema.graphql"
},
"jest": {
"collectCoverageFrom": [
"src/components/**/*.ts*",
Expand Down Expand Up @@ -58,8 +64,7 @@
"@craco/craco": "6.4.3",
"@ethersproject/experimental": "^5.4.0",
"@graphql-codegen/cli": "1.21.5",
"@graphql-codegen/typescript": "1.22.3",
"@graphql-codegen/typescript-operations": "^1.18.2",
"@graphql-codegen/schema-ast": "^2.5.1",
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
"@lingui/cli": "^3.9.0",
"@testing-library/jest-dom": "^5.16.4",
Expand Down Expand Up @@ -90,6 +95,7 @@
"@types/wcag-contrast": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4",
"@typescript-eslint/parser": "^4",
"babel-plugin-relay": "^14.1.0",
"@vanilla-extract/babel-plugin": "^1.1.7",
"@vanilla-extract/webpack-plugin": "^2.1.11",
"cypress": "^10.3.1",
Expand All @@ -106,6 +112,7 @@
"ms.macro": "^2.0.0",
"prettier": "^2.7.1",
"react-scripts": "^4.0.3",
"relay-compiler": "^14.1.0",
"serve": "^11.3.2",
"typechain": "^5.0.0",
"typescript": "^4.4.3"
Expand All @@ -126,6 +133,7 @@
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@types/react-relay": "^13.0.2",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
Expand Down Expand Up @@ -174,7 +182,7 @@
"firebase": "^9.1.3",
"focus-visible": "^5.2.0",
"fortmatic": "^2.4.0",
"graphql": "^15.5.0",
"graphql": "^16.5.0",
"graphql-request": "^3.4.0",
"immer": "^9.0.6",
"inter-ui": "^3.13.1",
Expand All @@ -199,6 +207,7 @@
"react-popper": "^2.2.3",
"react-query": "^3.39.1",
"react-redux": "^8.0.2",
"react-relay": "^14.1.0",
"react-router-dom": "^6.3.0",
"react-spring": "^8.0.27",
"react-table": "^7.8.0",
Expand All @@ -208,6 +217,7 @@
"rebass": "^4.0.7",
"redux": "^4.1.2",
"redux-localstorage-simple": "^2.3.1",
"relay-hooks": "^7.1.0",
"setimmediate": "^1.0.5",
"styled-components": "^5.3.5",
"tiny-invariant": "^1.2.0",
Expand Down
8 changes: 3 additions & 5 deletions src/components/LiquidityChartRangeInput/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function useDensityChartData({
currencyB: Currency | undefined
feeAmount: FeeAmount | undefined
}) {
const { isLoading, isUninitialized, isError, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
const { isLoading, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)

const formatData = useCallback(() => {
if (!data?.length) {
Expand Down Expand Up @@ -42,10 +42,8 @@ export function useDensityChartData({
return useMemo(() => {
return {
isLoading,
isUninitialized,
isError,
error,
formattedData: !isLoading && !isUninitialized ? formatData() : undefined,
formattedData: !isLoading ? formatData() : undefined,
}
}, [isLoading, isUninitialized, isError, error, formatData])
}, [isLoading, error, formatData])
}
8 changes: 5 additions & 3 deletions src/components/LiquidityChartRangeInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default function LiquidityChartRangeInput({

const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)

const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
const { isLoading, error, formattedData } = useDensityChartData({
currencyA,
currencyB,
feeAmount,
Expand Down Expand Up @@ -157,10 +157,12 @@ export default function LiquidityChartRangeInput({
[isSorted, price, ticksAtLimit]
)

if (isError) {
if (error) {
sendEvent('exception', { description: error.toString(), fatal: false })
}

const isUninitialized = !currencyA || !currencyB || (formattedData === undefined && !isLoading)

return (
<AutoColumn gap="md" style={{ minHeight: '200px' }}>
{isUninitialized ? (
Expand All @@ -170,7 +172,7 @@ export default function LiquidityChartRangeInput({
/>
) : isLoading ? (
<InfoBox icon={<Loader size="40px" stroke={theme.deprecated_text4} />} />
) : isError ? (
) : error ? (
<InfoBox
message={<Trans>Liquidity data not available.</Trans>}
icon={<CloudOff size={56} stroke={theme.deprecated_text4} />}
Expand Down
2 changes: 1 addition & 1 deletion src/constants/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(Support
(id) => typeof id === 'number'
) as SupportedChainId[]

export function isSupportedChain(chainId: number | undefined): chainId is SupportedChainId {
export function isSupportedChain(chainId: number | null | undefined): chainId is SupportedChainId {
return !!chainId && !!SupportedChainId[chainId]
}

Expand Down
53 changes: 53 additions & 0 deletions src/graphql/AllV3TicksQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import graphql from 'babel-plugin-relay/macro'
import useInterval from 'lib/hooks/useInterval'
import { useCallback, useEffect, useState } from 'react'
import { fetchQuery, useRelayEnvironment } from 'relay-hooks'
import { useAppSelector } from 'state/hooks'

import type {
AllV3TicksQuery as AllV3TicksQueryType,
AllV3TicksQuery$data,
} from './__generated__/AllV3TicksQuery.graphql'

const query = graphql`
query AllV3TicksQuery($poolAddress: String!, $skip: Int!) {
ticks(first: 1000, skip: $skip, where: { poolAddress: $poolAddress }, orderBy: tickIdx) {
tick: tickIdx
liquidityNet
price0
price1
}
}
`

export type Ticks = AllV3TicksQuery$data['ticks']
export type TickData = Ticks[number]

export default function useAllV3TicksQuery(poolAddress: string | undefined, skip: number, interval: number) {
const [data, setData] = useState<AllV3TicksQuery$data | null>(null)
const [error, setError] = useState<any>(null)
const [isLoading, setIsLoading] = useState(true)
const chainId = useAppSelector((state) => state.application.chainId)
const environment = useRelayEnvironment()

const refreshData = useCallback(() => {
if (poolAddress && chainId) {
fetchQuery<AllV3TicksQueryType>(environment, query, {
poolAddress: poolAddress.toLowerCase(),
skip,
}).subscribe({
next: setData,
error: setError,
complete: () => setIsLoading(false),
})
} else {
setIsLoading(false)
}
}, [poolAddress, skip, chainId, environment])

// Trigger fetch on first load
useEffect(refreshData, [refreshData, poolAddress, skip])

useInterval(refreshData, interval, true)
return { error, isLoading, data }
}
69 changes: 69 additions & 0 deletions src/graphql/FeeTierDistributionQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import graphql from 'babel-plugin-relay/macro'
import useInterval from 'lib/hooks/useInterval'
import { useCallback, useEffect, useState } from 'react'
import { fetchQuery, useRelayEnvironment } from 'relay-hooks'
import { useAppSelector } from 'state/hooks'

import type {
FeeTierDistributionQuery as FeeTierDistributionQueryType,
FeeTierDistributionQuery$data,
} from './__generated__/FeeTierDistributionQuery.graphql'

const query = graphql`
query FeeTierDistributionQuery($token0: String!, $token1: String!) {
_meta {
block {
number
}
}
asToken0: pools(
orderBy: totalValueLockedToken0
orderDirection: desc
where: { token0: $token0, token1: $token1 }
) {
feeTier
totalValueLockedToken0
totalValueLockedToken1
}
asToken1: pools(
orderBy: totalValueLockedToken0
orderDirection: desc
where: { token0: $token1, token1: $token0 }
) {
feeTier
totalValueLockedToken0
totalValueLockedToken1
}
}
`

export default function useFeeTierDistributionQuery(
token0: string | undefined,
token1: string | undefined,
interval: number
) {
const [data, setData] = useState<FeeTierDistributionQuery$data | null>(null)
const [error, setError] = useState<any>(null)
const [isLoading, setIsLoading] = useState(true)
const environment = useRelayEnvironment()
const chainId = useAppSelector((state) => state.application.chainId)

const refreshData = useCallback(() => {
if (token0 && token1 && chainId) {
fetchQuery<FeeTierDistributionQueryType>(environment, query, {
token0: token0.toLowerCase(),
token1: token1.toLowerCase(),
}).subscribe({
next: setData,
error: setError,
complete: () => setIsLoading(false),
})
}
}, [token0, token1, chainId, environment])

// Trigger fetch on first load
useEffect(refreshData, [refreshData, token0, token1])

useInterval(refreshData, interval, true)
return { error, isLoading, data }
}
9 changes: 9 additions & 0 deletions src/graphql/RelayEnvironment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Environment, Network, RecordSource, Store } from 'relay-runtime'

import fetchGraphQL from './fetchGraphQL'

// Export a singleton instance of Relay Environment configured with our network function:
export default new Environment({
network: Network.create(fetchGraphQL),
store: new Store(new RecordSource()),
})
53 changes: 53 additions & 0 deletions src/graphql/fetchGraphQL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* Helpful Resources
* https://github.com/sibelius/create-react-app-relay-modern/blob/master/src/relay/fetchQuery.js
* https://github.com/relay-tools/relay-compiler-language-typescript/blob/master/example/ts/app.tsx
*/

import { SupportedChainId } from 'constants/chains'
import { Variables } from 'react-relay'
import { GraphQLResponse, ObservableFromValue, RequestParameters } from 'relay-runtime'

import store, { AppState } from '../state/index'

const CHAIN_SUBGRAPH_URL: Record<number, string> = {
[SupportedChainId.MAINNET]: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3',
[SupportedChainId.RINKEBY]: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3',

[SupportedChainId.ARBITRUM_ONE]: 'https://api.thegraph.com/subgraphs/name/ianlapham/arbitrum-minimal',

[SupportedChainId.OPTIMISM]: 'https://api.thegraph.com/subgraphs/name/ianlapham/optimism-post-regenesis',

[SupportedChainId.POLYGON]: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswap-v3-polygon',

[SupportedChainId.CELO]: 'https://api.thegraph.com/subgraphs/name/jesse-sawa/uniswap-celo',
}

const headers = {
Accept: 'application/json',
'Content-type': 'application/json',
}

// Define a function that fetches the results of a request (query/mutation/etc)
// and returns its results as a Promise:
const fetchQuery = (params: RequestParameters, variables: Variables): ObservableFromValue<GraphQLResponse> => {
const chainId = (store.getState() as AppState).application.chainId

const subgraphUrl =
chainId && CHAIN_SUBGRAPH_URL[chainId] ? CHAIN_SUBGRAPH_URL[chainId] : CHAIN_SUBGRAPH_URL[SupportedChainId.MAINNET]

const body = JSON.stringify({
query: params.text, // GraphQL text from input
variables,
})

const response = fetch(subgraphUrl, {
method: 'POST',
headers,
body,
}).then((res) => res.json())

return response
}

export default fetchQuery
Loading

0 comments on commit 91f4892

Please sign in to comment.