Commit 6294915b authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: convert token list to context (#3712)

* fix: convert token list to context

* fix: cosmos
parent 984c742d
import { tokens } from '@uniswap/default-token-list'
import { DAI, USDC_MAINNET } from 'constants/tokens' import { DAI, USDC_MAINNET } from 'constants/tokens'
import { useUpdateAtom } from 'jotai/utils' import { useUpdateAtom } from 'jotai/utils'
import { useEffect } from 'react' import { useEffect } from 'react'
...@@ -65,7 +64,6 @@ function Fixture() { ...@@ -65,7 +64,6 @@ function Fixture() {
defaultInputAmount={defaultInputAmount} defaultInputAmount={defaultInputAmount}
defaultOutputTokenAddress={optionsToAddressMap[defaultOutputToken]} defaultOutputTokenAddress={optionsToAddressMap[defaultOutputToken]}
defaultOutputAmount={defaultOutputAmount} defaultOutputAmount={defaultOutputAmount}
tokenList={tokens}
onConnectWallet={() => console.log('onConnectWallet')} // this handler is included as a test of functionality, but only logs onConnectWallet={() => console.log('onConnectWallet')} // this handler is included as a test of functionality, but only logs
/> />
) )
......
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { TokenInfo } from '@uniswap/token-lists'
import { useAtom } from 'jotai' import { useAtom } from 'jotai'
import { SwapInfoProvider } from 'lib/hooks/swap/useSwapInfo' import { SwapInfoProvider } from 'lib/hooks/swap/useSwapInfo'
import useSyncConvenienceFee, { FeeOptions } from 'lib/hooks/swap/useSyncConvenienceFee' import useSyncConvenienceFee, { FeeOptions } from 'lib/hooks/swap/useSyncConvenienceFee'
...@@ -8,7 +7,6 @@ import { usePendingTransactions } from 'lib/hooks/transactions' ...@@ -8,7 +7,6 @@ import { usePendingTransactions } from 'lib/hooks/transactions'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import useHasFocus from 'lib/hooks/useHasFocus' import useHasFocus from 'lib/hooks/useHasFocus'
import useOnSupportedNetwork from 'lib/hooks/useOnSupportedNetwork' import useOnSupportedNetwork from 'lib/hooks/useOnSupportedNetwork'
import { useIsTokenListLoaded, useSyncTokenList } from 'lib/hooks/useTokenList'
import { displayTxHashAtom } from 'lib/state/swap' import { displayTxHashAtom } from 'lib/state/swap'
import { SwapTransactionInfo, Transaction, TransactionType, WrapTransactionInfo } from 'lib/state/transactions' import { SwapTransactionInfo, Transaction, TransactionType, WrapTransactionInfo } from 'lib/state/transactions'
import { useState } from 'react' import { useState } from 'react'
...@@ -43,18 +41,13 @@ function getTransactionFromMap( ...@@ -43,18 +41,13 @@ function getTransactionFromMap(
} }
export interface SwapProps extends TokenDefaults, FeeOptions { export interface SwapProps extends TokenDefaults, FeeOptions {
tokenList?: string | TokenInfo[]
onConnectWallet?: () => void onConnectWallet?: () => void
} }
function Updaters(props: SwapProps) {
useSyncTokenDefaults(props)
useSyncConvenienceFee(props)
return null
}
export default function Swap(props: SwapProps) { export default function Swap(props: SwapProps) {
useValidate(props) useValidate(props)
useSyncConvenienceFee(props)
useSyncTokenDefaults(props)
const { active, account } = useActiveWeb3React() const { active, account } = useActiveWeb3React()
const [wrapper, setWrapper] = useState<HTMLDivElement | null>(null) const [wrapper, setWrapper] = useState<HTMLDivElement | null>(null)
...@@ -66,14 +59,10 @@ export default function Swap(props: SwapProps) { ...@@ -66,14 +59,10 @@ export default function Swap(props: SwapProps) {
const onSupportedNetwork = useOnSupportedNetwork() const onSupportedNetwork = useOnSupportedNetwork()
const isDisabled = !(active && onSupportedNetwork) const isDisabled = !(active && onSupportedNetwork)
useSyncTokenList(props.tokenList)
const isTokenListLoaded = useIsTokenListLoaded()
const focused = useHasFocus(wrapper) const focused = useHasFocus(wrapper)
return ( return (
<> <>
{isTokenListLoaded && <Updaters {...props} />}
<Header title={<Trans>Swap</Trans>}> <Header title={<Trans>Swap</Trans>}>
{active && <Wallet disabled={!account} onClick={props.onConnectWallet} />} {active && <Wallet disabled={!account} onClick={props.onConnectWallet} />}
<Settings disabled={isDisabled} /> <Settings disabled={isDisabled} />
......
import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list' import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
import { useSyncTokenList } from 'lib/hooks/useTokenList' import { TokenListProvider } from 'lib/hooks/useTokenList'
import { Modal } from './Dialog' import { Modal } from './Dialog'
import { TokenSelectDialog } from './TokenSelect' import { TokenSelectDialog } from './TokenSelect'
export default function Fixture() { export default function Fixture() {
useSyncTokenList(DEFAULT_TOKEN_LIST.tokens)
return ( return (
<Modal color="module"> <Modal color="module">
<TokenSelectDialog onSelect={() => void 0} /> <TokenListProvider list={DEFAULT_TOKEN_LIST.tokens}>
<TokenSelectDialog onSelect={() => void 0} />
</TokenListProvider>
</Modal> </Modal>
) )
} }
...@@ -25,9 +25,9 @@ const SearchInput = styled(StringInput)` ...@@ -25,9 +25,9 @@ const SearchInput = styled(StringInput)`
function usePrefetchBalances() { function usePrefetchBalances() {
const { account } = useActiveWeb3React() const { account } = useActiveWeb3React()
const tokenList = useTokenList() const tokenList = useTokenList()
const [prefetchedTokenList, setPrefetchedTokenList] = useState(tokenList) const prefetchedTokenList = useRef<typeof tokenList>()
useEffect(() => setPrefetchedTokenList(tokenList), [tokenList]) useCurrencyBalances(account, tokenList !== prefetchedTokenList.current ? tokenList : undefined)
useCurrencyBalances(account, tokenList !== prefetchedTokenList ? tokenList : undefined) prefetchedTokenList.current = tokenList
} }
function useAreBalancesLoaded(): boolean { function useAreBalancesLoaded(): boolean {
......
import { JsonRpcProvider } from '@ethersproject/providers' import { JsonRpcProvider } from '@ethersproject/providers'
import { TokenInfo } from '@uniswap/token-lists'
import { Provider as Eip1193Provider } from '@web3-react/types' import { Provider as Eip1193Provider } from '@web3-react/types'
import { DEFAULT_LOCALE, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales' import { DEFAULT_LOCALE, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
import { Provider as AtomProvider } from 'jotai' import { Provider as AtomProvider } from 'jotai'
import { TransactionsUpdater } from 'lib/hooks/transactions' import { TransactionsUpdater } from 'lib/hooks/transactions'
import { ActiveWeb3Provider } from 'lib/hooks/useActiveWeb3React' import { ActiveWeb3Provider } from 'lib/hooks/useActiveWeb3React'
import { BlockNumberProvider } from 'lib/hooks/useBlockNumber' import { BlockNumberProvider } from 'lib/hooks/useBlockNumber'
import { TokenListProvider } from 'lib/hooks/useTokenList'
import { Provider as I18nProvider } from 'lib/i18n' import { Provider as I18nProvider } from 'lib/i18n'
import { MulticallUpdater, store as multicallStore } from 'lib/state/multicall' import { MulticallUpdater, store as multicallStore } from 'lib/state/multicall'
import styled, { keyframes, Theme, ThemeProvider } from 'lib/theme' import styled, { keyframes, Theme, ThemeProvider } from 'lib/theme'
...@@ -80,20 +82,12 @@ const DialogWrapper = styled.div` ...@@ -80,20 +82,12 @@ const DialogWrapper = styled.div`
} }
` `
function Updaters() {
return (
<>
<MulticallUpdater />
<TransactionsUpdater />
</>
)
}
export type WidgetProps = { export type WidgetProps = {
theme?: Theme theme?: Theme
locale?: SupportedLocale locale?: SupportedLocale
provider?: Eip1193Provider | JsonRpcProvider provider?: Eip1193Provider | JsonRpcProvider
jsonRpcEndpoint?: string | JsonRpcProvider jsonRpcEndpoint?: string | JsonRpcProvider
tokenList?: string | TokenInfo[]
width?: string | number width?: string | number
dialog?: HTMLElement | null dialog?: HTMLElement | null
className?: string className?: string
...@@ -130,8 +124,9 @@ export default function Widget(props: PropsWithChildren<WidgetProps>) { ...@@ -130,8 +124,9 @@ export default function Widget(props: PropsWithChildren<WidgetProps>) {
<AtomProvider> <AtomProvider>
<ActiveWeb3Provider provider={provider} jsonRpcEndpoint={jsonRpcEndpoint}> <ActiveWeb3Provider provider={provider} jsonRpcEndpoint={jsonRpcEndpoint}>
<BlockNumberProvider> <BlockNumberProvider>
<Updaters /> <MulticallUpdater />
{children} <TransactionsUpdater />
<TokenListProvider list={props.tokenList}>{children}</TokenListProvider>
</BlockNumberProvider> </BlockNumberProvider>
</ActiveWeb3Provider> </ActiveWeb3Provider>
</AtomProvider> </AtomProvider>
......
import { tokens } from '@uniswap/default-token-list'
import { initializeConnector } from '@web3-react/core' import { initializeConnector } from '@web3-react/core'
import { MetaMask } from '@web3-react/metamask' import { MetaMask } from '@web3-react/metamask'
import { Connector } from '@web3-react/types' import { Connector } from '@web3-react/types'
...@@ -75,6 +76,7 @@ export default function Wrapper({ children }: { children: ReactNode }) { ...@@ -75,6 +76,7 @@ export default function Wrapper({ children }: { children: ReactNode }) {
locale={locale} locale={locale}
jsonRpcEndpoint={jsonRpcEndpoint === NO_JSON_RPC ? undefined : jsonRpcEndpoint} jsonRpcEndpoint={jsonRpcEndpoint === NO_JSON_RPC ? undefined : jsonRpcEndpoint}
provider={connector?.provider} provider={connector?.provider}
tokenList={tokens}
> >
{children} {children}
</Widget> </Widget>
......
...@@ -5,9 +5,10 @@ import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' ...@@ -5,9 +5,10 @@ import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import { useToken } from 'lib/hooks/useCurrency' import { useToken } from 'lib/hooks/useCurrency'
import useNativeCurrency from 'lib/hooks/useNativeCurrency' import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { Field, Swap, swapAtom } from 'lib/state/swap' import { Field, Swap, swapAtom } from 'lib/state/swap'
import { useCallback, useLayoutEffect, useRef } from 'react' import { useCallback, useRef } from 'react'
import useOnSupportedNetwork from '../useOnSupportedNetwork' import useOnSupportedNetwork from '../useOnSupportedNetwork'
import { useIsTokenListLoaded } from '../useTokenList'
export type DefaultAddress = string | { [chainId: number]: string | 'NATIVE' } | 'NATIVE' export type DefaultAddress = string | { [chainId: number]: string | 'NATIVE' } | 'NATIVE'
...@@ -72,10 +73,9 @@ export default function useSyncTokenDefaults({ ...@@ -72,10 +73,9 @@ export default function useSyncTokenDefaults({
}, [defaultInputAmount, defaultInputToken, defaultOutputAmount, defaultOutputToken, updateSwap]) }, [defaultInputAmount, defaultInputToken, defaultOutputAmount, defaultOutputToken, updateSwap])
const lastChainId = useRef<number | undefined>(undefined) const lastChainId = useRef<number | undefined>(undefined)
useLayoutEffect(() => { const shouldSync = useIsTokenListLoaded() && chainId && chainId !== lastChainId.current
if (chainId && chainId !== lastChainId.current) { if (shouldSync) {
setToDefaults() setToDefaults()
}
lastChainId.current = chainId lastChainId.current = chainId
}, [chainId, setToDefaults]) }
} }
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, useAtom } from 'jotai'
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 { createContext, PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo' import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
import fetchTokenList from './fetchTokenList' import fetchTokenList from './fetchTokenList'
...@@ -14,19 +12,61 @@ import { validateTokens } from './validateTokenList' ...@@ -14,19 +12,61 @@ 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 | null>(null) const MISSING_PROVIDER = Symbol()
const ChainTokenMapContext = createContext<ChainTokenMap | undefined | typeof MISSING_PROVIDER>(MISSING_PROVIDER)
function useChainTokenMapContext() {
const chainTokenMap = useContext(ChainTokenMapContext)
if (chainTokenMap === MISSING_PROVIDER) {
throw new Error('TokenList hooks must be wrapped in a <TokenListProvider>')
}
return chainTokenMap
}
export function useIsTokenListLoaded() { export function useIsTokenListLoaded() {
return Boolean(useAtomValue(chainTokenMapAtom)) return Boolean(useChainTokenMapContext())
}
export default function useTokenList(): WrappedTokenInfo[] {
const { chainId } = useActiveWeb3React()
const chainTokenMap = useChainTokenMapContext()
const tokenMap = chainId && chainTokenMap?.[chainId]
return useMemo(() => {
if (!tokenMap) return []
return Object.values(tokenMap).map(({ token }) => token)
}, [tokenMap])
}
export type TokenMap = { [address: string]: Token }
export function useTokenMap(): TokenMap {
const { chainId } = useActiveWeb3React()
const chainTokenMap = useChainTokenMapContext()
const tokenMap = chainId && chainTokenMap?.[chainId]
return useMemo(() => {
if (!tokenMap) return {}
return Object.entries(tokenMap).reduce((map, [address, { token }]) => {
map[address] = token
return map
}, {} as TokenMap)
}, [tokenMap])
}
export function useQueryCurrencies(query = ''): (WrappedTokenInfo | NativeCurrency)[] {
return useQueryTokens(query, useTokenList())
} }
export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST): void { export function TokenListProvider({
list = DEFAULT_TOKEN_LIST,
children,
}: PropsWithChildren<{ list?: string | TokenInfo[] }>) {
// 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) const [chainTokenMap, setChainTokenMap] = useState<ChainTokenMap>()
useEffect(() => setChainTokenMap(null), [list, setChainTokenMap])
useEffect(() => setChainTokenMap(undefined), [list])
const { chainId, library } = useActiveWeb3React() const { chainId, library } = useActiveWeb3React()
const resolver = useCallback( const resolver = useCallback(
...@@ -70,34 +110,7 @@ export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST ...@@ -70,34 +110,7 @@ export function useSyncTokenList(list: string | TokenInfo[] = DEFAULT_TOKEN_LIST
} }
} }
} }
}, [chainTokenMap, list, resolver, setChainTokenMap]) }, [chainTokenMap, list, resolver])
}
export default function useTokenList(): WrappedTokenInfo[] {
const { chainId } = useActiveWeb3React()
const chainTokenMap = useAtomValue(chainTokenMapAtom)
const tokenMap = chainId && chainTokenMap?.[chainId]
return useMemo(() => {
if (!tokenMap) return []
return Object.values(tokenMap).map(({ token }) => token)
}, [tokenMap])
}
export type TokenMap = { [address: string]: Token }
export function useTokenMap(): TokenMap { return <ChainTokenMapContext.Provider value={chainTokenMap}>{children}</ChainTokenMapContext.Provider>
const { chainId } = useActiveWeb3React()
const chainTokenMap = useAtomValue(chainTokenMapAtom)
const tokenMap = chainId && chainTokenMap?.[chainId]
return useMemo(() => {
if (!tokenMap) return {}
return Object.entries(tokenMap).reduce((map, [address, { token }]) => {
map[address] = token
return map
}, {} as TokenMap)
}, [tokenMap])
}
export function useQueryCurrencies(query = ''): (WrappedTokenInfo | NativeCurrency)[] {
return useQueryTokens(query, useTokenList())
} }
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