Compare commits

...

16 Commits

Author SHA1 Message Date
Crowdin Bot
361e0ca8d7 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-01 10:04:56 +00:00
Justin Domingue
cc0a757ddd fix: run graphql:codegen on yarn build (#1933)
* run graphql codegen on build

* yarn.lock

* remove ms.macro
2021-06-29 17:22:41 -07:00
Justin Domingue
31632d3c5b ignore state/data/generated.ts 2021-06-29 16:27:12 -07:00
Justin Domingue
9cfbec4b8b generated graphql schema and en-US.po on start (#1930) 2021-06-29 12:03:47 -07:00
Crowdin Bot
b5ac5d882f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-29 09:04:42 +00:00
Crowdin Bot
130606cab4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-29 08:04:53 +00:00
Moody Salem
fa56919a06 fix(theme): always show the gradient background color 2021-06-28 19:47:22 -05:00
Moody Salem
633f65676e fix(theme): fix the missing background radial gradient
fixes https://github.com/Uniswap/uniswap-interface/issues/1925
2021-06-28 19:35:41 -05:00
Moody Salem
9bccb7ae3a fix(multicall): better use the block number returned from calls, and cancel all calls when a new block comes in 2021-06-28 19:26:47 -05:00
Moody Salem
8a2e4123c6 chore(yarn): update yarn.lock 2021-06-28 18:57:10 -05:00
Moody Salem
e93ddbedfa chore(ci): take a screenshot in the cypress test for an example 2021-06-28 18:55:42 -05:00
Justin Domingue
1667b56a04 feat(thegraph): auto-generate graphql types (#1926)
* auto-generate graphql types

* remove introspection

* generated .ts and add to prettierignore

* updated graph url
2021-06-28 16:43:24 -07:00
Justin Domingue
6e995d6c09 feat(pools): integrate with The Graph using RTK Query (#1924)
* integrate with the graph

* Update src/state/data/slice.ts

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-06-28 15:12:58 -07:00
Moody Salem
f096112716 fix: improve how we use blocks per fetch in the memoization of the listeners (#1920)
fixes https://github.com/Uniswap/uniswap-interface/issues/1877
2021-06-28 16:39:19 -05:00
Jordan Frankfurt
50c7d36164 replace trustwallet assets repo w/ our fork (#1922) 2021-06-28 16:35:31 -04:00
Moody Salem
8a7f1d51ce chore(ci): record integration tests 2021-06-28 15:12:24 -05:00
26 changed files with 2112 additions and 159 deletions

View File

@@ -46,5 +46,6 @@ jobs:
- run: yarn integration-test
env:
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

3
.gitignore vendored
View File

@@ -7,6 +7,7 @@
/src/locales/**/*.ts
/src/locales/**/*.json
/src/locales/**/en-US.po
/src/state/data/generated.ts
# dependencies
/node_modules
@@ -39,4 +40,4 @@ package-lock.json
cypress/videos
cypress/screenshots
cypress/fixtures/example.json
cypress/fixtures/example.json

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
/src/state/data/generated.ts

10
codegen.yml Normal file
View File

@@ -0,0 +1,10 @@
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
documents: 'src/**/!(*.d).{ts,tsx}'
generates:
./src/state/data/generated.ts:
plugins:
- typescript
- typescript-operations
- typescript-rtk-query:
importBaseApiFrom: './slice'
exportHooks: true

View File

@@ -1,4 +1,5 @@
{
"projectId": "yp82ef",
"baseUrl": "http://localhost:3000",
"pluginsFile": false,
"fixturesFolder": false,

View File

@@ -4,6 +4,7 @@ describe('Landing Page', () => {
beforeEach(() => cy.visit('/'))
it('loads swap page', () => {
cy.get('#swap-page')
cy.screenshot()
})
it('redirects to url /swap', () => {

View File

@@ -5,6 +5,10 @@
"private": true,
"devDependencies": {
"@ethersproject/experimental": "^5.2.0",
"@graphql-codegen/cli": "1.21.5",
"@graphql-codegen/typescript": "1.22.3",
"@graphql-codegen/typescript-operations": "^1.18.2",
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
"@lingui/cli": "^3.9.0",
"@lingui/loader": "^3.9.0",
"@lingui/macro": "^3.9.0",
@@ -14,7 +18,8 @@
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.3.5",
"@reduxjs/toolkit": "^1.6.0",
"@rtk-query/graphql-request-base-query": "^1.0.3",
"@typechain/ethers-v5": "^7.0.0",
"@types/jest": "^25.2.1",
"@types/lingui__core": "^2.7.1",
@@ -66,6 +71,8 @@
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.0.0",
"ethers": "^5.2.0",
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"inter-ui": "^3.13.1",
"lightweight-charts": "^3.3.0",
"lodash.flatmap": "^4.5.0",
@@ -92,6 +99,7 @@
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"rebass": "^4.0.7",
"redux-devtools-extension": "^2.13.9",
"redux-localstorage-simple": "^2.3.1",
"serve": "^11.3.2",
"start-server-and-test": "^1.11.0",
@@ -115,13 +123,15 @@
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
"compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"build": "yarn compile-contract-types && yarn i18n:extract && yarn i18n:compile && react-scripts build",
"build": "yarn compile-contract-types && yarn graphql:generate && yarn i18n:extract && yarn i18n:compile && react-scripts build",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "lingui compile",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
"graphql:generate": "graphql-codegen --config codegen.yml",
"postinstall": "yarn compile-contract-types",
"start": "yarn compile-contract-types && react-scripts start",
"test": "react-scripts test --env=jsdom"
"test": "react-scripts test --env=jsdom",
"prestart": "yarn graphql:generate && touch src/locales/en-US.po"
},
"eslintConfig": {
"extends": "react-app",

View File

@@ -63,7 +63,6 @@
pointer-events: none;
width: 200vw;
height: 200vh;
mix-blend-mode: color;
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
transform: translate(-50vw, -100vh);
z-index: -1;

View File

@@ -7,7 +7,7 @@ import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import Logo from '../Logo'
export const getTokenLogoURL = (address: string) =>
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
`https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png`
const StyledEthereumLogo = styled.img<{ size: string }>`
width: ${({ size }) => size};

View File

@@ -10,7 +10,7 @@ async function getColorFromToken(token: Token): Promise<string | null> {
return Promise.resolve('#FAAB14')
}
const path = `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${token.address}/logo.png`
const path = `https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${token.address}/logo.png`
return Vibrant.from(path)
.getPalette()

View File

@@ -19,6 +19,7 @@ import MulticallUpdater from './state/multicall/updater'
import TransactionUpdater from './state/transactions/updater'
import UserUpdater from './state/user/updater'
import ThemeProvider, { ThemedGlobalStyle } from './theme'
import RadialGradientByChainUpdater from './theme/RadialGradientByChainUpdater'
import getLibrary from './utils/getLibrary'
const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
@@ -50,6 +51,7 @@ if (typeof GOOGLE_ANALYTICS_ID === 'string') {
function Updaters() {
return (
<>
<RadialGradientByChainUpdater />
<ListsUpdater />
<UserUpdater />
<ApplicationUpdater />

View File

@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-File-ID: 4\n"
"Project-Id-Version: uniswap-interface\n"
"Language-Team: German\n"
"PO-Revision-Date: 2021-06-27 05:04\n"
"PO-Revision-Date: 2021-06-29 09:04\n"
#: src/pages/Pool/PositionPage.tsx
#: src/pages/Pool/PositionPage.tsx

View File

@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-File-ID: 4\n"
"Project-Id-Version: uniswap-interface\n"
"Language-Team: Japanese\n"
"PO-Revision-Date: 2021-06-27 05:04\n"
"PO-Revision-Date: 2021-07-01 10:04\n"
#: src/pages/Pool/PositionPage.tsx
#: src/pages/Pool/PositionPage.tsx
@@ -272,11 +272,11 @@ msgstr "{0} を承認中"
#: src/components/Header/NetworkCard.tsx
msgid "Arbitrum Explorer"
msgstr "アービトラムエクスプローラー"
msgstr "Arbitrumエクスプローラー"
#: src/components/Header/NetworkCard.tsx
msgid "Arbitrum Token Bridge"
msgstr "アービトラムトークンブリッジ"
msgstr "Arbitrumトークンブリッジ"
#: src/components/Settings/index.tsx
msgid "Are you sure?"
@@ -1615,7 +1615,7 @@ msgstr "Uniswap移行コントラクト↗"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "Uniswap on <0>Arbitrum</0>"
msgstr "<0>アービトラムでのユニスワップ</0>"
msgstr "<0>アービトラム</0>でのユニスワップ"
#: src/components/SearchModal/ImportToken.tsx
msgid "Unknown Source"

View File

@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-File-ID: 4\n"
"Project-Id-Version: uniswap-interface\n"
"Language-Team: Chinese Simplified\n"
"PO-Revision-Date: 2021-06-28 16:04\n"
"PO-Revision-Date: 2021-07-01 10:04\n"
#: src/pages/Pool/PositionPage.tsx
#: src/pages/Pool/PositionPage.tsx
@@ -37,7 +37,7 @@ msgstr "(最大值)"
#: src/components/AddressInputPanel/index.tsx
msgid "(View on Explorer)"
msgstr "(在浏览器查看"
msgstr "(在以太坊资源浏览器查看)"
#: src/components/Header/UniBalanceContent.tsx
msgid "(claim)"
@@ -328,7 +328,7 @@ msgstr "已屏蔽地址"
#: src/components/PositionCard/index.tsx
msgid "By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity."
msgstr "通过注入流动资金,您将按您在流动池中所占份额赚取对应交易量的0.3%手续费。手续费将实时累计,添加到流动池中,您可以通过赎回您的流动资金将手续费也赎回。"
msgstr "通过添加流动性,您将按您在流动池中所占比例赚取对应交易量的0.3%手续费。手续费将实时累加到流动池中,在您取回流动性时也将同时取回您赚取的手续费。"
#: src/components/SearchModal/ImportList.tsx
msgid "By adding this list you are implicitly trusting that the data is correct. Anyone can create a list, including creating fake versions of existing lists and lists that claim to represent projects that do not have one."
@@ -463,7 +463,7 @@ msgstr "收取手续费的操作将为您提取您当前已累积的手续费。
#: src/components/SearchModal/CommonBases.tsx
msgid "Common bases"
msgstr "共同基础"
msgstr "常用代币"
#: src/pages/RemoveLiquidity/index.tsx
msgid "Confirm"
@@ -615,7 +615,7 @@ msgstr "充入流动资金"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "Deposit to Arbitrum"
msgstr "存款到 Arbitrum"
msgstr "充值到 Arbitrum"
#: src/pages/Earn/index.tsx
msgid "Deposit your Liquidity Provider tokens to receive UNI, the Uniswap protocol governance token."
@@ -1231,7 +1231,7 @@ msgstr "最近的交易"
#: src/components/AddressInputPanel/index.tsx
msgid "Recipient"
msgstr "接受者"
msgstr "接收方"
#: src/components/PositionCard/V2.tsx
#: src/components/PositionCard/index.tsx
@@ -1462,7 +1462,7 @@ msgstr "这些代币通常与其他代币配对。"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "This is an alpha release of Uniswap on the Arbitrum network. You must bridge L1 assets to the network to swap them."
msgstr "这是 Arbitrum 网络上 Uniswap 的 alpha 版本。您需要先将 L1 资产桥接到 Arbitrum 网络才能使用。"
msgstr "这是 Arbitrum 网络上 Uniswap 的 alpha 版本。您必须桥接 L1 资产才能进行兑换。"
#: src/components/SearchModal/ImportToken.tsx
msgid "This token doesn't appear on the active token list(s). Make sure this is the token that you want to trade."
@@ -1574,7 +1574,7 @@ msgstr "UNI {0}/{1} 被摧毁"
#: src/pages/Earn/Manage.tsx
msgid "UNI-V2 LP tokens are required. Once you've added liquidity to the {0}-{1} pool you can stake your liquidity tokens on this page."
msgstr "需要 UNI-V2 流动池代币。一旦您已注入流动资金到 {0}-{1} 池,您就可以在此页面上抵押您的流动代币。"
msgstr "需要 UNI-V2 LP 代币。在您注入流动到 {0}-{1} 池,您就可以在此页面上抵押您的流动代币。"
#: src/pages/Earn/Manage.tsx
msgid "UNI-V2 {0}-{1}"
@@ -1615,7 +1615,7 @@ msgstr "Uniswap 迁移合约↗"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "Uniswap on <0>Arbitrum</0>"
msgstr "Uniswap on <0> Arbitrum</0>"
msgstr "<0> Arbitrum</0> 网络上的 Uniswap"
#: src/components/SearchModal/ImportToken.tsx
msgid "Unknown Source"
@@ -1746,7 +1746,7 @@ msgstr "等待确认"
#: src/components/AddressInputPanel/index.tsx
msgid "Wallet Address or ENS name"
msgstr "钱包地址或 ENS 名"
msgstr "钱包地址或 ENS 名"
#: src/components/earn/StakingModal.tsx
msgid "Weekly Rewards"

View File

@@ -14,7 +14,7 @@ msgstr ""
"X-Crowdin-File-ID: 4\n"
"Project-Id-Version: uniswap-interface\n"
"Language-Team: Chinese Traditional\n"
"PO-Revision-Date: 2021-06-27 05:04\n"
"PO-Revision-Date: 2021-07-01 10:04\n"
#: src/pages/Pool/PositionPage.tsx
#: src/pages/Pool/PositionPage.tsx
@@ -37,7 +37,7 @@ msgstr "(最大值)"
#: src/components/AddressInputPanel/index.tsx
msgid "(View on Explorer)"
msgstr "(在資源管理器上查看"
msgstr "(在以太坊資源瀏覽器上查看)"
#: src/components/Header/UniBalanceContent.tsx
msgid "(claim)"
@@ -272,11 +272,11 @@ msgstr "批準 {0}"
#: src/components/Header/NetworkCard.tsx
msgid "Arbitrum Explorer"
msgstr "套利瀏覽器"
msgstr "Arbitrum 瀏覽器"
#: src/components/Header/NetworkCard.tsx
msgid "Arbitrum Token Bridge"
msgstr "套利代幣橋"
msgstr "Arbitrum 代幣橋"
#: src/components/Settings/index.tsx
msgid "Are you sure?"
@@ -328,7 +328,7 @@ msgstr "已屏蔽地址"
#: src/components/PositionCard/index.tsx
msgid "By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity."
msgstr "通過註入流動資金,您將按您在流動池中所占份額賺取對應交易量的0.3%手續費。手續費將實時累計,添加到流動池中,您可以通過贖回您的流動資金將手續費也贖回。"
msgstr "通過添加流動性,您將按您在流動池中所佔比例賺取對應交易量的0.3%手續費。手續費將實時累加到流動池中,在您取回流動性時也將同時取回您賺取的手續費。"
#: src/components/SearchModal/ImportList.tsx
msgid "By adding this list you are implicitly trusting that the data is correct. Anyone can create a list, including creating fake versions of existing lists and lists that claim to represent projects that do not have one."
@@ -463,7 +463,7 @@ msgstr "收取手續費的操作將為您提取您當前已累積的手續費。
#: src/components/SearchModal/CommonBases.tsx
msgid "Common bases"
msgstr "共同基礎"
msgstr "常用代幣"
#: src/pages/RemoveLiquidity/index.tsx
msgid "Confirm"
@@ -615,7 +615,7 @@ msgstr "存入流動資金"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "Deposit to Arbitrum"
msgstr "存款到 Arbitrum"
msgstr "充值到 Arbitrum"
#: src/pages/Earn/index.tsx
msgid "Deposit your Liquidity Provider tokens to receive UNI, the Uniswap protocol governance token."
@@ -874,7 +874,7 @@ msgstr "瞭解"
#: src/components/Header/NetworkCard.tsx
msgid "Learn more"
msgstr "學到更多"
msgstr "瞭解更多"
#: src/components/claim/ClaimModal.tsx
#: src/pages/Pool/PositionPage.tsx
@@ -1231,7 +1231,7 @@ msgstr "最近交易"
#: src/components/AddressInputPanel/index.tsx
msgid "Recipient"
msgstr "接受者"
msgstr "接收方"
#: src/components/PositionCard/V2.tsx
#: src/components/PositionCard/index.tsx
@@ -1462,7 +1462,7 @@ msgstr "這些代幣通常與其他代幣配對。"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "This is an alpha release of Uniswap on the Arbitrum network. You must bridge L1 assets to the network to swap them."
msgstr "這是 Arbitrum 網絡上 Uniswap 的 alpha 版本。您必須 L1 資產橋接到網絡才能交換它們。"
msgstr "這是 Arbitrum 網絡上 Uniswap 的 alpha 版本。您必須橋接 L1 資產才能進行兌換。"
#: src/components/SearchModal/ImportToken.tsx
msgid "This token doesn't appear on the active token list(s). Make sure this is the token that you want to trade."
@@ -1574,7 +1574,7 @@ msgstr "UNI {0}/{1} 已銷毀"
#: src/pages/Earn/Manage.tsx
msgid "UNI-V2 LP tokens are required. Once you've added liquidity to the {0}-{1} pool you can stake your liquidity tokens on this page."
msgstr "需要 UNI-V2 流動池代幣。一旦您已註入流動資金到 {0}-{1} 池,您就可以在此頁面上抵押您的流動代幣。"
msgstr "需要 UNI-V2 LP 代幣。在您注入流動到 {0}-{1} 池,您就可以在此頁面上抵押您的流動代幣。"
#: src/pages/Earn/Manage.tsx
msgid "UNI-V2 {0}-{1}"
@@ -1615,7 +1615,7 @@ msgstr "Uniswap 遷移合約↗"
#: src/components/swap/SwapNetworkAlert.tsx
msgid "Uniswap on <0>Arbitrum</0>"
msgstr "Uniswap on <0> Arbitrum</0>"
msgstr "<0> Arbitrum</0> 網絡上的 Uniswap"
#: src/components/SearchModal/ImportToken.tsx
msgid "Unknown Source"
@@ -1746,7 +1746,7 @@ msgstr "等待確認中"
#: src/components/AddressInputPanel/index.tsx
msgid "Wallet Address or ENS name"
msgstr "錢包地址或 ENS 名"
msgstr "錢包地址或 ENS 名"
#: src/components/earn/StakingModal.tsx
msgid "Weekly Rewards"

View File

@@ -1,10 +1,9 @@
import { useCallback, useEffect, useState } from 'react'
import { useActiveWeb3React } from '../../hooks/web3'
import { useAppDispatch } from 'state/hooks'
import useDebounce from '../../hooks/useDebounce'
import useIsWindowVisible from '../../hooks/useIsWindowVisible'
import { useActiveWeb3React } from '../../hooks/web3'
import { updateBlockNumber } from './actions'
import { useAppDispatch } from 'state/hooks'
import { SupportedChainId } from 'constants/chains'
export default function Updater(): null {
const { library, chainId } = useActiveWeb3React()
@@ -54,25 +53,5 @@ export default function Updater(): null {
dispatch(updateBlockNumber({ chainId: debouncedState.chainId, blockNumber: debouncedState.blockNumber }))
}, [windowVisible, dispatch, debouncedState.blockNumber, debouncedState.chainId])
// manage background color
const background = document.getElementById('background-radial-gradient')
useEffect(() => {
if (!background) {
return
}
let gradient
switch (chainId) {
case SupportedChainId.ARBITRUM_ONE:
gradient =
'radial-gradient(96.19% 96.19% at 50% -5.43%, hsla(204, 87%, 55%, 0.2) 0%, hsla(227, 0%, 0%, 0) 100%)'
break
default:
gradient = 'radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%)'
}
background.style.background = gradient
}, [background, chainId])
return null
}

110
src/state/data/slice.ts Normal file
View File

@@ -0,0 +1,110 @@
import { createApi } from '@reduxjs/toolkit/query/react'
import { gql, GraphQLClient } from 'graphql-request'
import { FeeAmount } from '@uniswap/v3-sdk'
import { reduce } from 'lodash'
import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query'
import { FeeTierDistribution, PoolTVL } from './types'
export const UNISWAP_V3_GRAPH_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
export const client = new GraphQLClient(UNISWAP_V3_GRAPH_URL)
export const api = createApi({
reducerPath: 'dataApi',
baseQuery: graphqlRequestBaseQuery({ client }),
endpoints: (builder) => ({
getFeeTierDistribution: builder.query<FeeTierDistribution, { token0: string; token1: string }>({
query: ({ token0, token1 }) => ({
document: gql`
query pools($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
}
}
`,
variables: {
token0,
token1,
},
}),
transformResponse: (poolTvl: PoolTVL) => {
const all = poolTvl.asToken0.concat(poolTvl.asToken1)
// sum tvl for token0 and token1 by fee tier
const tvlByFeeTer = all.reduce<{ [feeAmount: number]: [number | undefined, number | undefined] }>(
(acc, value) => {
acc[value.feeTier][0] = (acc[value.feeTier][0] ?? 0) + Number(value.totalValueLockedToken0)
acc[value.feeTier][1] = (acc[value.feeTier][1] ?? 0) + Number(value.totalValueLockedToken1)
return acc
},
{
[FeeAmount.LOW]: [undefined, undefined],
[FeeAmount.MEDIUM]: [undefined, undefined],
[FeeAmount.HIGH]: [undefined, undefined],
}
)
// sum total tvl for token0 and token1
const [sumToken0Tvl, sumToken1Tvl] = reduce(
tvlByFeeTer,
(acc: [number, number], value) => {
acc[0] += value[0] ?? 0
acc[1] += value[1] ?? 0
return acc
},
[0, 0]
)
// returns undefined if both tvl0 and tvl1 are undefined (pool not created)
const mean = (tvl0: number | undefined, sumTvl0: number, tvl1: number | undefined, sumTvl1: number) =>
tvl0 === undefined && tvl1 === undefined ? undefined : ((tvl0 ?? 0) + (tvl1 ?? 0)) / (sumTvl0 + sumTvl1) || 0
return {
block: poolTvl._meta.block.number,
distributions: {
[FeeAmount.LOW]: mean(
tvlByFeeTer[FeeAmount.LOW][0],
sumToken0Tvl,
tvlByFeeTer[FeeAmount.LOW][1],
sumToken1Tvl
),
[FeeAmount.MEDIUM]: mean(
tvlByFeeTer[FeeAmount.MEDIUM][0],
sumToken0Tvl,
tvlByFeeTer[FeeAmount.MEDIUM][1],
sumToken1Tvl
),
[FeeAmount.HIGH]: mean(
tvlByFeeTer[FeeAmount.HIGH][0],
sumToken0Tvl,
tvlByFeeTer[FeeAmount.HIGH][1],
sumToken1Tvl
),
},
}
},
}),
}),
})
export const { useGetFeeTierDistributionQuery } = api

28
src/state/data/types.ts Normal file
View File

@@ -0,0 +1,28 @@
import { FeeAmount } from '@uniswap/v3-sdk'
export interface PoolTVL {
_meta: {
block: {
number: number
}
}
asToken0: {
feeTier: FeeAmount
totalValueLockedToken0: number
totalValueLockedToken1: number
}[]
asToken1: {
feeTier: FeeAmount
totalValueLockedToken0: number
totalValueLockedToken1: number
}[]
}
export interface FeeTierDistribution {
block: number
distributions: {
[FeeAmount.LOW]: number | undefined
[FeeAmount.MEDIUM]: number | undefined
[FeeAmount.HIGH]: number | undefined
}
}

View File

@@ -1,4 +1,4 @@
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import { configureStore } from '@reduxjs/toolkit'
import { save, load } from 'redux-localstorage-simple'
import application from './application/reducer'
@@ -12,6 +12,7 @@ import lists from './lists/reducer'
import burn from './burn/reducer'
import burnV3 from './burn/v3/reducer'
import multicall from './multicall/reducer'
import { api } from './data/slice'
const PERSISTED_KEYS: string[] = ['user', 'transactions', 'lists']
@@ -27,8 +28,12 @@ const store = configureStore({
burnV3,
multicall,
lists,
[api.reducerPath]: api.reducer,
},
middleware: [...getDefaultMiddleware({ thunk: false }), save({ states: PERSISTED_KEYS, debounce: 1000 })],
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ thunk: true })
.concat(api.middleware)
.concat(save({ states: PERSISTED_KEYS, debounce: 1000 })),
preloadedState: load({ states: PERSISTED_KEYS }),
})

View File

@@ -39,13 +39,13 @@ export function parseCallKey(callKey: string): Call {
export interface ListenerOptions {
// how often this data should be fetched, by default 1
readonly blocksPerFetch?: number
readonly blocksPerFetch: number
}
export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
export const addMulticallListeners = createAction<{ chainId: number; calls: Call[]; options: ListenerOptions }>(
'multicall/addMulticallListeners'
)
export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options?: ListenerOptions }>(
export const removeMulticallListeners = createAction<{ chainId: number; calls: Call[]; options: ListenerOptions }>(
'multicall/removeMulticallListeners'
)
export const fetchingMulticallResults = createAction<{ chainId: number; calls: Call[]; fetchingBlockNumber: number }>(

View File

@@ -48,7 +48,10 @@ export const NEVER_RELOAD: ListenerOptions = {
}
// the lowest level call for subscribing to contract data
function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): CallResult[] {
function useCallsData(
calls: (Call | undefined)[],
{ blocksPerFetch }: ListenerOptions = { blocksPerFetch: 1 }
): CallResult[] {
const { chainId } = useActiveWeb3React()
const callResults = useAppSelector((state) => state.multicall.callResults)
const dispatch = useAppDispatch()
@@ -73,7 +76,7 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C
addMulticallListeners({
chainId,
calls,
options,
options: { blocksPerFetch },
})
)
@@ -82,11 +85,11 @@ function useCallsData(calls: (Call | undefined)[], options?: ListenerOptions): C
removeMulticallListeners({
chainId,
calls,
options,
options: { blocksPerFetch },
})
)
}
}, [chainId, dispatch, options, serializedCallKeys])
}, [chainId, dispatch, blocksPerFetch, serializedCallKeys])
return useMemo(
() =>

View File

@@ -32,6 +32,7 @@ describe('multicall reducer', () => {
callData: '0x',
},
],
options: { blocksPerFetch: 1 },
})
)
expect(store.getState()).toEqual({
@@ -58,6 +59,7 @@ describe('multicall reducer', () => {
},
],
chainId: 1,
options: { blocksPerFetch: 1 },
})
)
expect(store.getState()).toEqual({ callResults: {}, callListeners: {} })
@@ -72,6 +74,7 @@ describe('multicall reducer', () => {
callData: '0x',
},
],
options: { blocksPerFetch: 1 },
})
)
store.dispatch(
@@ -83,6 +86,7 @@ describe('multicall reducer', () => {
},
],
chainId: 1,
options: { blocksPerFetch: 1 },
})
)
expect(store.getState()).toEqual({

View File

@@ -37,20 +37,41 @@ const initialState: MulticallState = {
export default createReducer(initialState, (builder) =>
builder
.addCase(addMulticallListeners, (state, { payload: { calls, chainId, options: { blocksPerFetch = 1 } = {} } }) => {
const listeners: MulticallState['callListeners'] = state.callListeners
? state.callListeners
: (state.callListeners = {})
listeners[chainId] = listeners[chainId] ?? {}
calls.forEach((call) => {
const callKey = toCallKey(call)
listeners[chainId][callKey] = listeners[chainId][callKey] ?? {}
listeners[chainId][callKey][blocksPerFetch] = (listeners[chainId][callKey][blocksPerFetch] ?? 0) + 1
})
})
.addCase(
addMulticallListeners,
(
state,
{
payload: {
calls,
chainId,
options: { blocksPerFetch },
},
}
) => {
const listeners: MulticallState['callListeners'] = state.callListeners
? state.callListeners
: (state.callListeners = {})
listeners[chainId] = listeners[chainId] ?? {}
calls.forEach((call) => {
const callKey = toCallKey(call)
listeners[chainId][callKey] = listeners[chainId][callKey] ?? {}
listeners[chainId][callKey][blocksPerFetch] = (listeners[chainId][callKey][blocksPerFetch] ?? 0) + 1
})
}
)
.addCase(
removeMulticallListeners,
(state, { payload: { chainId, calls, options: { blocksPerFetch = 1 } = {} } }) => {
(
state,
{
payload: {
chainId,
calls,
options: { blocksPerFetch },
},
}
) => {
const listeners: MulticallState['callListeners'] = state.callListeners
? state.callListeners
: (state.callListeners = {})

View File

@@ -5,6 +5,7 @@ import { useMulticall2Contract } from '../../hooks/useContract'
import useDebounce from '../../hooks/useDebounce'
import chunkArray from '../../utils/chunkArray'
import { retry, RetryableError } from '../../utils/retry'
import { updateBlockNumber } from '../application/actions'
import { useBlockNumber } from '../application/hooks'
import { AppState } from '../index'
import {
@@ -149,8 +150,8 @@ export default function Updater(): null {
const chunkedCalls = chunkArray(calls)
if (cancellations.current?.blockNumber !== latestBlockNumber) {
cancellations.current?.cancellations?.forEach((c) => c())
if (cancellations.current && cancellations.current.blockNumber !== latestBlockNumber) {
cancellations.current.cancellations.forEach((c) => c())
}
dispatch(
@@ -171,8 +172,6 @@ export default function Updater(): null {
})
promise
.then(({ results: returnData, blockNumber: fetchBlockNumber }) => {
cancellations.current = { cancellations: [], blockNumber: latestBlockNumber }
// accumulates the length of all previous indices
const firstCallKeyIndex = chunkedCalls.slice(0, index).reduce<number>((memo, curr) => memo + curr.length, 0)
const lastCallKeyIndex = firstCallKeyIndex + returnData.length
@@ -216,10 +215,14 @@ export default function Updater(): null {
})
)
}
if (fetchBlockNumber > latestBlockNumber) {
dispatch(updateBlockNumber({ chainId, blockNumber: fetchBlockNumber }))
}
})
.catch((error: any) => {
if (error.isCancelledError) {
console.debug('Cancelled fetch for blockNumber', latestBlockNumber)
console.debug('Cancelled fetch for blockNumber', latestBlockNumber, chunk, chainId)
return
}
console.error('Failed to fetch multicall chunk', chunk, chainId, error)

View File

@@ -0,0 +1,22 @@
import { useEffect } from 'react'
import { SupportedChainId } from '../constants/chains'
import { useActiveWeb3React } from '../hooks/web3'
const backgroundRadialGradientElement = document.getElementById('background-radial-gradient')
export default function RadialGradientByChainUpdater(): null {
const { chainId } = useActiveWeb3React()
// manage background color
useEffect(() => {
if (!backgroundRadialGradientElement) {
return
}
if (chainId === SupportedChainId.ARBITRUM_ONE) {
backgroundRadialGradientElement.style.background =
'radial-gradient(96.19% 96.19% at 50% -5.43%, hsla(204, 87%, 55%, 0.2) 0%, hsla(227, 0%, 0%, 0) 100%)'
} else {
backgroundRadialGradientElement.style.background = ''
}
}, [chainId])
return null
}

1900
yarn.lock

File diff suppressed because it is too large Load Diff