0xtrails 0.1.2 → 0.1.3
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/dist/analytics.d.ts +68 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-BmFTEOaB.js → ccip-CWd4g9uZ.js} +1 -1
- package/dist/chains.d.ts +9 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/ens.d.ts +7 -0
- package/dist/ens.d.ts.map +1 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/{index-BPsVj7zK.js → index-BTUBzx4R.js} +23624 -21770
- package/dist/index.js +2 -2
- package/dist/lifi.d.ts +4 -0
- package/dist/lifi.d.ts.map +1 -0
- package/dist/mode.d.ts +1 -1
- package/dist/mode.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +3 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +2 -0
- package/dist/prices.d.ts.map +1 -1
- package/dist/relaySdk.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +2 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +3 -3
- package/dist/trails.d.ts.map +1 -1
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts +247 -5
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
- package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
- package/dist/widget/components/ConnectWallet.d.ts +1 -0
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
- package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
- package/dist/widget/components/FundSendForm.d.ts +2 -2
- package/dist/widget/components/FundSendForm.d.ts.map +1 -1
- package/dist/widget/components/PaySendForm.d.ts +2 -2
- package/dist/widget/components/PaySendForm.d.ts.map +1 -1
- package/dist/widget/components/QrCode.d.ts +1 -1
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/RefundAddressInput.d.ts +13 -0
- package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
- package/dist/widget/components/Swap.d.ts +43 -0
- package/dist/widget/components/Swap.d.ts.map +1 -0
- package/dist/widget/components/TokenList.d.ts +0 -2
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TokenSelector.d.ts +26 -0
- package/dist/widget/components/TokenSelector.d.ts.map +1 -0
- package/dist/widget/components/WalletConnect.d.ts.map +1 -1
- package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
- package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
- package/dist/widget/components/WalletList.d.ts.map +1 -1
- package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
- package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useSendForm.d.ts +6 -4
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts +2 -3
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +9 -6
- package/src/aave.ts +13 -13
- package/src/analytics.ts +87 -4
- package/src/chains.ts +45 -7
- package/src/constants.ts +4 -4
- package/src/ens.ts +17 -0
- package/src/error.ts +16 -1
- package/src/lifi.ts +58 -0
- package/src/mode.ts +1 -1
- package/src/morpho.ts +3 -3
- package/src/pools.ts +18 -18
- package/src/prepareSend.ts +35 -3
- package/src/prices.ts +21 -0
- package/src/relaySdk.ts +1 -0
- package/src/relayer.ts +8 -0
- package/src/tokenBalances.ts +3 -0
- package/src/tokens.ts +85 -19
- package/src/trails.ts +2 -2
- package/src/transactions.ts +1 -0
- package/src/wallets.ts +275 -35
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/ChainFilterDropdown.tsx +42 -33
- package/src/widget/components/ChainImage.tsx +1 -1
- package/src/widget/components/ConnectWallet.tsx +92 -128
- package/src/widget/components/DebugScreensDropdown.tsx +3 -0
- package/src/widget/components/FundSendForm.tsx +17 -3
- package/src/widget/components/PaySendForm.tsx +16 -2
- package/src/widget/components/QRCodeDeposit.tsx +1 -1
- package/src/widget/components/QrCode.tsx +277 -16
- package/src/widget/components/Receipt.tsx +1 -1
- package/src/widget/components/RefundAddressInput.tsx +149 -0
- package/src/widget/components/Swap.tsx +648 -0
- package/src/widget/components/TokenList.tsx +27 -363
- package/src/widget/components/TokenSelector.tsx +405 -0
- package/src/widget/components/WalletConnect.tsx +9 -7
- package/src/widget/components/WalletConnectionPending.tsx +157 -0
- package/src/widget/components/WalletList.tsx +6 -5
- package/src/widget/hooks/useAmountUsd.ts +3 -8
- package/src/widget/hooks/useCheckout.ts +3 -2
- package/src/widget/hooks/useSendForm.ts +66 -32
- package/src/widget/hooks/useTokenList.ts +158 -106
- package/src/widget/widget.tsx +335 -72
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useRef } from "react"
|
|
2
|
+
import { getSessionId } from "../../analytics.js"
|
|
2
3
|
|
|
3
4
|
export type CheckoutCallbacks = {
|
|
4
5
|
onCheckoutStart?: (data: { sessionId: string }) => void
|
|
@@ -38,10 +39,10 @@ export function useCheckout({
|
|
|
38
39
|
onCheckoutError,
|
|
39
40
|
onCheckoutStatusUpdate,
|
|
40
41
|
}: UseCheckoutProps): UseCheckoutReturn {
|
|
41
|
-
const sessionIdRef = useRef<string>(
|
|
42
|
+
const sessionIdRef = useRef<string>(getSessionId())
|
|
42
43
|
|
|
43
44
|
const startCheckout = useCallback(() => {
|
|
44
|
-
sessionIdRef.current =
|
|
45
|
+
sessionIdRef.current = getSessionId()
|
|
45
46
|
}, [])
|
|
46
47
|
|
|
47
48
|
const triggerCheckoutStart = useCallback(() => {
|
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
type WalletClient,
|
|
10
10
|
zeroAddress,
|
|
11
11
|
} from "viem"
|
|
12
|
-
import { mainnet } from "viem/chains"
|
|
13
|
-
import { useEnsAddress } from "wagmi"
|
|
14
12
|
import { useAPIClient } from "../../apiClient.js"
|
|
15
13
|
import { getChainInfo, useSupportedChains } from "../../chains.js"
|
|
16
14
|
import { getFullErrorMessage } from "../../error.js"
|
|
@@ -37,6 +35,8 @@ import {
|
|
|
37
35
|
useTokenInfo,
|
|
38
36
|
} from "../../tokens.js"
|
|
39
37
|
import type { CheckoutOnHandlers } from "./useCheckout.js"
|
|
38
|
+
import { useResolveEnsAddress } from "../../ens.js"
|
|
39
|
+
import * as chains from "viem/chains"
|
|
40
40
|
|
|
41
41
|
export interface Token {
|
|
42
42
|
id: number
|
|
@@ -100,6 +100,7 @@ export type UseSendProps = {
|
|
|
100
100
|
toChainId?: number
|
|
101
101
|
toToken?: string
|
|
102
102
|
toCalldata?: string
|
|
103
|
+
refundAddress?: string
|
|
103
104
|
walletClient: WalletClient
|
|
104
105
|
onTransactionStateChange: (transactionStates: TransactionState[]) => void
|
|
105
106
|
onError: (error: Error | string | null) => void
|
|
@@ -109,12 +110,12 @@ export type UseSendProps = {
|
|
|
109
110
|
onSend: (amount: string, recipient: string) => void
|
|
110
111
|
onConfirm: () => void
|
|
111
112
|
onComplete: (result: OnCompleteProps) => void
|
|
112
|
-
selectedToken
|
|
113
|
+
selectedToken?: Token
|
|
113
114
|
setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
|
|
114
115
|
tradeType?: TradeType
|
|
115
116
|
quoteProvider?: string
|
|
116
|
-
fundMethod?: string
|
|
117
|
-
mode?: "pay" | "fund" | "earn"
|
|
117
|
+
fundMethod?: string
|
|
118
|
+
mode?: "pay" | "fund" | "earn" | "swap"
|
|
118
119
|
onNavigateToMeshConnect?: (
|
|
119
120
|
props: {
|
|
120
121
|
toTokenSymbol: string
|
|
@@ -161,7 +162,7 @@ export type UseSendReturn = {
|
|
|
161
162
|
isValidRecipient: boolean
|
|
162
163
|
destTokenPrices: TokenPrice[] | null
|
|
163
164
|
sourceTokenPrices: TokenPrice[] | null
|
|
164
|
-
selectedToken
|
|
165
|
+
selectedToken?: Token
|
|
165
166
|
selectedFeeToken: TokenInfo | null
|
|
166
167
|
setIsChainDropdownOpen: (isOpen: boolean) => void
|
|
167
168
|
setIsTokenDropdownOpen: (isOpen: boolean) => void
|
|
@@ -179,6 +180,7 @@ export function useSendForm({
|
|
|
179
180
|
toChainId, // Custom specified destination chain id
|
|
180
181
|
toToken, // Custom specified destination token address or symbol
|
|
181
182
|
toCalldata, // Custom specified destination calldata
|
|
183
|
+
refundAddress, // Custom specified refund address
|
|
182
184
|
walletClient,
|
|
183
185
|
onTransactionStateChange,
|
|
184
186
|
onError,
|
|
@@ -197,6 +199,19 @@ export function useSendForm({
|
|
|
197
199
|
onNavigateToMeshConnect,
|
|
198
200
|
checkoutOnHandlers,
|
|
199
201
|
}: UseSendProps): UseSendReturn {
|
|
202
|
+
// Auto-set quoteProvider to "lifi" if either from or to chain is etherlink
|
|
203
|
+
const effectiveQuoteProvider = useMemo(() => {
|
|
204
|
+
if (!quoteProvider || quoteProvider === "auto") {
|
|
205
|
+
if (
|
|
206
|
+
selectedToken?.chainId === chains.etherlink.id ||
|
|
207
|
+
toChainId === chains.etherlink.id
|
|
208
|
+
) {
|
|
209
|
+
return "lifi"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return quoteProvider
|
|
213
|
+
}, [quoteProvider, selectedToken?.chainId, toChainId])
|
|
214
|
+
|
|
200
215
|
const [amount, setAmount] = useState(
|
|
201
216
|
tradeType === TradeType.EXACT_INPUT ? "" : (toAmount ?? ""),
|
|
202
217
|
)
|
|
@@ -204,16 +219,10 @@ export function useSendForm({
|
|
|
204
219
|
const [recipient, setRecipient] = useState(toRecipient ?? "")
|
|
205
220
|
const [error, setError] = useState<string | null>(null)
|
|
206
221
|
const { supportedChains } = useSupportedChains()
|
|
207
|
-
const {
|
|
208
|
-
|
|
209
|
-
chainId: mainnet.id,
|
|
210
|
-
query: {
|
|
211
|
-
enabled: !!recipientInput && recipientInput.endsWith(".eth"),
|
|
212
|
-
},
|
|
222
|
+
const { ensAddress } = useResolveEnsAddress({
|
|
223
|
+
textInput: recipientInput,
|
|
213
224
|
})
|
|
214
225
|
|
|
215
|
-
console.log("GENERATED CALLLDATA", toCalldata)
|
|
216
|
-
|
|
217
226
|
useEffect(() => {
|
|
218
227
|
if (ensAddress) {
|
|
219
228
|
setRecipient(ensAddress)
|
|
@@ -287,13 +296,17 @@ export function useSendForm({
|
|
|
287
296
|
}, [errorCustomToken, isCustomToken, isLoadingCustomToken])
|
|
288
297
|
|
|
289
298
|
const defaultDestToken = useMemo(() => {
|
|
299
|
+
if (mode === "swap") {
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
302
|
+
|
|
290
303
|
if (selectedDestinationChain) {
|
|
291
304
|
return supportedTokens.find(
|
|
292
305
|
(token) => token.chainId === selectedDestinationChain.id,
|
|
293
306
|
)
|
|
294
307
|
}
|
|
295
308
|
return supportedTokens?.[0] as TokenInfo
|
|
296
|
-
}, [supportedTokens, selectedDestinationChain])
|
|
309
|
+
}, [supportedTokens, selectedDestinationChain, mode])
|
|
297
310
|
|
|
298
311
|
const [isChainDropdownOpen, setIsChainDropdownOpen] = useState(false)
|
|
299
312
|
const [isTokenDropdownOpen, setIsTokenDropdownOpen] = useState(false)
|
|
@@ -366,7 +379,7 @@ export function useSendForm({
|
|
|
366
379
|
|
|
367
380
|
// Update selectedDestToken when toToken prop changes
|
|
368
381
|
useEffect(() => {
|
|
369
|
-
if (toToken && !isCustomToken) {
|
|
382
|
+
if (toToken && !isCustomToken && selectedDestinationChain?.id) {
|
|
370
383
|
const isToTokenAddress = isAddress(toToken)
|
|
371
384
|
const newToken = supportedTokens.find(
|
|
372
385
|
(token) =>
|
|
@@ -385,7 +398,7 @@ export function useSendForm({
|
|
|
385
398
|
toToken,
|
|
386
399
|
supportedTokens,
|
|
387
400
|
toChainId,
|
|
388
|
-
selectedDestinationChain
|
|
401
|
+
selectedDestinationChain?.id,
|
|
389
402
|
isCustomToken,
|
|
390
403
|
])
|
|
391
404
|
|
|
@@ -406,7 +419,7 @@ export function useSendForm({
|
|
|
406
419
|
setRecipient(toRecipient ?? "")
|
|
407
420
|
}, [toRecipient])
|
|
408
421
|
|
|
409
|
-
const chainInfo = getChainInfo(selectedToken
|
|
422
|
+
const chainInfo = getChainInfo(selectedToken?.chainId)
|
|
410
423
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
411
424
|
const [isWaitingForWalletConfirm, setIsWaitingForWalletConfirm] =
|
|
412
425
|
useState(false)
|
|
@@ -422,11 +435,13 @@ export function useSendForm({
|
|
|
422
435
|
[onTransactionStateChange],
|
|
423
436
|
)
|
|
424
437
|
|
|
425
|
-
const balanceFormatted =
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
438
|
+
const balanceFormatted = selectedToken
|
|
439
|
+
? formatRawAmount(
|
|
440
|
+
selectedToken.balance,
|
|
441
|
+
selectedToken.contractInfo?.decimals,
|
|
442
|
+
)
|
|
443
|
+
: "0"
|
|
444
|
+
const balanceUsdDisplay = selectedToken?.balanceUsdFormatted ?? ""
|
|
430
445
|
const isValidRecipient = Boolean(recipient && isAddress(recipient))
|
|
431
446
|
|
|
432
447
|
// Calculate USD value based on trade type
|
|
@@ -460,7 +475,7 @@ export function useSendForm({
|
|
|
460
475
|
|
|
461
476
|
// Calculate raw amount (in wei/smallest unit)
|
|
462
477
|
const amountRaw = useMemo(() => {
|
|
463
|
-
if (!amount) {
|
|
478
|
+
if (!amount || !selectedToken?.contractInfo?.decimals) {
|
|
464
479
|
return "0"
|
|
465
480
|
}
|
|
466
481
|
|
|
@@ -472,6 +487,12 @@ export function useSendForm({
|
|
|
472
487
|
: selectedDestToken?.decimals
|
|
473
488
|
|
|
474
489
|
if (!decimals) {
|
|
490
|
+
console.warn("[trails-sdk] Missing token decimals for quote", {
|
|
491
|
+
decimals,
|
|
492
|
+
selectedToken,
|
|
493
|
+
selectedDestToken,
|
|
494
|
+
tradeType,
|
|
495
|
+
})
|
|
475
496
|
return "0"
|
|
476
497
|
}
|
|
477
498
|
|
|
@@ -482,8 +503,10 @@ export function useSendForm({
|
|
|
482
503
|
}
|
|
483
504
|
}, [
|
|
484
505
|
amount,
|
|
506
|
+
selectedDestToken,
|
|
507
|
+
selectedToken,
|
|
485
508
|
selectedDestToken?.decimals,
|
|
486
|
-
selectedToken
|
|
509
|
+
selectedToken?.contractInfo?.decimals,
|
|
487
510
|
tradeType,
|
|
488
511
|
])
|
|
489
512
|
|
|
@@ -498,7 +521,8 @@ export function useSendForm({
|
|
|
498
521
|
!selectedDestinationChain ||
|
|
499
522
|
amount === "0" ||
|
|
500
523
|
!amountRaw ||
|
|
501
|
-
amountRaw === "0"
|
|
524
|
+
amountRaw === "0" ||
|
|
525
|
+
!selectedToken
|
|
502
526
|
) {
|
|
503
527
|
setPrepareSendResult(null)
|
|
504
528
|
return
|
|
@@ -609,6 +633,7 @@ export function useSendForm({
|
|
|
609
633
|
originRelayer,
|
|
610
634
|
destinationRelayer,
|
|
611
635
|
destinationCalldata: toCalldata,
|
|
636
|
+
refundAddress,
|
|
612
637
|
dryMode: isDryMode,
|
|
613
638
|
onTransactionStateChange: handleTransactionStateChange,
|
|
614
639
|
sourceTokenPriceUsd,
|
|
@@ -621,7 +646,8 @@ export function useSendForm({
|
|
|
621
646
|
)?.url ?? undefined,
|
|
622
647
|
gasless,
|
|
623
648
|
originNativeTokenPriceUsd: nativeTokenPriceUsd,
|
|
624
|
-
quoteProvider,
|
|
649
|
+
quoteProvider: effectiveQuoteProvider,
|
|
650
|
+
mode,
|
|
625
651
|
fundMethod,
|
|
626
652
|
checkoutOnHandlers,
|
|
627
653
|
}
|
|
@@ -653,6 +679,7 @@ export function useSendForm({
|
|
|
653
679
|
selectedToken?.balance,
|
|
654
680
|
selectedToken?.tokenPriceUsd,
|
|
655
681
|
toCalldata,
|
|
682
|
+
refundAddress,
|
|
656
683
|
paymasterUrls,
|
|
657
684
|
gasless,
|
|
658
685
|
handleTransactionStateChange,
|
|
@@ -662,10 +689,11 @@ export function useSendForm({
|
|
|
662
689
|
selectedDestToken,
|
|
663
690
|
selectedDestinationChain,
|
|
664
691
|
selectedToken,
|
|
665
|
-
|
|
692
|
+
effectiveQuoteProvider,
|
|
666
693
|
fundMethod,
|
|
667
694
|
amountRaw,
|
|
668
695
|
checkoutOnHandlers,
|
|
696
|
+
mode,
|
|
669
697
|
])
|
|
670
698
|
|
|
671
699
|
// Auto-fetch quotes when inputs change (debounced)
|
|
@@ -865,10 +893,12 @@ export function useSendForm({
|
|
|
865
893
|
|
|
866
894
|
// Get button text based on recipient and calldata
|
|
867
895
|
const buttonText = useMemo(() => {
|
|
896
|
+
if (!selectedToken) return "Select a token"
|
|
897
|
+
if (!amount) return "Enter an amount"
|
|
898
|
+
if (!selectedDestToken?.symbol) return "Select a token"
|
|
868
899
|
if (isWaitingForWalletConfirm) return "Waiting for wallet..."
|
|
869
900
|
if (isSubmitting) return "Processing..."
|
|
870
|
-
if (!
|
|
871
|
-
if (!isValidRecipient) return "Enter recipient"
|
|
901
|
+
if (!isValidRecipient) return "Enter a recipient"
|
|
872
902
|
if (isLoadingQuote) return "Getting quote..."
|
|
873
903
|
if (!prepareSendResult) return "No quote available"
|
|
874
904
|
|
|
@@ -886,11 +916,15 @@ export function useSendForm({
|
|
|
886
916
|
)
|
|
887
917
|
|
|
888
918
|
try {
|
|
889
|
-
const isSameChain = selectedToken.chainId === selectedDestinationChain
|
|
919
|
+
const isSameChain = selectedToken.chainId === selectedDestinationChain?.id
|
|
890
920
|
const isSameToken = selectedToken.symbol === selectedDestToken.symbol
|
|
891
921
|
const checksummedRecipient = getAddress(recipient)
|
|
892
922
|
const checksummedAccount = getAddress(account.address)
|
|
893
923
|
|
|
924
|
+
if (mode === "swap") {
|
|
925
|
+
return `Swap ${amountDisplay} ${tokenSymbol}`
|
|
926
|
+
}
|
|
927
|
+
|
|
894
928
|
if (fundMethod === "exchange") {
|
|
895
929
|
return `Continue to Exchange`
|
|
896
930
|
}
|
|
@@ -936,7 +970,7 @@ export function useSendForm({
|
|
|
936
970
|
selectedToken,
|
|
937
971
|
tradeType,
|
|
938
972
|
prepareSendResult?.quote?.originAmountFormatted,
|
|
939
|
-
selectedDestinationChain
|
|
973
|
+
selectedDestinationChain?.id,
|
|
940
974
|
mode,
|
|
941
975
|
fundMethod,
|
|
942
976
|
])
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SequenceIndexerGateway } from "@0xsequence/indexer"
|
|
2
1
|
import { ResourceStatus } from "@0xsequence/indexer"
|
|
3
2
|
import { Address } from "ox"
|
|
4
3
|
import { useEffect, useMemo, useState } from "react"
|
|
@@ -17,6 +16,7 @@ import {
|
|
|
17
16
|
useTokenBalances,
|
|
18
17
|
} from "../../tokenBalances.js"
|
|
19
18
|
import { getFormatttedTokenName, getSupportedTokens } from "../../tokens.js"
|
|
19
|
+
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
20
20
|
|
|
21
21
|
export interface Token {
|
|
22
22
|
id: number
|
|
@@ -50,9 +50,9 @@ export type TokenFormatted = Token &
|
|
|
50
50
|
export type UseTokenListProps = {
|
|
51
51
|
onContinue: (selectedToken: Token) => void
|
|
52
52
|
targetAmountUsd?: number | null
|
|
53
|
-
indexerGatewayClient: SequenceIndexerGateway
|
|
54
53
|
onError: (error: Error | string | null) => void
|
|
55
54
|
fundMethod?: string | null
|
|
55
|
+
allSupportedTokens?: boolean
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export type UseTokenListReturn = {
|
|
@@ -76,27 +76,30 @@ export type UseTokenListReturn = {
|
|
|
76
76
|
export function useTokenList({
|
|
77
77
|
onContinue,
|
|
78
78
|
targetAmountUsd,
|
|
79
|
-
indexerGatewayClient,
|
|
80
79
|
onError,
|
|
81
80
|
fundMethod,
|
|
81
|
+
allSupportedTokens = false,
|
|
82
82
|
}: UseTokenListProps): UseTokenListReturn {
|
|
83
83
|
const [selectedToken, setSelectedToken] = useState<Token | null>(null)
|
|
84
84
|
const [searchQuery, setSearchQuery] = useState("")
|
|
85
|
-
const [
|
|
85
|
+
const [supportedTokens, setSupportedTokens] = useState<any[]>([])
|
|
86
86
|
const [isLoadingSupportedTokens, setIsLoadingSupportedTokens] =
|
|
87
87
|
useState(false)
|
|
88
88
|
const { address } = useAccount()
|
|
89
|
+
const indexerGatewayClient = useIndexerGatewayClient()
|
|
89
90
|
const {
|
|
90
91
|
sortedTokens: allSortedTokens,
|
|
91
92
|
isLoadingSortedTokens,
|
|
92
93
|
balanceError,
|
|
93
94
|
} = useTokenBalances(address as Address.Address, indexerGatewayClient)
|
|
94
95
|
|
|
95
|
-
// Determine loading state based on fund method
|
|
96
|
+
// Determine loading state based on fund method and allSupportedTokens
|
|
96
97
|
const isLoadingTokens =
|
|
97
98
|
fundMethod === "qr-code" || fundMethod === "exchange"
|
|
98
99
|
? isLoadingSupportedTokens
|
|
99
|
-
:
|
|
100
|
+
: allSupportedTokens
|
|
101
|
+
? isLoadingSortedTokens || isLoadingSupportedTokens
|
|
102
|
+
: isLoadingSortedTokens
|
|
100
103
|
|
|
101
104
|
const {
|
|
102
105
|
totalBalanceUsd,
|
|
@@ -111,13 +114,18 @@ export function useTokenList({
|
|
|
111
114
|
const showContinueButton = false
|
|
112
115
|
const { supportedChains: supportedToChains } = useSupportedChains()
|
|
113
116
|
|
|
114
|
-
// Fetch all supported tokens when fundMethod is "qr-code" or "exchange"
|
|
117
|
+
// Fetch all supported tokens when fundMethod is "qr-code" or "exchange" or when allSupportedTokens is true
|
|
115
118
|
useEffect(() => {
|
|
116
|
-
if (
|
|
119
|
+
if (
|
|
120
|
+
fundMethod === "qr-code" ||
|
|
121
|
+
fundMethod === "exchange" ||
|
|
122
|
+
allSupportedTokens
|
|
123
|
+
) {
|
|
117
124
|
setIsLoadingSupportedTokens(true)
|
|
118
125
|
getSupportedTokens()
|
|
119
126
|
.then((tokens) => {
|
|
120
|
-
|
|
127
|
+
// Store supported tokens for use in filteredTokensFormatted
|
|
128
|
+
setSupportedTokens(tokens)
|
|
121
129
|
})
|
|
122
130
|
.catch((error) => {
|
|
123
131
|
console.error("[trails-sdk] Failed to fetch supported tokens:", error)
|
|
@@ -126,17 +134,17 @@ export function useTokenList({
|
|
|
126
134
|
setIsLoadingSupportedTokens(false)
|
|
127
135
|
})
|
|
128
136
|
}
|
|
129
|
-
}, [fundMethod])
|
|
137
|
+
}, [fundMethod, allSupportedTokens])
|
|
130
138
|
|
|
131
139
|
const supportedChainIds = useMemo(() => {
|
|
132
140
|
return new Set(supportedToChains.map((c) => c.id))
|
|
133
141
|
}, [supportedToChains])
|
|
134
142
|
|
|
135
143
|
const sortedTokens = useMemo<Array<TokenBalanceExtended>>(() => {
|
|
136
|
-
// If fundMethod is "qr-code" or "exchange", use
|
|
144
|
+
// If fundMethod is "qr-code" or "exchange", use filtered supported tokens
|
|
137
145
|
if (fundMethod === "qr-code" || fundMethod === "exchange") {
|
|
138
146
|
// Filter to only show specific tokens for QR code and exchange modes
|
|
139
|
-
const filteredTokens =
|
|
147
|
+
const filteredTokens = supportedTokens.filter((token) => {
|
|
140
148
|
const symbol = token.symbol.toUpperCase()
|
|
141
149
|
return ["ETH", "POL", "USDC", "USDT", "DAI", "BAT", "WETH"].includes(
|
|
142
150
|
symbol,
|
|
@@ -199,16 +207,10 @@ export function useTokenList({
|
|
|
199
207
|
}
|
|
200
208
|
return true
|
|
201
209
|
})
|
|
202
|
-
}, [
|
|
203
|
-
allSortedTokens,
|
|
204
|
-
supportedChainIds,
|
|
205
|
-
fundMethod,
|
|
206
|
-
allSupportedTokens,
|
|
207
|
-
address,
|
|
208
|
-
])
|
|
210
|
+
}, [allSortedTokens, supportedChainIds, fundMethod, supportedTokens, address])
|
|
209
211
|
|
|
210
212
|
useEffect(() => {
|
|
211
|
-
if (onError) {
|
|
213
|
+
if (onError && balanceError) {
|
|
212
214
|
onError(balanceError)
|
|
213
215
|
}
|
|
214
216
|
}, [balanceError, onError])
|
|
@@ -359,95 +361,145 @@ export function useTokenList({
|
|
|
359
361
|
}, [sortedTokens, searchQuery])
|
|
360
362
|
|
|
361
363
|
const filteredTokensFormatted = useMemo(() => {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
imageContractAddress =
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
isSufficientBalance =
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
364
|
+
// Get base formatted tokens
|
|
365
|
+
const baseFormattedTokens = filteredTokens.map(
|
|
366
|
+
(token: TokenBalanceExtended): TokenFormatted => {
|
|
367
|
+
const isNative = !("contractAddress" in token)
|
|
368
|
+
const chainInfo = getChainInfo(token.chainId)
|
|
369
|
+
const nativeSymbol = chainInfo?.nativeCurrency.symbol || "ETH"
|
|
370
|
+
const tokenSymbol = isNative
|
|
371
|
+
? nativeSymbol
|
|
372
|
+
: token.contractInfo?.symbol || "???"
|
|
373
|
+
const contractAddress = isNative ? zeroAddress : token.contractAddress
|
|
374
|
+
let imageContractAddress = contractAddress
|
|
375
|
+
if (tokenSymbol === "WETH") {
|
|
376
|
+
imageContractAddress = zeroAddress
|
|
377
|
+
}
|
|
378
|
+
const imageUrl = `https://assets.sequence.info/images/tokens/small/${token.chainId}/${imageContractAddress}.webp`
|
|
379
|
+
const currentTokenName =
|
|
380
|
+
(token as TokenBalanceWithPrice).contractInfo?.name || ""
|
|
381
|
+
const tokenName = getFormatttedTokenName(
|
|
382
|
+
currentTokenName,
|
|
383
|
+
tokenSymbol,
|
|
384
|
+
token.chainId,
|
|
385
|
+
)
|
|
386
|
+
const formattedBalance = formatRawAmount(
|
|
387
|
+
token.balance,
|
|
388
|
+
isNative ? 18 : token.contractInfo?.decimals,
|
|
389
|
+
)
|
|
390
|
+
const priceUsd = Number(token.price?.value) ?? 0
|
|
391
|
+
const balanceUsdFormatted = token.balanceUsdFormatted ?? ""
|
|
392
|
+
const decimals = isNative ? 18 : (token.contractInfo?.decimals ?? 18)
|
|
393
|
+
let isSufficientBalance = true
|
|
394
|
+
if (targetAmountUsd) {
|
|
395
|
+
isSufficientBalance = (token.balanceUsd ?? 0) >= targetAmountUsd
|
|
396
|
+
}
|
|
397
|
+
const chainName = chainInfo?.name || ""
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
...token,
|
|
401
|
+
id: token.chainId,
|
|
402
|
+
contractInfo: {
|
|
403
|
+
...(token as TokenBalanceWithPrice).contractInfo,
|
|
404
|
+
chainId: Number(token.chainId),
|
|
405
|
+
source: (token as TokenBalanceWithPrice).contractInfo?.source || "",
|
|
406
|
+
type: (token as TokenBalanceWithPrice).contractInfo?.type || "",
|
|
407
|
+
logoURI:
|
|
408
|
+
(token as TokenBalanceWithPrice).contractInfo?.logoURI || "",
|
|
409
|
+
deployed:
|
|
410
|
+
(token as TokenBalanceWithPrice).contractInfo?.deployed || false,
|
|
411
|
+
bytecodeHash:
|
|
412
|
+
(token as TokenBalanceWithPrice).contractInfo?.bytecodeHash || "",
|
|
413
|
+
updatedAt:
|
|
414
|
+
(token as TokenBalanceWithPrice).contractInfo?.updatedAt || "",
|
|
415
|
+
queuedAt:
|
|
416
|
+
(token as TokenBalanceWithPrice).contractInfo?.queuedAt || "",
|
|
417
|
+
extensions: (token as TokenBalanceWithPrice).contractInfo
|
|
418
|
+
?.extensions || {
|
|
419
|
+
link: "",
|
|
420
|
+
description: "",
|
|
421
|
+
categories: [],
|
|
422
|
+
ogImage: "",
|
|
423
|
+
ogName: "",
|
|
424
|
+
originChainId: 0,
|
|
425
|
+
originAddress: "",
|
|
426
|
+
blacklist: false,
|
|
427
|
+
verified: false,
|
|
428
|
+
featureIndex: 0,
|
|
429
|
+
verifiedBy: "",
|
|
430
|
+
featured: false,
|
|
431
|
+
},
|
|
432
|
+
status:
|
|
433
|
+
(token as TokenBalanceWithPrice).contractInfo?.status ||
|
|
434
|
+
ResourceStatus.NOT_AVAILABLE,
|
|
435
|
+
address: contractAddress,
|
|
436
|
+
name: tokenName,
|
|
437
|
+
symbol: tokenSymbol,
|
|
438
|
+
decimals: decimals,
|
|
426
439
|
},
|
|
427
|
-
status:
|
|
428
|
-
(token as TokenBalanceWithPrice).contractInfo?.status ||
|
|
429
|
-
ResourceStatus.NOT_AVAILABLE,
|
|
430
|
-
address: contractAddress,
|
|
431
440
|
name: tokenName,
|
|
432
441
|
symbol: tokenSymbol,
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
442
|
+
balanceFormatted: formattedBalance,
|
|
443
|
+
imageUrl,
|
|
444
|
+
chainId: token.chainId,
|
|
445
|
+
contractAddress: contractAddress,
|
|
446
|
+
balanceUsdFormatted,
|
|
447
|
+
tokenPriceUsd: priceUsd,
|
|
448
|
+
isNative: isNative,
|
|
449
|
+
tokenName: tokenName,
|
|
450
|
+
priceUsd: priceUsd,
|
|
451
|
+
isSufficientBalance,
|
|
452
|
+
chainName,
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
// If allSupportedTokens is true, combine with supported tokens
|
|
458
|
+
if (allSupportedTokens) {
|
|
459
|
+
// Create a set of existing token keys for quick lookup
|
|
460
|
+
const existingTokenKeys = new Set(
|
|
461
|
+
baseFormattedTokens.map(
|
|
462
|
+
(token) => `${token.chainId}-${token.contractAddress.toLowerCase()}`,
|
|
463
|
+
),
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
// Add supported tokens that don't exist in the base list
|
|
467
|
+
const additionalSupportedTokens = supportedTokens
|
|
468
|
+
.filter((supportedToken) => {
|
|
469
|
+
const key = `${supportedToken.chainId}-${supportedToken.contractAddress.toLowerCase()}`
|
|
470
|
+
return !existingTokenKeys.has(key)
|
|
471
|
+
})
|
|
472
|
+
.map((supportedToken) => {
|
|
473
|
+
const tokenName = getFormatttedTokenName(
|
|
474
|
+
supportedToken.name,
|
|
475
|
+
supportedToken.symbol,
|
|
476
|
+
supportedToken.chainId,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
...supportedToken,
|
|
481
|
+
tokenName: tokenName,
|
|
482
|
+
contractInfo: {
|
|
483
|
+
decimals: supportedToken.decimals,
|
|
484
|
+
symbol: supportedToken.symbol,
|
|
485
|
+
name: supportedToken.name,
|
|
486
|
+
},
|
|
487
|
+
// Add minimal required properties to make it work
|
|
488
|
+
balance: "",
|
|
489
|
+
balanceFormatted: "",
|
|
490
|
+
balanceUsdFormatted: "",
|
|
491
|
+
priceUsd: 0,
|
|
492
|
+
isSufficientBalance: true,
|
|
493
|
+
// Use any type to bypass strict type checking for now
|
|
494
|
+
} as any
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
// Combine lists with base tokens taking precedence
|
|
498
|
+
return [...baseFormattedTokens, ...additionalSupportedTokens]
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return baseFormattedTokens
|
|
502
|
+
}, [filteredTokens, targetAmountUsd, allSupportedTokens, supportedTokens])
|
|
451
503
|
|
|
452
504
|
const showInsufficientBalance = useMemo(() => {
|
|
453
505
|
return (
|