forked from Uniswap/interface
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hooks.ts
138 lines (120 loc) · 4.88 KB
/
hooks.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import DEFAULT_TOKEN_LIST from '@uniswap/default-token-list'
import { TokenList } from '@uniswap/token-lists'
import { useMemo } from 'react'
import { useAppSelector } from 'state/hooks'
import sortByListPriority from 'utils/listSort'
import BROKEN_LIST from '../../constants/tokenLists/broken.tokenlist.json'
import UNSUPPORTED_TOKEN_LIST from '../../constants/tokenLists/unsupported.tokenlist.json'
import { AppState } from '../index'
import { UNSUPPORTED_LIST_URLS } from './../../constants/lists'
import { WrappedTokenInfo } from './wrappedTokenInfo'
export type TokenAddressMap = Readonly<{
[chainId: number]: Readonly<{ [tokenAddress: string]: { token: WrappedTokenInfo; list: TokenList } }>
}>
type Mutable<T> = {
-readonly [P in keyof T]: Mutable<T[P]>
}
const listCache: WeakMap<TokenList, TokenAddressMap> | null =
typeof WeakMap !== 'undefined' ? new WeakMap<TokenList, TokenAddressMap>() : null
function listToTokenMap(list: TokenList): TokenAddressMap {
const result = listCache?.get(list)
if (result) return result
const map = list.tokens.reduce<Mutable<TokenAddressMap>>((tokenMap, tokenInfo) => {
const token = new WrappedTokenInfo(tokenInfo, list)
if (tokenMap[token.chainId]?.[token.address] !== undefined) {
console.error(`Duplicate token! ${token.address}`)
return tokenMap
}
if (!tokenMap[token.chainId]) tokenMap[token.chainId] = {}
tokenMap[token.chainId][token.address] = {
token,
list,
}
return tokenMap
}, {}) as TokenAddressMap
listCache?.set(list, map)
return map
}
const TRANSFORMED_DEFAULT_TOKEN_LIST = listToTokenMap(DEFAULT_TOKEN_LIST)
export function useAllLists(): AppState['lists']['byUrl'] {
return useAppSelector((state) => state.lists.byUrl)
}
/**
* Combine the tokens in map2 with the tokens on map1, where tokens on map1 take precedence
* @param map1 the base token map
* @param map2 the map of additioanl tokens to add to the base map
*/
export function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddressMap {
const chainIds = Object.keys(
Object.keys(map1)
.concat(Object.keys(map2))
.reduce<{ [chainId: string]: true }>((memo, value) => {
memo[value] = true
return memo
}, {})
).map((id) => parseInt(id))
return chainIds.reduce<Mutable<TokenAddressMap>>((memo, chainId) => {
memo[chainId] = {
...map2[chainId],
// map1 takes precedence
...map1[chainId],
}
return memo
}, {}) as TokenAddressMap
}
// merge tokens contained within lists from urls
function useCombinedTokenMapFromUrls(urls: string[] | undefined): TokenAddressMap {
const lists = useAllLists()
return useMemo(() => {
if (!urls) return {}
return (
urls
.slice()
// sort by priority so top priority goes last
.sort(sortByListPriority)
.reduce((allTokens, currentUrl) => {
const current = lists[currentUrl]?.current
if (!current) return allTokens
try {
return combineMaps(allTokens, listToTokenMap(current))
} catch (error) {
console.error('Could not show token list due to error', error)
return allTokens
}
}, {})
)
}, [lists, urls])
}
// filter out unsupported lists
export function useActiveListUrls(): string[] | undefined {
return useAppSelector((state) => state.lists.activeListUrls)?.filter((url) => !UNSUPPORTED_LIST_URLS.includes(url))
}
export function useInactiveListUrls(): string[] {
const lists = useAllLists()
const allActiveListUrls = useActiveListUrls()
return Object.keys(lists).filter((url) => !allActiveListUrls?.includes(url) && !UNSUPPORTED_LIST_URLS.includes(url))
}
// get all the tokens from active lists, combine with local default tokens
export function useCombinedActiveList(): TokenAddressMap {
const activeListUrls = useActiveListUrls()
const activeTokens = useCombinedTokenMapFromUrls(activeListUrls)
return combineMaps(activeTokens, TRANSFORMED_DEFAULT_TOKEN_LIST)
}
// list of tokens not supported on interface for various reasons, used to show warnings and prevent swaps and adds
export function useUnsupportedTokenList(): TokenAddressMap {
// get hard-coded broken tokens
const brokenListMap = useMemo(() => listToTokenMap(BROKEN_LIST), [])
// get hard-coded list of unsupported tokens
const localUnsupportedListMap = useMemo(() => listToTokenMap(UNSUPPORTED_TOKEN_LIST), [])
// get dynamic list of unsupported tokens
const loadedUnsupportedListMap = useCombinedTokenMapFromUrls(UNSUPPORTED_LIST_URLS)
// format into one token address map
return useMemo(
() => combineMaps(brokenListMap, combineMaps(localUnsupportedListMap, loadedUnsupportedListMap)),
[brokenListMap, localUnsupportedListMap, loadedUnsupportedListMap]
)
}
export function useIsListActive(url: string): boolean {
const activeListUrls = useActiveListUrls()
return Boolean(activeListUrls?.includes(url))
}