0xtrails 0.5.0 → 0.6.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/dist/analytics.d.ts +8 -3
- package/dist/analytics.d.ts.map +1 -1
- package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
- package/dist/cctp.d.ts +0 -149
- package/dist/cctp.d.ts.map +1 -1
- package/dist/chains.d.ts +28 -3
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/estimate.d.ts.map +1 -1
- package/dist/fees.d.ts.map +1 -1
- package/dist/gasless.d.ts +12 -0
- package/dist/gasless.d.ts.map +1 -1
- package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +108 -107
- package/dist/indexerClient.d.ts +2 -2
- package/dist/intents.d.ts +0 -17
- package/dist/intents.d.ts.map +1 -1
- package/dist/mutations.d.ts.map +1 -1
- package/dist/paymasterSend.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +1 -1
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/sendUserOp.d.ts +0 -18
- package/dist/sendUserOp.d.ts.map +1 -1
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts +10 -8
- package/dist/tokens.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
- package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
- package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
- package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
- package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
- package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
- package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/index.d.ts +0 -1
- package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
- package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
- package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
- package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
- package/dist/transactionIntent/types.d.ts +6 -19
- package/dist/transactionIntent/types.d.ts.map +1 -1
- package/dist/transactionIntent/utils/index.d.ts +0 -1
- package/dist/transactionIntent/utils/index.d.ts.map +1 -1
- package/dist/transactions.d.ts +2 -20
- package/dist/transactions.d.ts.map +1 -1
- package/dist/utils.d.ts +8 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/walletUtils.d.ts +21 -0
- package/dist/walletUtils.d.ts.map +1 -0
- package/dist/wallets.d.ts +33 -240
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
- package/dist/widget/components/FeeOption.d.ts +8 -13
- package/dist/widget/components/FeeOption.d.ts.map +1 -1
- package/dist/widget/components/FeeOptions.d.ts +11 -5
- package/dist/widget/components/FeeOptions.d.ts.map +1 -1
- package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
- package/dist/widget/components/Pay.d.ts.map +1 -1
- package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
- package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
- package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
- package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
- package/dist/widget/components/QrCode.d.ts.map +1 -1
- package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
- package/dist/widget/components/Receipt.d.ts.map +1 -1
- package/dist/widget/components/ScreenHeader.d.ts +1 -1
- package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
- package/dist/widget/components/Toast.d.ts.map +1 -1
- package/dist/widget/components/TokenImage.d.ts.map +1 -1
- package/dist/widget/css/compiled.css +1 -1
- package/dist/widget/hooks/useCheckout.d.ts +15 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
- package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
- package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
- package/dist/widget/hooks/useQuote.d.ts +5 -8
- package/dist/widget/hooks/useQuote.d.ts.map +1 -1
- package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
- package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
- package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +6 -15
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +1 -1
- package/dist/widget/providers/TrailsProvider.d.ts +23 -12
- package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
- package/dist/widget/widget.d.ts +11 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/analytics.ts +53 -21
- package/src/cctp.ts +0 -1016
- package/src/chains.ts +93 -39
- package/src/config.ts +24 -6
- package/src/constants.ts +1 -4
- package/src/contractUtils.ts +6 -6
- package/src/estimate.ts +3 -6
- package/src/fees.ts +5 -10
- package/src/gasless.ts +45 -0
- package/src/index.ts +7 -6
- package/src/indexerClient.ts +2 -2
- package/src/intents.ts +52 -206
- package/src/mutations.ts +3 -2
- package/src/paymasterSend.ts +2 -5
- package/src/prepareSend.ts +9 -12
- package/src/sendUserOp.ts +3 -64
- package/src/tokenBalances.ts +2 -1
- package/src/tokens.ts +62 -133
- package/src/trailsClient.ts +1 -1
- package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
- package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
- package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
- package/src/transactionIntent/execution/transactionState.ts +2 -2
- package/src/transactionIntent/handlers/crossChain.ts +165 -385
- package/src/transactionIntent/handlers/index.ts +0 -1
- package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
- package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
- package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
- package/src/transactionIntent/types.ts +6 -27
- package/src/transactionIntent/utils/index.ts +0 -1
- package/src/transactions.ts +6 -203
- package/src/umd.tsx +1 -3
- package/src/utils.ts +28 -8
- package/src/walletUtils.ts +42 -0
- package/src/wallets.ts +361 -203
- package/src/widget/compiled.css +1 -1
- package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
- package/src/widget/components/AccountSettings.tsx +17 -17
- package/src/widget/components/ChainList.tsx +3 -3
- package/src/widget/components/ClassicSwap.tsx +19 -10
- package/src/widget/components/ConfigDisplay.tsx +1 -1
- package/src/widget/components/FeeOption.tsx +63 -20
- package/src/widget/components/FeeOptions.tsx +54 -123
- package/src/widget/components/NativeGasOption.tsx +3 -1
- package/src/widget/components/Pay.tsx +18 -11
- package/src/widget/components/PoolDeposit.tsx +23 -10
- package/src/widget/components/QRCodeDeposit.tsx +50 -30
- package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
- package/src/widget/components/QrCode.tsx +188 -233
- package/src/widget/components/QuoteDetails.tsx +48 -2
- package/src/widget/components/Receipt.tsx +5 -2
- package/src/widget/components/ScreenHeader.tsx +10 -8
- package/src/widget/components/Toast.tsx +10 -0
- package/src/widget/components/TokenImage.tsx +56 -13
- package/src/widget/hooks/useCheckout.ts +71 -0
- package/src/widget/hooks/useCurrentScreen.tsx +1 -0
- package/src/widget/hooks/useDebugScreens.ts +5 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
- package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
- package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
- package/src/widget/hooks/useQuote.ts +16 -17
- package/src/widget/hooks/useRecentTokens.ts +2 -1
- package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
- package/src/widget/hooks/useSendForm.ts +172 -47
- package/src/widget/hooks/useTokenList.ts +15 -2
- package/src/widget/providers/TrailsProvider.tsx +53 -25
- package/src/widget/widget.tsx +119 -48
- package/dist/cctpqueue.d.ts +0 -18
- package/dist/cctpqueue.d.ts.map +0 -1
- package/dist/preconditions.d.ts +0 -12
- package/dist/preconditions.d.ts.map +0 -1
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
- package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
- package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
- package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
- package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
- package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
- package/src/cctpqueue.ts +0 -69
- package/src/preconditions.ts +0 -47
- package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
- package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
- package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { TokenPrice } from "@0xsequence/trails-api"
|
|
1
|
+
import type { TokenPrice, FeeOption } from "@0xsequence/trails-api"
|
|
2
|
+
import { JsonEncode } from "@0xsequence/trails-api"
|
|
2
3
|
import type React from "react"
|
|
3
4
|
import { useCallback, useEffect, useMemo, useState, useRef } from "react"
|
|
4
5
|
import {
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
TradeType,
|
|
18
19
|
type PrepareSendReturn,
|
|
19
20
|
type PrepareSendQuote,
|
|
20
|
-
type SelectedFeeToken,
|
|
21
21
|
} from "../../prepareSend.js"
|
|
22
22
|
import type { TransactionState } from "../../transactions.js"
|
|
23
23
|
import { getTokenPrice, useTokenPrices, normalizeNumber } from "../../prices.js"
|
|
@@ -42,7 +42,10 @@ import { getIsContract } from "../../contractUtils.js"
|
|
|
42
42
|
import { useTrailsClient } from "../../trailsClient.js"
|
|
43
43
|
import { useTrails } from "../providers/TrailsProvider.js"
|
|
44
44
|
import { useTokenList } from "./useTokenList.js"
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
useSelectedFeeOption,
|
|
47
|
+
type ProcessedFeeOption,
|
|
48
|
+
} from "./useSelectedFeeOption.js"
|
|
46
49
|
|
|
47
50
|
export interface Token {
|
|
48
51
|
id: number
|
|
@@ -118,17 +121,6 @@ export type UseSendProps = {
|
|
|
118
121
|
refetchTrigger?: number
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
export type FeeOption = {
|
|
122
|
-
tokenAddress: string
|
|
123
|
-
tokenSymbol: string
|
|
124
|
-
tokenDecimals: number
|
|
125
|
-
amount: string
|
|
126
|
-
amountUSD: number
|
|
127
|
-
notEnoughBalance?: boolean
|
|
128
|
-
tokenImageUrl?: string
|
|
129
|
-
chainId?: number
|
|
130
|
-
}
|
|
131
|
-
|
|
132
124
|
export type UseSendReturn = {
|
|
133
125
|
amount: string
|
|
134
126
|
amountRaw: string
|
|
@@ -153,8 +145,8 @@ export type UseSendReturn = {
|
|
|
153
145
|
setRecipientInput: (recipientInput: string) => void
|
|
154
146
|
setSelectedDestinationChain: (chain: ChainInfo) => void
|
|
155
147
|
setSelectedDestToken: (token: TokenInfo) => void
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
setSelectedFeeOption: (token: FeeOption | null) => void
|
|
149
|
+
processedFeeOptions: ProcessedFeeOption[]
|
|
158
150
|
supportedTokens: SupportedToken[]
|
|
159
151
|
supportedChains: ChainInfo[]
|
|
160
152
|
ensAddress: string | null
|
|
@@ -164,7 +156,7 @@ export type UseSendReturn = {
|
|
|
164
156
|
destTokenPrices: TokenPrice[] | null
|
|
165
157
|
sourceTokenPrices: TokenPrice[] | null
|
|
166
158
|
selectedToken?: Token
|
|
167
|
-
|
|
159
|
+
selectedFeeOption: FeeOption | null
|
|
168
160
|
setIsChainDropdownOpen: (isOpen: boolean) => void
|
|
169
161
|
setIsTokenDropdownOpen: (isOpen: boolean) => void
|
|
170
162
|
toAmountFormatted: string
|
|
@@ -434,7 +426,7 @@ export function useSendForm({
|
|
|
434
426
|
}, [selectedDestToken, defaultDestToken])
|
|
435
427
|
|
|
436
428
|
const trailsClient = useTrailsClient()
|
|
437
|
-
const { sequenceIndexerUrl } = useTrails()
|
|
429
|
+
const { trailsApiKey, sequenceIndexerUrl } = useTrails()
|
|
438
430
|
|
|
439
431
|
// Get user's token balances for balance checking
|
|
440
432
|
const { filteredTokensFormatted } = useTokenList({
|
|
@@ -547,6 +539,10 @@ export function useSendForm({
|
|
|
547
539
|
const [isLoadingQuote, setIsLoadingQuote] = useState(false)
|
|
548
540
|
const [prepareSendResult, setPrepareSendResult] =
|
|
549
541
|
useState<PrepareSendReturn | null>(null)
|
|
542
|
+
const latestResolvedIntentIdRef = useRef<string | null>(null)
|
|
543
|
+
const quoteInputsFingerprintRef = useRef<string | null>(null)
|
|
544
|
+
const preparedQuoteFingerprintRef = useRef<string | null>(null)
|
|
545
|
+
|
|
550
546
|
// Create a stable callback for transaction state changes
|
|
551
547
|
const handleTransactionStateChange = useCallback(
|
|
552
548
|
(transactionStates: TransactionState[]) => {
|
|
@@ -585,11 +581,11 @@ export function useSendForm({
|
|
|
585
581
|
|
|
586
582
|
// Use the fee token selection hook
|
|
587
583
|
const {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
processedFeeOptions
|
|
591
|
-
|
|
592
|
-
} =
|
|
584
|
+
selectedFeeOption,
|
|
585
|
+
setSelectedFeeOption,
|
|
586
|
+
processedFeeOptions,
|
|
587
|
+
setFeeOptions,
|
|
588
|
+
} = useSelectedFeeOption()
|
|
593
589
|
|
|
594
590
|
const [isRecipientContract, setIsRecipientContract] = useState(false)
|
|
595
591
|
const [isSenderContractOnOrigin, setIsSenderContractOnOrigin] =
|
|
@@ -686,6 +682,34 @@ export function useSendForm({
|
|
|
686
682
|
tradeType,
|
|
687
683
|
])
|
|
688
684
|
|
|
685
|
+
const buildQuoteInputsFingerprint = useCallback(
|
|
686
|
+
() =>
|
|
687
|
+
JSON.stringify({
|
|
688
|
+
originTokenAddress: selectedToken?.contractAddress?.toLowerCase() ?? "",
|
|
689
|
+
originChainId: selectedToken?.chainId ?? "",
|
|
690
|
+
destinationTokenSymbol: selectedDestToken?.symbol ?? "",
|
|
691
|
+
destinationChainId: selectedDestinationChain?.id ?? "",
|
|
692
|
+
destinationTokenAddress: destinationTokenAddress?.toLowerCase() ?? "",
|
|
693
|
+
amount: amount || "",
|
|
694
|
+
amountRaw: amountRaw || "",
|
|
695
|
+
recipient: recipient?.toLowerCase() ?? "",
|
|
696
|
+
tradeType,
|
|
697
|
+
toCalldata: toCalldata ?? "",
|
|
698
|
+
}),
|
|
699
|
+
[
|
|
700
|
+
amount,
|
|
701
|
+
amountRaw,
|
|
702
|
+
destinationTokenAddress,
|
|
703
|
+
recipient,
|
|
704
|
+
selectedDestinationChain?.id,
|
|
705
|
+
selectedDestToken?.symbol,
|
|
706
|
+
selectedToken?.chainId,
|
|
707
|
+
selectedToken?.contractAddress,
|
|
708
|
+
toCalldata,
|
|
709
|
+
tradeType,
|
|
710
|
+
],
|
|
711
|
+
)
|
|
712
|
+
|
|
689
713
|
// Get quote automatically when inputs change
|
|
690
714
|
const getQuote = useCallback(async () => {
|
|
691
715
|
// Only get quote if all required inputs are present
|
|
@@ -715,9 +739,16 @@ export function useSendForm({
|
|
|
715
739
|
)
|
|
716
740
|
setQuoteError(null)
|
|
717
741
|
setPrepareSendResult(null)
|
|
742
|
+
preparedQuoteFingerprintRef.current = null
|
|
718
743
|
return
|
|
719
744
|
}
|
|
720
745
|
|
|
746
|
+
latestResolvedIntentIdRef.current = null
|
|
747
|
+
let requestFingerprint = quoteInputsFingerprintRef.current
|
|
748
|
+
if (!requestFingerprint) {
|
|
749
|
+
requestFingerprint = buildQuoteInputsFingerprint()
|
|
750
|
+
quoteInputsFingerprintRef.current = requestFingerprint
|
|
751
|
+
}
|
|
721
752
|
try {
|
|
722
753
|
setIsLoadingQuote(true)
|
|
723
754
|
setError(null)
|
|
@@ -735,6 +766,8 @@ export function useSendForm({
|
|
|
735
766
|
tradeType,
|
|
736
767
|
})
|
|
737
768
|
setPrepareSendResult(null)
|
|
769
|
+
preparedQuoteFingerprintRef.current = null
|
|
770
|
+
latestResolvedIntentIdRef.current = null
|
|
738
771
|
setIsLoadingQuote(false)
|
|
739
772
|
return
|
|
740
773
|
}
|
|
@@ -849,31 +882,66 @@ export function useSendForm({
|
|
|
849
882
|
mode,
|
|
850
883
|
fundMethod,
|
|
851
884
|
checkoutOnHandlers,
|
|
852
|
-
|
|
885
|
+
selectedFeeOption: selectedFeeOption ?? null,
|
|
853
886
|
walletId,
|
|
854
887
|
sequenceIndexerUrl,
|
|
888
|
+
sequenceProjectAccessKey: trailsApiKey,
|
|
855
889
|
}
|
|
856
890
|
|
|
857
891
|
logger.console.log(
|
|
858
|
-
"[trails-sdk] [FEE-SELECT] getQuote using
|
|
892
|
+
"[trails-sdk] [FEE-SELECT] getQuote using selectedFeeOption:",
|
|
859
893
|
{
|
|
860
|
-
|
|
894
|
+
selectedFeeOption,
|
|
861
895
|
},
|
|
862
896
|
)
|
|
863
897
|
|
|
864
898
|
const result = await prepareSend(options)
|
|
865
899
|
|
|
900
|
+
if (requestFingerprint !== quoteInputsFingerprintRef.current) {
|
|
901
|
+
logger.console.log(
|
|
902
|
+
"[trails-sdk] Ignoring stale quote result (fingerprint mismatch)",
|
|
903
|
+
{
|
|
904
|
+
requestFingerprint,
|
|
905
|
+
currentFingerprint: quoteInputsFingerprintRef.current,
|
|
906
|
+
},
|
|
907
|
+
)
|
|
908
|
+
return
|
|
909
|
+
}
|
|
910
|
+
|
|
866
911
|
logger.console.log("[trails-sdk] prepareSend quote:", result.quote)
|
|
867
912
|
|
|
913
|
+
// Track the intentId returned by the most recent quote so we never execute a stale CommitIntent
|
|
914
|
+
// even when the quote inputs (fingerprint) are identical between retries.
|
|
915
|
+
const quoteIntentId = result.quote.intentId ?? null
|
|
916
|
+
|
|
917
|
+
logger.console.log("[trails-sdk] Resolved quote intentId:", quoteIntentId)
|
|
918
|
+
|
|
919
|
+
latestResolvedIntentIdRef.current = quoteIntentId
|
|
868
920
|
setPrepareSendResult(result)
|
|
921
|
+
preparedQuoteFingerprintRef.current = requestFingerprint
|
|
869
922
|
setIsLoadingQuote(false)
|
|
870
923
|
} catch (error) {
|
|
924
|
+
if (requestFingerprint !== quoteInputsFingerprintRef.current) {
|
|
925
|
+
logger.console.log(
|
|
926
|
+
"[trails-sdk] Ignoring quote error (fingerprint mismatch)",
|
|
927
|
+
{
|
|
928
|
+
requestFingerprint,
|
|
929
|
+
currentFingerprint: quoteInputsFingerprintRef.current,
|
|
930
|
+
},
|
|
931
|
+
)
|
|
932
|
+
return
|
|
933
|
+
}
|
|
934
|
+
|
|
871
935
|
logger.console.error("[trails-sdk] Error getting quote:", error)
|
|
872
|
-
|
|
936
|
+
const errorMessage = getFullErrorMessage(error)
|
|
937
|
+
setQuoteError(errorMessage)
|
|
873
938
|
setPrepareSendResult(null)
|
|
939
|
+
preparedQuoteFingerprintRef.current = null
|
|
940
|
+
latestResolvedIntentIdRef.current = null
|
|
874
941
|
setIsLoadingQuote(false)
|
|
875
942
|
}
|
|
876
943
|
}, [
|
|
944
|
+
trailsApiKey,
|
|
877
945
|
tradeType,
|
|
878
946
|
isDryMode,
|
|
879
947
|
account,
|
|
@@ -904,8 +972,9 @@ export function useSendForm({
|
|
|
904
972
|
amountRaw,
|
|
905
973
|
checkoutOnHandlers,
|
|
906
974
|
mode,
|
|
907
|
-
|
|
975
|
+
selectedFeeOption,
|
|
908
976
|
walletId,
|
|
977
|
+
buildQuoteInputsFingerprint,
|
|
909
978
|
])
|
|
910
979
|
|
|
911
980
|
// Auto-fetch quotes when inputs change (debounced)
|
|
@@ -917,12 +986,29 @@ export function useSendForm({
|
|
|
917
986
|
!destinationTokenAddress ||
|
|
918
987
|
!isValidRecipient ||
|
|
919
988
|
!selectedDestToken?.symbol ||
|
|
920
|
-
!selectedDestinationChain?.id
|
|
989
|
+
!selectedDestinationChain?.id ||
|
|
990
|
+
!selectedToken
|
|
921
991
|
) {
|
|
922
992
|
setPrepareSendResult(null)
|
|
993
|
+
latestResolvedIntentIdRef.current = null
|
|
994
|
+
quoteInputsFingerprintRef.current = null
|
|
995
|
+
preparedQuoteFingerprintRef.current = null
|
|
996
|
+
setIsLoadingQuote(false)
|
|
923
997
|
return
|
|
924
998
|
}
|
|
925
999
|
|
|
1000
|
+
const nextQuoteInputsFingerprint = buildQuoteInputsFingerprint()
|
|
1001
|
+
|
|
1002
|
+
// Fingerprint all quote-driving inputs so we can invalidate debounced quotes immediately when
|
|
1003
|
+
// the user switches tokens; this fixes the stale quote so that it stays loading until the new quote is ready.
|
|
1004
|
+
if (quoteInputsFingerprintRef.current !== nextQuoteInputsFingerprint) {
|
|
1005
|
+
quoteInputsFingerprintRef.current = nextQuoteInputsFingerprint
|
|
1006
|
+
latestResolvedIntentIdRef.current = null
|
|
1007
|
+
setPrepareSendResult(null)
|
|
1008
|
+
preparedQuoteFingerprintRef.current = null
|
|
1009
|
+
setIsLoadingQuote(true)
|
|
1010
|
+
}
|
|
1011
|
+
|
|
926
1012
|
const timeoutId = setTimeout(() => {
|
|
927
1013
|
getQuote()
|
|
928
1014
|
}, 500) // Debounce by 500ms
|
|
@@ -936,12 +1022,15 @@ export function useSendForm({
|
|
|
936
1022
|
selectedDestinationChain?.id,
|
|
937
1023
|
toCalldata,
|
|
938
1024
|
refetchTrigger,
|
|
1025
|
+
amountRaw,
|
|
1026
|
+
tradeType,
|
|
939
1027
|
selectedToken?.contractAddress,
|
|
940
1028
|
selectedToken?.chainId,
|
|
941
1029
|
selectedToken?.balance,
|
|
942
1030
|
selectedToken?.tokenPriceUsd,
|
|
943
1031
|
recipient, // Add recipient to trigger quote re-fetch when it changes
|
|
944
|
-
//
|
|
1032
|
+
// selectedFeeOption is passed to send() at execution time, not needed here
|
|
1033
|
+
buildQuoteInputsFingerprint,
|
|
945
1034
|
])
|
|
946
1035
|
|
|
947
1036
|
// Calculate destination amount from quote if available
|
|
@@ -971,12 +1060,12 @@ export function useSendForm({
|
|
|
971
1060
|
},
|
|
972
1061
|
)
|
|
973
1062
|
|
|
974
|
-
|
|
1063
|
+
setFeeOptions(
|
|
975
1064
|
apiFeeOptions,
|
|
976
1065
|
selectedToken?.chainId,
|
|
977
1066
|
filteredTokensFormattedRef.current,
|
|
978
1067
|
)
|
|
979
|
-
}, [prepareSendResult, selectedToken?.chainId,
|
|
1068
|
+
}, [prepareSendResult, selectedToken?.chainId, setFeeOptions])
|
|
980
1069
|
|
|
981
1070
|
const processSend = useCallback(async () => {
|
|
982
1071
|
try {
|
|
@@ -985,6 +1074,41 @@ export function useSendForm({
|
|
|
985
1074
|
return
|
|
986
1075
|
}
|
|
987
1076
|
|
|
1077
|
+
const currentQuoteInputsFingerprint = buildQuoteInputsFingerprint()
|
|
1078
|
+
if (
|
|
1079
|
+
!preparedQuoteFingerprintRef.current ||
|
|
1080
|
+
preparedQuoteFingerprintRef.current !== currentQuoteInputsFingerprint
|
|
1081
|
+
) {
|
|
1082
|
+
logger.console.warn(
|
|
1083
|
+
"[trails-sdk] Blocking send because quote fingerprint is stale",
|
|
1084
|
+
{
|
|
1085
|
+
prepared: preparedQuoteFingerprintRef.current,
|
|
1086
|
+
current: currentQuoteInputsFingerprint,
|
|
1087
|
+
},
|
|
1088
|
+
)
|
|
1089
|
+
setPrepareSendResult(null)
|
|
1090
|
+
preparedQuoteFingerprintRef.current = null
|
|
1091
|
+
latestResolvedIntentIdRef.current = null
|
|
1092
|
+
setIsLoadingQuote(true)
|
|
1093
|
+
return
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
const quoteIntentId = prepareSendResult.quote.intentId ?? null
|
|
1097
|
+
if (
|
|
1098
|
+
quoteIntentId &&
|
|
1099
|
+
latestResolvedIntentIdRef.current !== quoteIntentId
|
|
1100
|
+
) {
|
|
1101
|
+
logger.console.error(
|
|
1102
|
+
"[trails-sdk] Quote intentId has changed, waiting for latest quote",
|
|
1103
|
+
{
|
|
1104
|
+
current: latestResolvedIntentIdRef.current,
|
|
1105
|
+
prepareSend: quoteIntentId,
|
|
1106
|
+
},
|
|
1107
|
+
)
|
|
1108
|
+
setError("Quote is updating. Please wait for the latest quote.")
|
|
1109
|
+
return
|
|
1110
|
+
}
|
|
1111
|
+
|
|
988
1112
|
setError(null)
|
|
989
1113
|
setIsSubmitting(true)
|
|
990
1114
|
|
|
@@ -1063,28 +1187,28 @@ export function useSendForm({
|
|
|
1063
1187
|
"[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] handleSend called, about to call send()",
|
|
1064
1188
|
)
|
|
1065
1189
|
logger.console.log(
|
|
1066
|
-
"[trails-sdk] [FEE-SELECT] [GASLESS-FLOW]
|
|
1190
|
+
"[trails-sdk] [FEE-SELECT] [GASLESS-FLOW] selectedFeeOption value at send() call time:",
|
|
1067
1191
|
{
|
|
1068
|
-
|
|
1069
|
-
isNull:
|
|
1070
|
-
isUndefined:
|
|
1071
|
-
type: typeof
|
|
1072
|
-
stringified:
|
|
1192
|
+
selectedFeeOption,
|
|
1193
|
+
isNull: selectedFeeOption === null,
|
|
1194
|
+
isUndefined: selectedFeeOption === undefined,
|
|
1195
|
+
type: typeof selectedFeeOption,
|
|
1196
|
+
stringified: JsonEncode(selectedFeeOption),
|
|
1073
1197
|
},
|
|
1074
1198
|
)
|
|
1075
1199
|
// Wait for full send to complete
|
|
1076
1200
|
const {
|
|
1077
1201
|
depositUserTxnReceipt,
|
|
1078
|
-
|
|
1079
|
-
|
|
1202
|
+
originIntentTransaction,
|
|
1203
|
+
destinationIntentTransaction,
|
|
1080
1204
|
} = await send({
|
|
1081
1205
|
onOriginSend,
|
|
1082
|
-
|
|
1206
|
+
selectedFeeOption: selectedFeeOption ?? null, // Pass current value at execution time
|
|
1083
1207
|
})
|
|
1084
1208
|
logger.console.log("[trails-sdk] send() completed, receipts:", {
|
|
1085
1209
|
depositUserTxnReceipt,
|
|
1086
|
-
|
|
1087
|
-
|
|
1210
|
+
originIntentTransaction,
|
|
1211
|
+
destinationIntentTransaction,
|
|
1088
1212
|
})
|
|
1089
1213
|
|
|
1090
1214
|
// Move to receipt screen
|
|
@@ -1140,7 +1264,8 @@ export function useSendForm({
|
|
|
1140
1264
|
onError,
|
|
1141
1265
|
fundMethod,
|
|
1142
1266
|
onNavigateToMeshConnect,
|
|
1143
|
-
|
|
1267
|
+
selectedFeeOption, // Include so handleSend captures latest value
|
|
1268
|
+
buildQuoteInputsFingerprint,
|
|
1144
1269
|
])
|
|
1145
1270
|
|
|
1146
1271
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -1329,8 +1454,8 @@ export function useSendForm({
|
|
|
1329
1454
|
setRecipientInput,
|
|
1330
1455
|
setSelectedDestinationChain,
|
|
1331
1456
|
setSelectedDestToken,
|
|
1332
|
-
|
|
1333
|
-
|
|
1457
|
+
setSelectedFeeOption,
|
|
1458
|
+
processedFeeOptions,
|
|
1334
1459
|
supportedTokens,
|
|
1335
1460
|
supportedChains,
|
|
1336
1461
|
ensAddress: ensAddress ?? null,
|
|
@@ -1340,7 +1465,7 @@ export function useSendForm({
|
|
|
1340
1465
|
destTokenPrices: destTokenPrices ?? null,
|
|
1341
1466
|
sourceTokenPrices: sourceTokenPrices ?? null,
|
|
1342
1467
|
selectedToken,
|
|
1343
|
-
|
|
1468
|
+
selectedFeeOption: selectedFeeOption ?? null,
|
|
1344
1469
|
setIsChainDropdownOpen,
|
|
1345
1470
|
setIsTokenDropdownOpen,
|
|
1346
1471
|
toAmountFormatted: quotedDestinationAmount,
|
|
@@ -15,7 +15,11 @@ import {
|
|
|
15
15
|
useHasSufficientBalanceUsd,
|
|
16
16
|
useTokenBalances,
|
|
17
17
|
} from "../../tokenBalances.js"
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
getFormatttedTokenName,
|
|
20
|
+
useSupportedTokens,
|
|
21
|
+
useGetTokenImageUrl,
|
|
22
|
+
} from "../../tokens.js"
|
|
19
23
|
import { useIndexerGatewayClient } from "../../indexerClient.js"
|
|
20
24
|
import { useTrailsClient } from "../../trailsClient.js"
|
|
21
25
|
import { useTokenPrices } from "../../prices.js"
|
|
@@ -137,6 +141,7 @@ export function useTokenList({
|
|
|
137
141
|
const { address } = useAccount()
|
|
138
142
|
const indexerGatewayClient = useIndexerGatewayClient()
|
|
139
143
|
const trailsClient = useTrailsClient()
|
|
144
|
+
const { getTokenImageUrl } = useGetTokenImageUrl()
|
|
140
145
|
const {
|
|
141
146
|
sortedTokens: allSortedTokens,
|
|
142
147
|
isLoadingSortedTokens,
|
|
@@ -536,7 +541,14 @@ export function useTokenList({
|
|
|
536
541
|
if (tokenSymbol === "WETH") {
|
|
537
542
|
imageContractAddress = zeroAddress
|
|
538
543
|
}
|
|
539
|
-
const
|
|
544
|
+
const fallbackImageUrl =
|
|
545
|
+
(token as TokenBalanceWithPrice).contractInfo?.logoURI || ""
|
|
546
|
+
const imageUrl = getTokenImageUrl({
|
|
547
|
+
chainId: token.chainId,
|
|
548
|
+
contractAddress: imageContractAddress,
|
|
549
|
+
symbol: tokenSymbol,
|
|
550
|
+
fallbackImageUrl: fallbackImageUrl || undefined,
|
|
551
|
+
})
|
|
540
552
|
const currentTokenName =
|
|
541
553
|
(token as TokenBalanceWithPrice).contractInfo?.name || ""
|
|
542
554
|
const tokenName = getFormatttedTokenName(
|
|
@@ -771,6 +783,7 @@ export function useTokenList({
|
|
|
771
783
|
supportedTokens,
|
|
772
784
|
supportedTokenPrices,
|
|
773
785
|
searchQuery,
|
|
786
|
+
getTokenImageUrl,
|
|
774
787
|
])
|
|
775
788
|
|
|
776
789
|
const showInsufficientBalance = useMemo(() => {
|
|
@@ -1,70 +1,98 @@
|
|
|
1
1
|
import { SequenceHooksProvider } from "@0xsequence/hooks"
|
|
2
|
-
import { createContext, useContext, useMemo } from "react"
|
|
2
|
+
import { createContext, useContext, useMemo, useEffect } from "react"
|
|
3
3
|
import type { ComponentProps } from "react"
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_SLIPPAGE_TOLERANCE,
|
|
6
6
|
DEFAULT_WALLETCONNECT_PROJECT_ID,
|
|
7
7
|
PROD_SEQUENCE_INDEXER_URL,
|
|
8
8
|
PROD_SEQUENCE_NODE_GATEWAY_URL,
|
|
9
|
-
|
|
9
|
+
PROD_SEQUENCE_METADATA_URL,
|
|
10
10
|
PROD_TRAILS_API_URL,
|
|
11
11
|
} from "../../constants.js"
|
|
12
|
+
import {
|
|
13
|
+
declareCommitHash,
|
|
14
|
+
setGlobalDebug,
|
|
15
|
+
updateGlobalConfig,
|
|
16
|
+
} from "../../config.js"
|
|
12
17
|
import { logger } from "../../logger.js"
|
|
18
|
+
import { getQueryParam } from "../../queryParams.js"
|
|
13
19
|
|
|
14
20
|
export type SequenceEnv = "prod" | "dev" | "local" | "cors-anywhere"
|
|
15
21
|
|
|
16
22
|
export interface TrailsConfig {
|
|
17
23
|
trailsApiKey: string
|
|
18
|
-
trailsApiUrl
|
|
19
|
-
sequenceIndexerUrl
|
|
20
|
-
sequenceNodeGatewayUrl
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
trailsApiUrl: string
|
|
25
|
+
sequenceIndexerUrl: string
|
|
26
|
+
sequenceNodeGatewayUrl: string
|
|
27
|
+
sequenceMetadataUrl: string
|
|
28
|
+
walletConnectProjectId: string
|
|
29
|
+
slippageTolerance: string | number
|
|
30
|
+
debug: boolean
|
|
31
|
+
commitHash: string | undefined
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TrailsProviderProps {
|
|
35
|
+
children: React.ReactNode
|
|
36
|
+
config: {
|
|
37
|
+
trailsApiKey: string
|
|
38
|
+
trailsApiUrl?: string
|
|
39
|
+
sequenceIndexerUrl?: string
|
|
40
|
+
sequenceNodeGatewayUrl?: string
|
|
41
|
+
sequenceMetadataUrl?: string
|
|
42
|
+
walletConnectProjectId?: string
|
|
43
|
+
slippageTolerance?: string | number
|
|
44
|
+
debug?: boolean
|
|
45
|
+
}
|
|
25
46
|
}
|
|
26
47
|
|
|
27
48
|
export const TrailsContext = createContext<TrailsConfig | null>(null)
|
|
28
49
|
|
|
29
|
-
export const TrailsProvider = ({
|
|
30
|
-
children,
|
|
31
|
-
config = {
|
|
32
|
-
trailsApiKey: PROD_SEQUENCE_PROJECT_ACCESS_KEY,
|
|
33
|
-
},
|
|
34
|
-
}: {
|
|
35
|
-
children: React.ReactNode
|
|
36
|
-
config?: TrailsConfig
|
|
37
|
-
}) => {
|
|
50
|
+
export const TrailsProvider = ({ children, config }: TrailsProviderProps) => {
|
|
38
51
|
// Populate the trails config with default values if not provided
|
|
39
52
|
const trailsConfig: TrailsConfig = useMemo(() => {
|
|
40
53
|
return {
|
|
41
|
-
trailsApiKey: config.trailsApiKey
|
|
54
|
+
trailsApiKey: config.trailsApiKey,
|
|
42
55
|
trailsApiUrl: config.trailsApiUrl || PROD_TRAILS_API_URL,
|
|
43
56
|
sequenceIndexerUrl:
|
|
44
57
|
config.sequenceIndexerUrl || PROD_SEQUENCE_INDEXER_URL,
|
|
45
58
|
sequenceNodeGatewayUrl:
|
|
46
59
|
config.sequenceNodeGatewayUrl || PROD_SEQUENCE_NODE_GATEWAY_URL,
|
|
60
|
+
sequenceMetadataUrl:
|
|
61
|
+
config.sequenceMetadataUrl || PROD_SEQUENCE_METADATA_URL,
|
|
47
62
|
walletConnectProjectId:
|
|
48
63
|
config.walletConnectProjectId || DEFAULT_WALLETCONNECT_PROJECT_ID,
|
|
49
|
-
slippageTolerance: config.slippageTolerance
|
|
50
|
-
debug: config.debug ?? false,
|
|
51
|
-
commitHash:
|
|
64
|
+
slippageTolerance: config.slippageTolerance ?? DEFAULT_SLIPPAGE_TOLERANCE,
|
|
65
|
+
debug: (config.debug ?? false) || getQueryParam("debug") === "true",
|
|
66
|
+
commitHash: declareCommitHash(),
|
|
52
67
|
} satisfies TrailsConfig
|
|
53
68
|
}, [
|
|
54
69
|
config.trailsApiKey,
|
|
55
70
|
config.trailsApiUrl,
|
|
56
71
|
config.sequenceIndexerUrl,
|
|
72
|
+
config.sequenceMetadataUrl,
|
|
57
73
|
config.sequenceNodeGatewayUrl,
|
|
58
74
|
config.walletConnectProjectId,
|
|
59
75
|
config.slippageTolerance,
|
|
60
76
|
config.debug,
|
|
61
|
-
config.commitHash,
|
|
62
77
|
])
|
|
63
78
|
|
|
79
|
+
// Set global debug mode for backward compatibility with getDebug()
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
setGlobalDebug(trailsConfig.debug ?? false)
|
|
82
|
+
}, [trailsConfig.debug])
|
|
83
|
+
|
|
84
|
+
// Update global config when trails config changes (temporary solution)
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
updateGlobalConfig({
|
|
87
|
+
trailsApiKey: trailsConfig.trailsApiKey,
|
|
88
|
+
sequenceNodeGatewayUrl: trailsConfig.sequenceNodeGatewayUrl,
|
|
89
|
+
})
|
|
90
|
+
}, [trailsConfig.trailsApiKey, trailsConfig.sequenceNodeGatewayUrl])
|
|
91
|
+
|
|
64
92
|
// Create a sequence hooks config based on the trails config
|
|
65
93
|
const sequenceHooksConfig = useMemo(() => {
|
|
66
94
|
return {
|
|
67
|
-
projectAccessKey: trailsConfig.trailsApiKey,
|
|
95
|
+
projectAccessKey: trailsConfig.trailsApiKey || "",
|
|
68
96
|
env: {
|
|
69
97
|
apiUrl: trailsConfig.trailsApiUrl,
|
|
70
98
|
indexerUrl: trailsConfig.sequenceIndexerUrl,
|
|
@@ -84,7 +112,7 @@ export const TrailsProvider = ({
|
|
|
84
112
|
)
|
|
85
113
|
}
|
|
86
114
|
|
|
87
|
-
export const useTrails = () => {
|
|
115
|
+
export const useTrails = (): Readonly<TrailsConfig> => {
|
|
88
116
|
const context = useContext(TrailsContext)
|
|
89
117
|
|
|
90
118
|
if (!context) {
|