@lifi/widget 3.29.1 → 3.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.d.ts +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +3 -2
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.d.ts +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.js +3 -2
- package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
- package/dist/esm/components/ChainSelect/ChainSelect.js +35 -18
- package/dist/esm/components/ChainSelect/ChainSelect.js.map +1 -1
- package/dist/esm/components/ChainSelect/useChainSelect.d.ts +1 -1
- package/dist/esm/components/ChainSelect/useChainSelect.js +3 -3
- package/dist/esm/components/ChainSelect/useChainSelect.js.map +1 -1
- package/dist/esm/components/Chains/AllChainsAvatar.d.ts +7 -0
- package/dist/esm/components/Chains/AllChainsAvatar.js +77 -0
- package/dist/esm/components/Chains/AllChainsAvatar.js.map +1 -0
- package/dist/esm/components/Chains/ChainList.d.ts +2 -1
- package/dist/esm/components/Chains/ChainList.js +2 -2
- package/dist/esm/components/Chains/ChainList.js.map +1 -1
- package/dist/esm/components/Chains/ChainSearchInput.js +2 -2
- package/dist/esm/components/Chains/ChainSearchInput.js.map +1 -1
- package/dist/esm/components/Chains/SelectChainContent.js +1 -1
- package/dist/esm/components/Chains/SelectChainContent.js.map +1 -1
- package/dist/esm/components/Chains/VirtualizedChainList.d.ts +2 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js +42 -10
- package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
- package/dist/esm/components/RouteCard/RouteCardEssentials.js +1 -1
- package/dist/esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheet.d.ts +1 -5
- package/dist/esm/components/TokenList/TokenDetailsSheet.js +4 -2
- package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +2 -2
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
- package/dist/esm/components/TokenList/TokenList.js +11 -53
- package/dist/esm/components/TokenList/TokenList.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.d.ts +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +29 -25
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +56 -37
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TokenList/types.d.ts +6 -10
- package/dist/esm/components/TokenList/useTokenSelect.js +3 -4
- package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
- package/dist/esm/components/TransactionDetails.js +2 -2
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/hooks/useAccountsBalancesData.d.ts +6 -0
- package/dist/esm/hooks/useAccountsBalancesData.js +64 -0
- package/dist/esm/hooks/useAccountsBalancesData.js.map +1 -0
- package/dist/esm/hooks/useFilteredByTokenBalances.d.ts +8 -0
- package/dist/esm/hooks/useFilteredByTokenBalances.js +69 -0
- package/dist/esm/hooks/useFilteredByTokenBalances.js.map +1 -0
- package/dist/esm/hooks/useListHeight.d.ts +0 -1
- package/dist/esm/hooks/useListHeight.js +0 -1
- package/dist/esm/hooks/useListHeight.js.map +1 -1
- package/dist/esm/hooks/useToken.d.ts +2 -2
- package/dist/esm/hooks/useToken.js +13 -11
- package/dist/esm/hooks/useToken.js.map +1 -1
- package/dist/esm/hooks/useTokenAddressBalance.d.ts +2 -3
- package/dist/esm/hooks/useTokenAddressBalance.js +10 -14
- package/dist/esm/hooks/useTokenAddressBalance.js.map +1 -1
- package/dist/esm/hooks/useTokenBalances.d.ts +6 -9
- package/dist/esm/hooks/useTokenBalances.js +57 -72
- package/dist/esm/hooks/useTokenBalances.js.map +1 -1
- package/dist/esm/hooks/useTokenBalancesQueries.d.ts +11 -0
- package/dist/esm/hooks/useTokenBalancesQueries.js +74 -0
- package/dist/esm/hooks/useTokenBalancesQueries.js.map +1 -0
- package/dist/esm/hooks/useTokens.d.ts +6 -6
- package/dist/esm/hooks/useTokens.js +78 -70
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/i18n/bn.json +3 -3
- package/dist/esm/i18n/de.json +3 -3
- package/dist/esm/i18n/en.json +3 -3
- package/dist/esm/i18n/es.json +3 -3
- package/dist/esm/i18n/fr.json +3 -3
- package/dist/esm/i18n/hi.json +3 -3
- package/dist/esm/i18n/id.json +3 -3
- package/dist/esm/i18n/it.json +3 -3
- package/dist/esm/i18n/ja.json +3 -3
- package/dist/esm/i18n/ko.json +3 -3
- package/dist/esm/i18n/pl.json +3 -3
- package/dist/esm/i18n/pt.json +3 -3
- package/dist/esm/i18n/th.json +3 -3
- package/dist/esm/i18n/tr.json +3 -3
- package/dist/esm/i18n/uk.json +3 -3
- package/dist/esm/i18n/vi.json +3 -3
- package/dist/esm/i18n/zh.json +3 -3
- package/dist/esm/pages/SelectTokenPage/SearchTokenInput.js +6 -1
- package/dist/esm/pages/SelectTokenPage/SearchTokenInput.js.map +1 -1
- package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js +2 -4
- package/dist/esm/pages/SelectTokenPage/SelectTokenPage.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +1 -1
- package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
- package/dist/esm/stores/chains/createChainOrderStore.d.ts +3 -2
- package/dist/esm/stores/chains/createChainOrderStore.js +13 -8
- package/dist/esm/stores/chains/createChainOrderStore.js.map +1 -1
- package/dist/esm/stores/chains/types.d.ts +2 -0
- package/dist/esm/stores/chains/useChainOrder.js +5 -1
- package/dist/esm/stores/chains/useChainOrder.js.map +1 -1
- package/dist/esm/types/token.d.ts +7 -2
- package/dist/esm/utils/chainType.d.ts +1 -0
- package/dist/esm/utils/chainType.js +2 -0
- package/dist/esm/utils/chainType.js.map +1 -1
- package/dist/esm/utils/token.d.ts +8 -0
- package/dist/esm/utils/token.js +29 -0
- package/dist/esm/utils/token.js.map +1 -0
- package/dist/esm/utils/tokenList.d.ts +13 -0
- package/dist/esm/utils/tokenList.js +106 -0
- package/dist/esm/utils/tokenList.js.map +1 -0
- package/package.json +7 -7
- package/package.json.tmp +6 -6
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +3 -2
- package/src/components/AmountInput/PriceFormHelperText.tsx +3 -2
- package/src/components/ChainSelect/ChainSelect.tsx +112 -40
- package/src/components/ChainSelect/useChainSelect.ts +3 -3
- package/src/components/Chains/AllChainsAvatar.tsx +113 -0
- package/src/components/Chains/ChainList.tsx +3 -0
- package/src/components/Chains/ChainSearchInput.tsx +2 -2
- package/src/components/Chains/SelectChainContent.tsx +1 -0
- package/src/components/Chains/VirtualizedChainList.tsx +80 -12
- package/src/components/RouteCard/RouteCardEssentials.tsx +1 -1
- package/src/components/TokenList/TokenDetailsSheet.tsx +4 -9
- package/src/components/TokenList/TokenDetailsSheetContent.tsx +2 -6
- package/src/components/TokenList/TokenList.tsx +57 -129
- package/src/components/TokenList/TokenListItem.tsx +191 -166
- package/src/components/TokenList/VirtualizedTokenList.tsx +88 -48
- package/src/components/TokenList/types.ts +14 -10
- package/src/components/TokenList/useTokenSelect.ts +3 -4
- package/src/components/TransactionDetails.tsx +2 -2
- package/src/config/version.ts +1 -1
- package/src/hooks/useAccountsBalancesData.ts +101 -0
- package/src/hooks/useFilteredByTokenBalances.ts +101 -0
- package/src/hooks/useListHeight.ts +0 -1
- package/src/hooks/useToken.ts +26 -14
- package/src/hooks/useTokenAddressBalance.ts +14 -20
- package/src/hooks/useTokenBalances.ts +81 -80
- package/src/hooks/useTokenBalancesQueries.ts +94 -0
- package/src/hooks/useTokens.ts +118 -90
- package/src/i18n/bn.json +3 -3
- package/src/i18n/de.json +3 -3
- package/src/i18n/en.json +3 -3
- package/src/i18n/es.json +3 -3
- package/src/i18n/fr.json +3 -3
- package/src/i18n/hi.json +3 -3
- package/src/i18n/id.json +3 -3
- package/src/i18n/it.json +3 -3
- package/src/i18n/ja.json +3 -3
- package/src/i18n/ko.json +3 -3
- package/src/i18n/pl.json +3 -3
- package/src/i18n/pt.json +3 -3
- package/src/i18n/th.json +3 -3
- package/src/i18n/tr.json +3 -3
- package/src/i18n/uk.json +3 -3
- package/src/i18n/vi.json +3 -3
- package/src/i18n/zh.json +3 -3
- package/src/pages/SelectTokenPage/SearchTokenInput.tsx +5 -0
- package/src/pages/SelectTokenPage/SelectTokenPage.tsx +7 -13
- package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +1 -1
- package/src/stores/chains/createChainOrderStore.ts +17 -8
- package/src/stores/chains/types.ts +2 -0
- package/src/stores/chains/useChainOrder.ts +5 -1
- package/src/types/token.ts +11 -2
- package/src/utils/chainType.ts +2 -0
- package/src/utils/token.ts +65 -0
- package/src/utils/tokenList.ts +172 -0
- package/dist/esm/components/TokenList/utils.d.ts +0 -2
- package/dist/esm/components/TokenList/utils.js +0 -35
- package/dist/esm/components/TokenList/utils.js.map +0 -1
- package/src/components/TokenList/utils.ts +0 -42
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Typography } from '@mui/material'
|
|
2
2
|
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
3
3
|
import type { FC } from 'react'
|
|
4
|
-
import { useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
5
5
|
import { useTranslation } from 'react-i18next'
|
|
6
|
+
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
6
7
|
import type { TokenAmount } from '../../types/token.js'
|
|
7
8
|
import { TokenDetailsSheet } from './TokenDetailsSheet.js'
|
|
8
9
|
import { List } from './TokenList.style.js'
|
|
@@ -12,50 +13,64 @@ import type {
|
|
|
12
13
|
VirtualizedTokenListProps,
|
|
13
14
|
} from './types.js'
|
|
14
15
|
|
|
16
|
+
const tokenItemHeight = 64 // 60 + 4px margin-bottom
|
|
17
|
+
|
|
15
18
|
export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
16
|
-
account,
|
|
17
19
|
tokens,
|
|
18
20
|
scrollElementRef,
|
|
19
21
|
chainId,
|
|
20
|
-
chain,
|
|
21
22
|
selectedTokenAddress,
|
|
22
23
|
isLoading,
|
|
23
24
|
isBalanceLoading,
|
|
24
25
|
showCategories,
|
|
25
26
|
onClick,
|
|
27
|
+
isAllNetworks,
|
|
26
28
|
}) => {
|
|
27
29
|
const { t } = useTranslation()
|
|
28
30
|
|
|
31
|
+
const { chains } = useAvailableChains()
|
|
32
|
+
|
|
33
|
+
// Create Set for O(1) chain lookup instead of O(n) find
|
|
34
|
+
const chainsSet = useMemo(() => {
|
|
35
|
+
if (!chains) {
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
return new Map(chains.map((chain) => [chain.id, chain]))
|
|
39
|
+
}, [chains])
|
|
40
|
+
|
|
29
41
|
const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)
|
|
30
42
|
|
|
31
43
|
const onShowTokenDetails = useCallback(
|
|
32
|
-
(tokenAddress: string, noContractAddress: boolean) => {
|
|
33
|
-
tokenDetailsSheetRef.current?.open(
|
|
44
|
+
(tokenAddress: string, noContractAddress: boolean, chainId: number) => {
|
|
45
|
+
tokenDetailsSheetRef.current?.open(
|
|
46
|
+
tokenAddress,
|
|
47
|
+
noContractAddress,
|
|
48
|
+
chainId
|
|
49
|
+
)
|
|
34
50
|
},
|
|
35
51
|
[]
|
|
36
52
|
)
|
|
37
53
|
|
|
38
54
|
const getItemKey = useCallback(
|
|
39
55
|
(index: number) => {
|
|
40
|
-
|
|
56
|
+
const token = tokens[index]
|
|
57
|
+
return `${token.chainId}-${token.address}-${index}`
|
|
41
58
|
},
|
|
42
59
|
[tokens]
|
|
43
60
|
)
|
|
44
61
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
getScrollElement: () => scrollElementRef.current,
|
|
50
|
-
estimateSize: (index) => {
|
|
62
|
+
const estimateSize = useCallback(
|
|
63
|
+
(index: number) => {
|
|
64
|
+
const currentToken = tokens[index]
|
|
65
|
+
|
|
51
66
|
// Base size for TokenListItem
|
|
52
|
-
let size =
|
|
67
|
+
let size = tokenItemHeight
|
|
68
|
+
|
|
53
69
|
// Early return if categories are not shown
|
|
54
70
|
if (!showCategories) {
|
|
55
71
|
return size
|
|
56
72
|
}
|
|
57
73
|
|
|
58
|
-
const currentToken = tokens[index]
|
|
59
74
|
const previousToken = tokens[index - 1]
|
|
60
75
|
|
|
61
76
|
// Adjust size for the first featured token
|
|
@@ -75,8 +90,30 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
75
90
|
|
|
76
91
|
return size
|
|
77
92
|
},
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
[tokens, showCategories]
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
// Chunk the tokens for infinite loading simulation
|
|
97
|
+
const virtualizerConfig = useMemo(
|
|
98
|
+
() => ({
|
|
99
|
+
count: tokens.length,
|
|
100
|
+
overscan: 5,
|
|
101
|
+
getScrollElement: () => scrollElementRef.current,
|
|
102
|
+
estimateSize,
|
|
103
|
+
getItemKey,
|
|
104
|
+
}),
|
|
105
|
+
[tokens.length, estimateSize, getItemKey, scrollElementRef]
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const { getVirtualItems, getTotalSize, scrollToIndex, measure } =
|
|
109
|
+
useVirtualizer(virtualizerConfig)
|
|
110
|
+
|
|
111
|
+
// Address the issue of disappearing tokens on rerender
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (scrollElementRef.current) {
|
|
114
|
+
measure()
|
|
115
|
+
}
|
|
116
|
+
}, [measure, scrollElementRef.current])
|
|
80
117
|
|
|
81
118
|
// biome-ignore lint/correctness/useExhaustiveDependencies: run only when chainId changes
|
|
82
119
|
useEffect(() => {
|
|
@@ -86,17 +123,7 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
86
123
|
}
|
|
87
124
|
// Close the token details sheet when switching the chains
|
|
88
125
|
tokenDetailsSheetRef.current?.close()
|
|
89
|
-
}, [scrollToIndex, chainId, getVirtualItems])
|
|
90
|
-
|
|
91
|
-
if (isLoading) {
|
|
92
|
-
return (
|
|
93
|
-
<List disablePadding>
|
|
94
|
-
{Array.from({ length: 3 }).map((_, index) => (
|
|
95
|
-
<TokenListItemSkeleton key={index} />
|
|
96
|
-
))}
|
|
97
|
-
</List>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
126
|
+
}, [scrollToIndex, isAllNetworks, chainId, getVirtualItems])
|
|
100
127
|
|
|
101
128
|
return (
|
|
102
129
|
<>
|
|
@@ -109,6 +136,8 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
109
136
|
const currentToken = tokens[item.index]
|
|
110
137
|
const previousToken: TokenAmount | undefined = tokens[item.index - 1]
|
|
111
138
|
|
|
139
|
+
const chain = chainsSet?.get(currentToken.chainId)
|
|
140
|
+
|
|
112
141
|
const isFirstFeaturedToken = currentToken.featured && item.index === 0
|
|
113
142
|
|
|
114
143
|
const isTransitionFromFeaturedTokens =
|
|
@@ -129,23 +158,28 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
129
158
|
isTransitionFromFeaturedTokens ||
|
|
130
159
|
(previousToken?.popular && !currentToken.popular)
|
|
131
160
|
|
|
132
|
-
const startAdornmentLabel =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
161
|
+
const startAdornmentLabel =
|
|
162
|
+
!isAllNetworks && showCategories
|
|
163
|
+
? (() => {
|
|
164
|
+
if (isFirstFeaturedToken) {
|
|
165
|
+
return t('main.featuredTokens')
|
|
166
|
+
}
|
|
167
|
+
if (isTransitionToMyTokens) {
|
|
168
|
+
return t('main.myTokens')
|
|
169
|
+
}
|
|
170
|
+
if (isTransitionToPopularTokens) {
|
|
171
|
+
return t('main.popularTokens')
|
|
172
|
+
}
|
|
173
|
+
if (shouldShowAllTokensCategory) {
|
|
174
|
+
return t('main.allTokens')
|
|
175
|
+
}
|
|
176
|
+
return null
|
|
177
|
+
})()
|
|
178
|
+
: null
|
|
179
|
+
|
|
180
|
+
const isSelected =
|
|
181
|
+
selectedTokenAddress === currentToken.address &&
|
|
182
|
+
chainId === currentToken.chainId
|
|
149
183
|
|
|
150
184
|
return (
|
|
151
185
|
<TokenListItem
|
|
@@ -154,11 +188,10 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
154
188
|
size={item.size}
|
|
155
189
|
start={item.start}
|
|
156
190
|
token={currentToken}
|
|
157
|
-
chain={chain}
|
|
158
|
-
selected={
|
|
191
|
+
chain={isAllNetworks ? chain : undefined}
|
|
192
|
+
selected={isSelected}
|
|
159
193
|
onShowTokenDetails={onShowTokenDetails}
|
|
160
194
|
isBalanceLoading={isBalanceLoading}
|
|
161
|
-
accountAddress={account.address}
|
|
162
195
|
startAdornment={
|
|
163
196
|
startAdornmentLabel ? (
|
|
164
197
|
<Typography
|
|
@@ -179,7 +212,14 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
|
|
|
179
212
|
)
|
|
180
213
|
})}
|
|
181
214
|
</List>
|
|
182
|
-
<TokenDetailsSheet ref={tokenDetailsSheetRef}
|
|
215
|
+
<TokenDetailsSheet ref={tokenDetailsSheetRef} />
|
|
216
|
+
{isLoading && (
|
|
217
|
+
<List disablePadding sx={{ cursor: 'default' }}>
|
|
218
|
+
{Array.from({ length: 3 }).map((_, index) => (
|
|
219
|
+
<TokenListItemSkeleton key={index} />
|
|
220
|
+
))}
|
|
221
|
+
</List>
|
|
222
|
+
)}
|
|
183
223
|
</>
|
|
184
224
|
)
|
|
185
225
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/sdk'
|
|
2
|
-
import type {
|
|
3
|
-
import type { MouseEventHandler, RefObject } from 'react'
|
|
2
|
+
import type { RefObject } from 'react'
|
|
4
3
|
import type { FormType } from '../../stores/form/types.js'
|
|
5
4
|
import type { TokenAmount } from '../../types/token.js'
|
|
6
5
|
|
|
@@ -12,16 +11,15 @@ export interface TokenListProps {
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export interface VirtualizedTokenListProps {
|
|
15
|
-
account: Account
|
|
16
14
|
tokens: TokenAmount[]
|
|
17
15
|
scrollElementRef: RefObject<HTMLElement | null>
|
|
18
16
|
isLoading: boolean
|
|
19
17
|
isBalanceLoading: boolean
|
|
20
18
|
chainId?: number
|
|
21
|
-
chain?: ExtendedChain
|
|
22
19
|
showCategories?: boolean
|
|
23
20
|
onClick(tokenAddress: string, chainId?: number): void
|
|
24
21
|
selectedTokenAddress?: string
|
|
22
|
+
isAllNetworks: boolean
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
export interface TokenListItemBaseProps {
|
|
@@ -31,9 +29,12 @@ export interface TokenListItemBaseProps {
|
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
export interface TokenListItemProps extends TokenListItemBaseProps {
|
|
34
|
-
accountAddress?: string
|
|
35
32
|
token: TokenAmount
|
|
36
|
-
onShowTokenDetails: (
|
|
33
|
+
onShowTokenDetails: (
|
|
34
|
+
tokenAddress: string,
|
|
35
|
+
noContractAddress: boolean,
|
|
36
|
+
chainId: number
|
|
37
|
+
) => void
|
|
37
38
|
chain?: ExtendedChain
|
|
38
39
|
isBalanceLoading?: boolean
|
|
39
40
|
startAdornment?: React.ReactNode
|
|
@@ -42,9 +43,12 @@ export interface TokenListItemProps extends TokenListItemBaseProps {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export interface TokenListItemButtonProps {
|
|
45
|
-
onShowTokenDetails: (
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
onShowTokenDetails: (
|
|
47
|
+
tokenAddress: string,
|
|
48
|
+
noContractAddress: boolean,
|
|
49
|
+
chainId: number
|
|
50
|
+
) => void
|
|
51
|
+
onClick?(tokenAddress: string, chainId?: number): void
|
|
48
52
|
token: TokenAmount
|
|
49
53
|
chain?: ExtendedChain
|
|
50
54
|
isBalanceLoading?: boolean
|
|
@@ -57,6 +61,6 @@ export interface TokenListItemAvatarProps {
|
|
|
57
61
|
|
|
58
62
|
export interface TokenDetailsSheetBase {
|
|
59
63
|
isOpen(): void
|
|
60
|
-
open(address: string, noContractAddress: boolean): void
|
|
64
|
+
open(address: string, noContractAddress: boolean, chainId: number): void
|
|
61
65
|
close(): void
|
|
62
66
|
}
|
|
@@ -2,7 +2,7 @@ import { useCallback } from 'react'
|
|
|
2
2
|
import { useToAddressAutoPopulate } from '../../hooks/useToAddressAutoPopulate.js'
|
|
3
3
|
import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
|
|
4
4
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
5
|
-
import {
|
|
5
|
+
import { useChainOrderStore } from '../../stores/chains/ChainOrderStore.js'
|
|
6
6
|
import type { FormType } from '../../stores/form/types.js'
|
|
7
7
|
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
8
8
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
@@ -21,7 +21,7 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
|
|
|
21
21
|
const emitter = useWidgetEvents()
|
|
22
22
|
const { setFieldValue, getFieldValues } = useFieldActions()
|
|
23
23
|
const autoPopulateToAddress = useToAddressAutoPopulate()
|
|
24
|
-
const
|
|
24
|
+
const setChain = useChainOrderStore((state) => state.setChain)
|
|
25
25
|
|
|
26
26
|
const tokenKey = FormKeyHelper.getTokenKey(formType)
|
|
27
27
|
|
|
@@ -71,7 +71,6 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// If no opposite token is selected, synchronize the opposite chain to match the currently selected chain
|
|
74
|
-
const { setChain } = chainOrderStore.getState()
|
|
75
74
|
if (!selectedOppositeTokenAddress && selectedChainId) {
|
|
76
75
|
setFieldValue(
|
|
77
76
|
FormKeyHelper.getChainKey(oppositeFormType),
|
|
@@ -109,12 +108,12 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
|
|
|
109
108
|
},
|
|
110
109
|
[
|
|
111
110
|
autoPopulateToAddress,
|
|
112
|
-
chainOrderStore,
|
|
113
111
|
disabledUI,
|
|
114
112
|
emitter,
|
|
115
113
|
formType,
|
|
116
114
|
getFieldValues,
|
|
117
115
|
onClick,
|
|
116
|
+
setChain,
|
|
118
117
|
setFieldValue,
|
|
119
118
|
subvariant,
|
|
120
119
|
splitSubvariant,
|
|
@@ -125,7 +125,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
125
125
|
lineHeight: 1.429,
|
|
126
126
|
}}
|
|
127
127
|
>
|
|
128
|
-
{hasRelayerSupport
|
|
128
|
+
{hasRelayerSupport || !combinedFeesUSD
|
|
129
129
|
? t('main.fees.free')
|
|
130
130
|
: t('format.currency', { value: combinedFeesUSD })}
|
|
131
131
|
</Typography>
|
|
@@ -163,7 +163,7 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
163
163
|
variant="body2"
|
|
164
164
|
sx={{ fontWeight: 600, cursor: 'help' }}
|
|
165
165
|
>
|
|
166
|
-
{hasRelayerSupport
|
|
166
|
+
{hasRelayerSupport || !gasCostUSD
|
|
167
167
|
? t('main.fees.free')
|
|
168
168
|
: t('format.currency', {
|
|
169
169
|
value: gasCostUSD,
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '3.
|
|
2
|
+
export const version = '3.30.0'
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ChainType, TokenExtended } from '@lifi/sdk'
|
|
2
|
+
import { useAccount } from '@lifi/wallet-management'
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
import type { FormType } from '../stores/form/types.js'
|
|
5
|
+
import { useChains } from './useChains.js'
|
|
6
|
+
import { useFilteredTokensByBalance } from './useFilteredByTokenBalances.js'
|
|
7
|
+
|
|
8
|
+
export const useAccountsBalancesData = (
|
|
9
|
+
selectedChainId?: number,
|
|
10
|
+
formType?: FormType,
|
|
11
|
+
isAllNetworks?: boolean,
|
|
12
|
+
allTokens?: Record<number, TokenExtended[]>
|
|
13
|
+
) => {
|
|
14
|
+
const { data: accountsWithTokens, isLoading: isAccountsLoading } =
|
|
15
|
+
useAccountsData(selectedChainId, formType, isAllNetworks, allTokens)
|
|
16
|
+
|
|
17
|
+
// Filter out EVM tokens that do not have balances
|
|
18
|
+
const { data: filteredTokens, isLoading: isCachedBalancesLoading } =
|
|
19
|
+
useFilteredTokensByBalance(accountsWithTokens)
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
data: filteredTokens,
|
|
23
|
+
isLoading: isAccountsLoading || isCachedBalancesLoading,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const useAccountsData = (
|
|
28
|
+
selectedChainId?: number,
|
|
29
|
+
formType?: FormType,
|
|
30
|
+
isAllNetworks?: boolean,
|
|
31
|
+
allTokens?: Record<number, TokenExtended[]>
|
|
32
|
+
) => {
|
|
33
|
+
const {
|
|
34
|
+
chains: allChains,
|
|
35
|
+
isLoading: isChainsLoading,
|
|
36
|
+
getChainById,
|
|
37
|
+
} = useChains(formType)
|
|
38
|
+
const currentChain = useMemo(() => {
|
|
39
|
+
return selectedChainId
|
|
40
|
+
? getChainById(selectedChainId, allChains)
|
|
41
|
+
: undefined
|
|
42
|
+
}, [selectedChainId, allChains, getChainById])
|
|
43
|
+
const chains = useMemo(() => {
|
|
44
|
+
return isAllNetworks ? allChains : currentChain ? [currentChain] : undefined
|
|
45
|
+
}, [allChains, isAllNetworks, currentChain])
|
|
46
|
+
|
|
47
|
+
const { accounts: allAccounts, account: currentAccount } = useAccount(
|
|
48
|
+
isAllNetworks ? undefined : { chainType: currentChain?.chainType }
|
|
49
|
+
)
|
|
50
|
+
const accounts = useMemo(() => {
|
|
51
|
+
return isAllNetworks
|
|
52
|
+
? allAccounts
|
|
53
|
+
: currentAccount
|
|
54
|
+
? [currentAccount]
|
|
55
|
+
: undefined
|
|
56
|
+
}, [allAccounts, currentAccount, isAllNetworks])
|
|
57
|
+
|
|
58
|
+
const accountsWithTokens = useMemo(() => {
|
|
59
|
+
if (!chains || !allTokens || !accounts?.length) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
return accounts
|
|
63
|
+
?.filter((account) => account.address)
|
|
64
|
+
.reduce(
|
|
65
|
+
(acc, account) => {
|
|
66
|
+
if (account.address) {
|
|
67
|
+
const accountChains = chains?.filter(
|
|
68
|
+
(chain) => account.chainType === chain?.chainType
|
|
69
|
+
)
|
|
70
|
+
if (accountChains) {
|
|
71
|
+
const chainIdSet = new Set(accountChains.map((chain) => chain.id))
|
|
72
|
+
const filteredTokens = Object.entries(allTokens).reduce(
|
|
73
|
+
(tokenAcc, [chainIdStr, tokens]) => {
|
|
74
|
+
const chainId = Number(chainIdStr)
|
|
75
|
+
if (chainIdSet.has(chainId)) {
|
|
76
|
+
tokenAcc[chainId] = tokens
|
|
77
|
+
}
|
|
78
|
+
return tokenAcc
|
|
79
|
+
},
|
|
80
|
+
{} as { [chainId: number]: TokenExtended[] }
|
|
81
|
+
)
|
|
82
|
+
acc[account.address] = {
|
|
83
|
+
chainType: account.chainType,
|
|
84
|
+
tokens: filteredTokens,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return acc
|
|
89
|
+
},
|
|
90
|
+
{} as Record<
|
|
91
|
+
string,
|
|
92
|
+
{ chainType: ChainType; tokens: Record<number, TokenExtended[]> }
|
|
93
|
+
>
|
|
94
|
+
)
|
|
95
|
+
}, [accounts, chains, allTokens])
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
data: accountsWithTokens,
|
|
99
|
+
isLoading: isChainsLoading,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChainType,
|
|
3
|
+
getWalletBalances,
|
|
4
|
+
type TokenExtended,
|
|
5
|
+
type WalletTokenExtended,
|
|
6
|
+
} from '@lifi/sdk'
|
|
7
|
+
import { useQuery } from '@tanstack/react-query'
|
|
8
|
+
import { useMemo } from 'react'
|
|
9
|
+
import { isSupportedToken } from '../utils/tokenList.js'
|
|
10
|
+
|
|
11
|
+
export const useFilteredTokensByBalance = (
|
|
12
|
+
accountsWithTokens?: Record<
|
|
13
|
+
string,
|
|
14
|
+
{ chainType: ChainType; tokens: Record<number, TokenExtended[]> }
|
|
15
|
+
>
|
|
16
|
+
) => {
|
|
17
|
+
const evmAddress = useMemo(() => {
|
|
18
|
+
const evmAccount = Object.entries(accountsWithTokens ?? {}).find(
|
|
19
|
+
([_, { chainType }]) => chainType === ChainType.EVM
|
|
20
|
+
)
|
|
21
|
+
return evmAccount?.[0]
|
|
22
|
+
}, [accountsWithTokens])
|
|
23
|
+
|
|
24
|
+
const { data: existingBalances, isLoading } = useQuery({
|
|
25
|
+
queryKey: ['existing-evm-balances', evmAddress],
|
|
26
|
+
queryFn: () => getWalletBalances(evmAddress ?? ''),
|
|
27
|
+
enabled: !!evmAddress,
|
|
28
|
+
refetchInterval: 30_000, // 30 seconds
|
|
29
|
+
staleTime: 30_000, // 30 seconds
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const accountsWithFilteredTokens = useMemo(() => {
|
|
33
|
+
if (!accountsWithTokens) {
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Early return if no existing balances - return all tokens
|
|
38
|
+
const result: Record<string, Record<number, TokenExtended[]>> = {}
|
|
39
|
+
if (!existingBalances) {
|
|
40
|
+
for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
|
|
41
|
+
result[address] = tokens
|
|
42
|
+
}
|
|
43
|
+
return result
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const [address, { tokens }] of Object.entries(accountsWithTokens)) {
|
|
47
|
+
result[address] = {}
|
|
48
|
+
|
|
49
|
+
for (const [chainIdStr, chainTokens] of Object.entries(tokens)) {
|
|
50
|
+
const chainId = Number(chainIdStr)
|
|
51
|
+
// Get balances for this specific chain
|
|
52
|
+
const balances = existingBalances?.[chainId]
|
|
53
|
+
// If no balances, RPC all tokens of the chain
|
|
54
|
+
if (!balances?.length) {
|
|
55
|
+
if (chainTokens.length) {
|
|
56
|
+
result[address][chainId] = chainTokens
|
|
57
|
+
}
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Optimize token matching with Set for O(1) lookup
|
|
62
|
+
const balanceSet = new Set(
|
|
63
|
+
balances.map((balance: WalletTokenExtended) =>
|
|
64
|
+
balance.address.toLowerCase()
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// Get tokens that are in chainTokens and have balances
|
|
69
|
+
const filteredTokens = chainTokens.filter((token) => {
|
|
70
|
+
const tokenKey = token.address.toLowerCase()
|
|
71
|
+
return balanceSet.has(tokenKey)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Get tokens that are in balances but not in chainTokens
|
|
75
|
+
const chainTokenSet = new Set(
|
|
76
|
+
chainTokens.map((token) => token.address.toLowerCase())
|
|
77
|
+
)
|
|
78
|
+
const additionalTokens = balances.filter(
|
|
79
|
+
(balance: WalletTokenExtended) => {
|
|
80
|
+
const balanceKey = balance.address.toLowerCase()
|
|
81
|
+
return !chainTokenSet.has(balanceKey) && isSupportedToken(balance)
|
|
82
|
+
}
|
|
83
|
+
) as TokenExtended[]
|
|
84
|
+
|
|
85
|
+
// Combine both sets of tokens - convert WalletTokenExtended to TokenAmount
|
|
86
|
+
const allTokens = [
|
|
87
|
+
...filteredTokens,
|
|
88
|
+
...additionalTokens,
|
|
89
|
+
] as TokenExtended[]
|
|
90
|
+
|
|
91
|
+
if (allTokens.length) {
|
|
92
|
+
result[address][chainId] = allTokens
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return result
|
|
98
|
+
}, [accountsWithTokens, existingBalances])
|
|
99
|
+
|
|
100
|
+
return { data: accountsWithFilteredTokens, isLoading }
|
|
101
|
+
}
|
package/src/hooks/useToken.ts
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
|
-
import { useMemo } from 'react'
|
|
2
1
|
import { useTokenSearch } from './useTokenSearch.js'
|
|
3
2
|
import { useTokens } from './useTokens.js'
|
|
4
3
|
|
|
5
|
-
export const useToken = (
|
|
6
|
-
|
|
4
|
+
export const useToken = (
|
|
5
|
+
chainId?: number,
|
|
6
|
+
tokenAddress?: string,
|
|
7
|
+
latest?: boolean
|
|
8
|
+
) => {
|
|
9
|
+
const { allTokens, isLoading: isTokensLoading } = useTokens()
|
|
7
10
|
|
|
8
|
-
const token =
|
|
9
|
-
|
|
10
|
-
(
|
|
11
|
-
|
|
12
|
-
return token
|
|
13
|
-
}, [chainId, tokenAddress, tokens])
|
|
11
|
+
const token =
|
|
12
|
+
chainId && tokenAddress
|
|
13
|
+
? allTokens?.[chainId]?.find((t) => t.address === tokenAddress)
|
|
14
|
+
: undefined
|
|
14
15
|
|
|
15
|
-
const tokenSearchEnabled =
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const tokenSearchEnabled =
|
|
17
|
+
!!chainId && !!tokenAddress && (latest || (!isTokensLoading && !token))
|
|
18
|
+
|
|
19
|
+
const { token: searchedToken, isLoading: isSearchLoading } = useTokenSearch(
|
|
20
|
+
chainId,
|
|
21
|
+
tokenAddress,
|
|
22
|
+
tokenSearchEnabled
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const resolvedToken = latest
|
|
26
|
+
? (searchedToken ?? token)
|
|
27
|
+
: (token ?? searchedToken)
|
|
18
28
|
|
|
19
29
|
return {
|
|
20
|
-
token:
|
|
21
|
-
isLoading:
|
|
30
|
+
token: resolvedToken,
|
|
31
|
+
isLoading:
|
|
32
|
+
!resolvedToken &&
|
|
33
|
+
(isTokensLoading || (tokenSearchEnabled && isSearchLoading)),
|
|
22
34
|
}
|
|
23
35
|
}
|
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { useAccount } from '@lifi/wallet-management'
|
|
2
|
+
import { useChain } from './useChain.js'
|
|
3
|
+
import { useToken } from './useToken.js'
|
|
4
|
+
import { useTokenBalance } from './useTokenBalance.js'
|
|
4
5
|
|
|
5
6
|
export const useTokenAddressBalance = (
|
|
6
7
|
chainId?: number,
|
|
7
8
|
tokenAddress?: string
|
|
8
9
|
) => {
|
|
9
|
-
const {
|
|
10
|
-
|
|
10
|
+
const { chain, isLoading: isChainLoading } = useChain(chainId)
|
|
11
|
+
const { account } = useAccount({ chainType: chain?.chainType })
|
|
12
|
+
const { token, isLoading: isTokenLoading } = useToken(chainId, tokenAddress)
|
|
11
13
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!token) {
|
|
18
|
-
token = tokens?.find(
|
|
19
|
-
(token) => token.address === tokenAddress && token.chainId === chainId
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
return token as TokenAmount
|
|
23
|
-
}
|
|
24
|
-
}, [chainId, tokenAddress, tokens, tokensWithBalance])
|
|
14
|
+
const {
|
|
15
|
+
token: tokenBalance,
|
|
16
|
+
isLoading: isBalanceLoading,
|
|
17
|
+
refetch,
|
|
18
|
+
} = useTokenBalance(account?.address, token)
|
|
25
19
|
|
|
26
20
|
return {
|
|
27
|
-
token,
|
|
21
|
+
token: tokenBalance,
|
|
28
22
|
chain,
|
|
29
|
-
isLoading: isBalanceLoading,
|
|
23
|
+
isLoading: isBalanceLoading || isChainLoading || isTokenLoading,
|
|
30
24
|
refetch,
|
|
31
25
|
}
|
|
32
26
|
}
|