fix: include user-added tokens in selector (#5319)

This commit is contained in:
Zach Pomerantz 2022-11-18 12:32:14 -08:00 committed by GitHub
parent 8cd32138ac
commit ce51ffae75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 48 deletions

@ -17,8 +17,9 @@ import { FixedSizeList } from 'react-window'
import { Text } from 'rebass' import { Text } from 'rebass'
import { useAllTokenBalances } from 'state/connection/hooks' import { useAllTokenBalances } from 'state/connection/hooks'
import styled, { useTheme } from 'styled-components/macro' import styled, { useTheme } from 'styled-components/macro'
import { UserAddedToken } from 'types/tokens'
import { useActiveTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens' import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { CloseIcon, ThemedText } from '../../theme' import { CloseIcon, ThemedText } from '../../theme'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import Column from '../Column' import Column from '../Column'
@ -66,15 +67,8 @@ export function CurrencySearch({
const [searchQuery, setSearchQuery] = useState<string>('') const [searchQuery, setSearchQuery] = useState<string>('')
const debouncedQuery = useDebounce(searchQuery, 200) const debouncedQuery = useDebounce(searchQuery, 200)
// Only display 'imported' tokens when the search filter has input
const defaultTokens = useActiveTokens(debouncedQuery.length > 0)
// if they input an address, use it
const isAddressSearch = isAddress(debouncedQuery) const isAddressSearch = isAddress(debouncedQuery)
const searchToken = useToken(debouncedQuery) const searchToken = useToken(debouncedQuery)
const searchTokenIsAdded = useIsUserAddedToken(searchToken) const searchTokenIsAdded = useIsUserAddedToken(searchToken)
useEffect(() => { useEffect(() => {
@ -87,13 +81,26 @@ export function CurrencySearch({
} }
}, [isAddressSearch]) }, [isAddressSearch])
const defaultTokens = useAllTokens()
const filteredTokens: Token[] = useMemo(() => { const filteredTokens: Token[] = useMemo(() => {
return Object.values(defaultTokens).filter(getTokenFilter(debouncedQuery)) return Object.values(defaultTokens).filter(getTokenFilter(debouncedQuery))
}, [defaultTokens, debouncedQuery]) }, [defaultTokens, debouncedQuery])
const [balances, balancesAreLoading] = useAllTokenBalances() const [balances, balancesAreLoading] = useAllTokenBalances()
const sortedTokens: Token[] = useMemo( const sortedTokens: Token[] = useMemo(
() => (!balancesAreLoading ? [...filteredTokens].sort(tokenComparator.bind(null, balances)) : []), () =>
!balancesAreLoading
? [...filteredTokens]
.filter((token) => {
// Filter out user-added tokens with no balance
if (token instanceof UserAddedToken) {
const balance = balances[token.address]
return balance?.greaterThan(0)
}
return true
})
.sort(tokenComparator.bind(null, balances))
: [],
[balances, filteredTokens, balancesAreLoading] [balances, filteredTokens, balancesAreLoading]
) )
const isLoading = Boolean(balancesAreLoading && !tokenLoaderTimerElapsed) const isLoading = Boolean(balancesAreLoading && !tokenLoaderTimerElapsed)

@ -14,50 +14,38 @@ import { useUserAddedTokens, useUserAddedTokensOnChain } from '../state/user/hoo
import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks' import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
// reduce token map into standard address <-> Token mapping, optionally include user added tokens // reduce token map into standard address <-> Token mapping, optionally include user added tokens
function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } { function useTokensFromMap(tokenMap: TokenAddressMap): { [address: string]: Token } {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const userAddedTokens = useUserAddedTokens()
return useMemo(() => { return useMemo(() => {
if (!chainId) return {} if (!chainId) return {}
// reduce to just tokens // reduce to just tokens
const mapWithoutUrls = Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>( return Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>((newMap, address) => {
(newMap, address) => { newMap[address] = tokenMap[chainId][address].token
newMap[address] = tokenMap[chainId][address].token return newMap
return newMap }, {})
}, }, [chainId, tokenMap])
{}
)
if (includeUserAdded) {
return (
userAddedTokens
// reduce into all ALL_TOKENS filtered by the current chain
.reduce<{ [address: string]: Token }>(
(tokenMap, token) => {
tokenMap[token.address] = token
return tokenMap
},
// must make a copy because reduce modifies the map, and we do not
// want to make a copy in every iteration
{ ...mapWithoutUrls }
)
)
}
return mapWithoutUrls
}, [chainId, userAddedTokens, tokenMap, includeUserAdded])
} }
export function useAllTokens(): { [address: string]: Token } { export function useAllTokens(): { [address: string]: Token } {
const allTokens = useCombinedActiveList() const allTokens = useCombinedActiveList()
return useTokensFromMap(allTokens, true) const tokensFromMap = useTokensFromMap(allTokens)
} const userAddedTokens = useUserAddedTokens()
return useMemo(() => {
export function useActiveTokens(includeUserAdded: boolean): { [address: string]: Token } { return (
const allTokens = useCombinedActiveList() userAddedTokens
return useTokensFromMap(allTokens, includeUserAdded) // reduce into all ALL_TOKENS filtered by the current chain
.reduce<{ [address: string]: Token }>(
(tokenMap, token) => {
tokenMap[token.address] = token
return tokenMap
},
// must make a copy because reduce modifies the map, and we do not
// want to make a copy in every iteration
{ ...tokensFromMap }
)
)
}, [tokensFromMap, userAddedTokens])
} }
type BridgeInfo = Record< type BridgeInfo = Record<
@ -73,7 +61,7 @@ export function useUnsupportedTokens(): { [address: string]: Token } {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const listsByUrl = useAllLists() const listsByUrl = useAllLists()
const unsupportedTokensMap = useUnsupportedTokenList() const unsupportedTokensMap = useUnsupportedTokenList()
const unsupportedTokens = useTokensFromMap(unsupportedTokensMap, false) const unsupportedTokens = useTokensFromMap(unsupportedTokensMap)
// checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported // checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported
const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => { const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => {

@ -8,6 +8,7 @@ import JSBI from 'jsbi'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { shallowEqual } from 'react-redux' import { shallowEqual } from 'react-redux'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { UserAddedToken } from 'types/tokens'
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses' import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing' import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing'
@ -38,8 +39,8 @@ function serializeToken(token: Token): SerializedToken {
} }
} }
function deserializeToken(serializedToken: SerializedToken): Token { function deserializeToken(serializedToken: SerializedToken, Class: typeof Token = Token): Token {
return new Token( return new Class(
serializedToken.chainId, serializedToken.chainId,
serializedToken.address, serializedToken.address,
serializedToken.decimals, serializedToken.decimals,
@ -238,7 +239,7 @@ export function useUserAddedTokensOnChain(chainId: number | undefined | null): T
return useMemo(() => { return useMemo(() => {
if (!chainId) return [] if (!chainId) return []
const tokenMap: Token[] = serializedTokensMap?.[chainId] const tokenMap: Token[] = serializedTokensMap?.[chainId]
? Object.values(serializedTokensMap[chainId]).map(deserializeToken) ? Object.values(serializedTokensMap[chainId]).map((value) => deserializeToken(value, UserAddedToken))
: [] : []
return tokenMap return tokenMap
}, [serializedTokensMap, chainId]) }, [serializedTokensMap, chainId])

3
src/types/tokens.ts Normal file

@ -0,0 +1,3 @@
import { Token } from '@uniswap/sdk-core'
export class UserAddedToken extends Token {}