|
| 1 | +import { parseBytes32String } from '@ethersproject/strings' |
1 | 2 | import { ChainId, Token, WETH } from '@uniswap/sdk' |
2 | 3 | import { useEffect, useMemo } from 'react' |
3 | 4 | import { ALL_TOKENS } from '../constants/tokens' |
4 | | -import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks' |
| 5 | +import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks' |
| 6 | +import { useAddUserToken, useUserAddedTokens } from '../state/user/hooks' |
5 | 7 | import { isAddress } from '../utils' |
6 | 8 |
|
7 | 9 | import { useActiveWeb3React } from './index' |
| 10 | +import { useBytes32TokenContract, useTokenContract } from './useContract' |
8 | 11 |
|
9 | 12 | export function useAllTokens(): { [address: string]: Token } { |
10 | 13 | const { chainId } = useActiveWeb3React() |
@@ -35,36 +38,83 @@ export function useAllTokens(): { [address: string]: Token } { |
35 | 38 | }, [userAddedTokens, chainId]) |
36 | 39 | } |
37 | 40 |
|
38 | | -export function useToken(tokenAddress?: string): Token | undefined { |
| 41 | +// parse a name or symbol from a token response |
| 42 | +const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/ |
| 43 | +function parseStringOrBytes32(str: string | undefined, bytes32: string | undefined, defaultValue: string): string { |
| 44 | + return str && str.length > 0 |
| 45 | + ? str |
| 46 | + : bytes32 && BYTES32_REGEX.test(bytes32) |
| 47 | + ? parseBytes32String(bytes32) |
| 48 | + : defaultValue |
| 49 | +} |
| 50 | + |
| 51 | +// undefined if invalid or does not exist |
| 52 | +// null if loading |
| 53 | +// otherwise returns the token |
| 54 | +export function useToken(tokenAddress?: string): Token | undefined | null { |
| 55 | + const { chainId } = useActiveWeb3React() |
39 | 56 | const tokens = useAllTokens() |
| 57 | + |
| 58 | + const address = isAddress(tokenAddress) |
| 59 | + |
| 60 | + const tokenContract = useTokenContract(address ? address : undefined, false) |
| 61 | + const tokenContractBytes32 = useBytes32TokenContract(address ? address : undefined, false) |
| 62 | + const token: Token | undefined = address ? tokens[address] : undefined |
| 63 | + |
| 64 | + const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD) |
| 65 | + const tokenNameBytes32 = useSingleCallResult( |
| 66 | + token ? undefined : tokenContractBytes32, |
| 67 | + 'name', |
| 68 | + undefined, |
| 69 | + NEVER_RELOAD |
| 70 | + ) |
| 71 | + const symbol = useSingleCallResult(token ? undefined : tokenContract, 'symbol', undefined, NEVER_RELOAD) |
| 72 | + const symbolBytes32 = useSingleCallResult(token ? undefined : tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD) |
| 73 | + const decimals = useSingleCallResult(token ? undefined : tokenContract, 'decimals', undefined, NEVER_RELOAD) |
| 74 | + |
40 | 75 | return useMemo(() => { |
41 | | - const validatedAddress = isAddress(tokenAddress) |
42 | | - if (!validatedAddress) return |
43 | | - return tokens[validatedAddress] |
44 | | - }, [tokens, tokenAddress]) |
| 76 | + if (token) return token |
| 77 | + if (!chainId || !address) return undefined |
| 78 | + if (decimals.loading || symbol.loading || tokenName.loading) return null |
| 79 | + if (decimals.result) { |
| 80 | + return new Token( |
| 81 | + chainId, |
| 82 | + address, |
| 83 | + decimals.result[0], |
| 84 | + parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'), |
| 85 | + parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token') |
| 86 | + ) |
| 87 | + } |
| 88 | + return undefined |
| 89 | + }, [ |
| 90 | + address, |
| 91 | + chainId, |
| 92 | + decimals.loading, |
| 93 | + decimals.result, |
| 94 | + symbol.loading, |
| 95 | + symbol.result, |
| 96 | + symbolBytes32.result, |
| 97 | + token, |
| 98 | + tokenName.loading, |
| 99 | + tokenName.result, |
| 100 | + tokenNameBytes32.result |
| 101 | + ]) |
45 | 102 | } |
46 | 103 |
|
47 | 104 | // gets token information by address (typically user input) and |
48 | | -// automatically adds it for the user if the token address is valid |
49 | | -export function useTokenByAddressAndAutomaticallyAdd(tokenAddress?: string): Token | undefined { |
50 | | - const fetchTokenByAddress = useFetchTokenByAddress() |
| 105 | +// automatically adds it for the user if it's a valid token address |
| 106 | +export function useTokenByAddressAndAutomaticallyAdd(tokenAddress?: string): Token | undefined | null { |
51 | 107 | const addToken = useAddUserToken() |
52 | 108 | const token = useToken(tokenAddress) |
53 | 109 | const { chainId } = useActiveWeb3React() |
| 110 | + const allTokens = useAllTokens() |
54 | 111 |
|
55 | 112 | useEffect(() => { |
56 | | - if (!chainId || !isAddress(tokenAddress)) return |
57 | | - const weth = WETH[chainId as ChainId] |
58 | | - if (weth && weth.address === isAddress(tokenAddress)) return |
59 | | - |
60 | | - if (tokenAddress && !token) { |
61 | | - fetchTokenByAddress(tokenAddress).then(token => { |
62 | | - if (token !== null) { |
63 | | - addToken(token) |
64 | | - } |
65 | | - }) |
66 | | - } |
67 | | - }, [tokenAddress, token, fetchTokenByAddress, addToken, chainId]) |
| 113 | + if (!chainId || !token) return |
| 114 | + if (WETH[chainId as ChainId]?.address === token.address) return |
| 115 | + if (allTokens[token.address]) return |
| 116 | + addToken(token) |
| 117 | + }, [token, addToken, chainId, allTokens]) |
68 | 118 |
|
69 | 119 | return token |
70 | 120 | } |
0 commit comments