Commit 496408b3 authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: token list fetching performance (#3480)

* fix: token list fetching

* fix: simplify naming
parent 78b6f5c7
import { NativeCurrency, Token } from '@uniswap/sdk-core' import { NativeCurrency, Token } from '@uniswap/sdk-core'
import { TokenInfo, TokenList } from '@uniswap/token-lists' import { TokenInfo, TokenList } from '@uniswap/token-lists'
import { atom } from 'jotai' import { atom, useAtom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import resolveENSContentHash from 'lib/utils/resolveENSContentHash' import resolveENSContentHash from 'lib/utils/resolveENSContentHash'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
...@@ -14,31 +14,35 @@ import { validateTokens } from './validateTokenList' ...@@ -14,31 +14,35 @@ import { validateTokens } from './validateTokenList'
export const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' export const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org'
const chainTokenMapAtom = atom<ChainTokenMap | undefined>(undefined) const chainTokenMapAtom = atom<ChainTokenMap | null>(null)
export function useIsTokenListLoaded() { export function useIsTokenListLoaded() {
return Boolean(useAtomValue(chainTokenMapAtom)) return Boolean(useAtomValue(chainTokenMapAtom))
} }
export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST): void { export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST): void {
const { chainId, library } = useActiveWeb3React()
const setChainTokenMap = useUpdateAtom(chainTokenMapAtom)
// Error boundaries will not catch (non-rendering) async errors, but it should still be shown // Error boundaries will not catch (non-rendering) async errors, but it should still be shown
const [error, setError] = useState<Error>() const [error, setError] = useState<Error>()
if (error) throw error if (error) throw error
const [chainTokenMap, setChainTokenMap] = useAtom(chainTokenMapAtom)
useEffect(() => setChainTokenMap(null), [list, setChainTokenMap])
const { chainId, library } = useActiveWeb3React()
const resolver = useCallback( const resolver = useCallback(
(ensName: string) => { (ensName: string) => {
if (library && chainId === 1) { if (library && chainId === 1) {
// TODO(zzmp): Use network resolver when wallet is not on chainId === 1.
return resolveENSContentHash(ensName, library) return resolveENSContentHash(ensName, library)
} }
throw new Error('Could not construct mainnet ENS resolver') throw new Error('Could not construct mainnet ENS resolver')
}, },
[chainId, library] [chainId, library]
) )
useEffect(() => { useEffect(() => {
// If the list was already loaded, don't reload it.
if (chainTokenMap) return
let stale = false let stale = false
activateList(list) activateList(list)
return () => { return () => {
...@@ -53,19 +57,20 @@ export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST ...@@ -53,19 +57,20 @@ export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST
} else { } else {
tokens = await validateTokens(list) tokens = await validateTokens(list)
} }
const tokenMap = tokensToChainTokenMap(tokens) // also caches the fetched tokens, so it is invoked even if stale // tokensToChainTokenMap also caches the fetched tokens, so it must be invoked even if stale.
const map = tokensToChainTokenMap(tokens)
if (!stale) { if (!stale) {
setChainTokenMap(tokenMap) setChainTokenMap(map)
setError(undefined) setError(undefined)
} }
} catch (e: unknown) { } catch (e: unknown) {
if (!stale) { if (!stale) {
setChainTokenMap(undefined) // Do not update the token map, in case the map was already resolved without error on mainnet.
setError(e as Error) setError(e as Error)
} }
} }
} }
}, [list, resolver, setChainTokenMap]) }, [chainTokenMap, list, resolver, setChainTokenMap])
} }
export default function useTokenList(): WrappedTokenInfo[] { export default function useTokenList(): WrappedTokenInfo[] {
......
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