-
-
Notifications
You must be signed in to change notification settings - Fork 248
/
Copy pathErc20TokenList.tsx
103 lines (86 loc) · 3.4 KB
/
Erc20TokenList.tsx
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
import { Contract } from 'ethers'
import { getAddress } from 'ethers/lib/utils'
import { Log } from '@ethersproject/abstract-provider'
import React, { useEffect, useState } from 'react'
import ClipLoader from 'react-spinners/ClipLoader'
import { Erc20TokenData, TokenMapping } from '../common/interfaces'
import Erc20Token from './Erc20Token'
import { isRegistered, getTokenIcon, toFloat } from '../common/util'
import { getTokenData } from './util'
import { ERC20 } from '../common/abis'
import { useNetwork, useProvider } from 'wagmi'
import { providers as multicall } from '@0xsequence/multicall'
interface Props {
filterRegisteredTokens: boolean
filterZeroBalances: boolean
transferEvents: Log[]
approvalEvents: Log[]
tokenMapping?: TokenMapping
inputAddress?: string
}
function Erc20TokenList({
filterRegisteredTokens,
filterZeroBalances,
transferEvents,
approvalEvents,
tokenMapping,
inputAddress
}: Props) {
const [tokens, setTokens] = useState<Erc20TokenData[]>([])
const [loading, setLoading] = useState<boolean>(true)
const provider = useProvider()
const [{ data: networkData }] = useNetwork()
const chainId = networkData?.chain?.id ?? 1
useEffect(() => {
loadData()
}, [inputAddress, provider])
const loadData = async () => {
if (!inputAddress) return
if (!(provider instanceof multicall.MulticallProvider)) return
setLoading(true)
const allEvents = [...approvalEvents, ...transferEvents]
// Filter unique token contract addresses and convert all events to Contract instances
const tokenContracts = allEvents
.filter((event, i) => i === allEvents.findIndex((other) => event.address === other.address))
.map((event) => new Contract(getAddress(event.address), ERC20, provider))
const unsortedTokens = await Promise.all(
tokenContracts.map(async (contract) => {
const tokenApprovals = approvalEvents.filter(approval => approval.address === contract.address)
const registered = isRegistered(contract.address, tokenMapping)
const icon = await getTokenIcon(contract.address, chainId, tokenMapping)
try {
const tokenData = await getTokenData(contract, inputAddress, tokenMapping)
return { ...tokenData, icon, contract, registered, approvals: tokenApprovals }
} catch {
// If the call to getTokenData() fails, the token is not an ERC20 token so
// we do not include it in the token list (should not happen).
return undefined
}
})
)
// Filter undefined tokens and sort tokens alphabetically on token symbol
const sortedTokens = unsortedTokens
.filter((token) => token !== undefined)
.sort((a: any, b: any) => a.symbol.localeCompare(b.symbol))
setTokens(sortedTokens)
setLoading(false)
}
if (loading) {
return (<ClipLoader css="margin: 10px;" size={40} color={'#000'} loading={loading} />)
}
if (tokens.length === 0) {
return (<div className="TokenList">No token balances</div>)
}
const tokenComponents = tokens
.filter((token) => !filterRegisteredTokens || token.registered)
.filter((token) => !filterZeroBalances || !(toFloat(Number(token.balance), token.decimals) === '0.000'))
.map((token) => (
<Erc20Token
key={token.contract.address}
token={token}
inputAddress={inputAddress}
/>
))
return (<div className="TokenList">{tokenComponents}</div>)
}
export default Erc20TokenList