0xtrails 0.9.2 → 0.9.4

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.
Files changed (79) hide show
  1. package/dist/{ccip-g6lDdnrD.js → ccip-lAtzqne5.js} +1 -1
  2. package/dist/config.d.ts +1 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/error.d.ts.map +1 -1
  7. package/dist/{index-D-QngA_s.js → index-D5AG6huo.js} +22290 -21786
  8. package/dist/index.js +3 -3
  9. package/dist/intents.d.ts +1 -1
  10. package/dist/intents.d.ts.map +1 -1
  11. package/dist/mutations.d.ts +5 -2
  12. package/dist/mutations.d.ts.map +1 -1
  13. package/dist/tokens.d.ts.map +1 -1
  14. package/dist/transactionIntent/constants.d.ts +1 -0
  15. package/dist/transactionIntent/constants.d.ts.map +1 -1
  16. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +3 -1
  17. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  18. package/dist/transactionIntent/deposits/standardDeposit.d.ts +4 -1
  19. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  20. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  21. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  22. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  23. package/dist/transactionIntent/types.d.ts +2 -0
  24. package/dist/transactionIntent/types.d.ts.map +1 -1
  25. package/dist/transactionIntent/utils/resilientDepositTracker.d.ts +25 -0
  26. package/dist/transactionIntent/utils/resilientDepositTracker.d.ts.map +1 -0
  27. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  28. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  29. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  30. package/dist/widget/components/DynamicInputStyles.d.ts +2 -2
  31. package/dist/widget/components/Earn.d.ts.map +1 -1
  32. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  33. package/dist/widget/components/Fund.d.ts.map +1 -1
  34. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  35. package/dist/widget/components/Receipt.d.ts.map +1 -1
  36. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  37. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  38. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  39. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  40. package/dist/widget/css/compiled.css +1 -1
  41. package/dist/widget/hooks/useQuote.d.ts +2 -0
  42. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  43. package/dist/widget/index.js +1 -1
  44. package/dist/widget/providers/TrailsProvider.d.ts +2 -0
  45. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  46. package/dist/widget/widget.d.ts +1 -0
  47. package/dist/widget/widget.d.ts.map +1 -1
  48. package/package.json +2 -2
  49. package/src/config.ts +1 -0
  50. package/src/constants.ts +1 -0
  51. package/src/error.ts +6 -1
  52. package/src/intents.ts +22 -1
  53. package/src/prices.ts +1 -1
  54. package/src/tokens.ts +4 -3
  55. package/src/transactionIntent/constants.ts +2 -0
  56. package/src/transactionIntent/deposits/depositOrchestrator.ts +7 -0
  57. package/src/transactionIntent/deposits/standardDeposit.ts +194 -37
  58. package/src/transactionIntent/handlers/crossChain.ts +152 -105
  59. package/src/transactionIntent/handlers/sameChainSameToken.ts +1 -0
  60. package/src/transactionIntent/quote/normalizeQuote.ts +7 -4
  61. package/src/transactionIntent/types.ts +2 -0
  62. package/src/transactionIntent/utils/resilientDepositTracker.ts +281 -0
  63. package/src/widget/compiled.css +1 -1
  64. package/src/widget/components/AccountIntentTransactionHistory.tsx +170 -87
  65. package/src/widget/components/ClassicSwap.tsx +7 -1
  66. package/src/widget/components/ConfigDisplay.tsx +5 -0
  67. package/src/widget/components/Earn.tsx +14 -1
  68. package/src/widget/components/EarnPools.tsx +180 -59
  69. package/src/widget/components/Fund.tsx +3 -1
  70. package/src/widget/components/PoolWithdraw.tsx +1 -1
  71. package/src/widget/components/QuoteDetails.tsx +12 -35
  72. package/src/widget/components/Receipt.tsx +66 -40
  73. package/src/widget/components/SlippageToleranceSettings.tsx +86 -44
  74. package/src/widget/components/TransactionDetails.tsx +138 -218
  75. package/src/widget/components/UserPreferences.tsx +114 -41
  76. package/src/widget/components/WalletConnect.tsx +111 -48
  77. package/src/widget/hooks/useQuote.ts +389 -352
  78. package/src/widget/providers/TrailsProvider.tsx +5 -0
  79. package/src/widget/widget.tsx +2 -0
@@ -191,6 +191,8 @@ export type Quote = {
191
191
  originTokenRate: string
192
192
  routeProviders: RouteProviderInfo[]
193
193
  destinationTokenRate: string
194
+ originTokenPriceUsd: number
195
+ destinationTokenPriceUsd: number
194
196
  gasCostUsd: number | null
195
197
  gasCostUsdDisplay: string
196
198
  gasCost: string
@@ -585,394 +587,429 @@ export function useQuote({
585
587
  return null
586
588
  }
587
589
 
588
- // Get token balance using async method
589
- let balances: any[] = []
590
+ // Create a 20-second timeout
591
+ const timeoutId = setTimeout(() => {
592
+ logger.console.warn(
593
+ "[trails-sdk] [useQuote] Quote request timed out after 20 seconds",
594
+ )
595
+ abortControllerRef.current.abort()
596
+ }, 20000) // 20 seconds
597
+
590
598
  try {
591
- const result = await getTokenBalancesWithPrices({
592
- account: walletClient.account!.address,
593
- indexerGatewayClient,
594
- trailsClient,
595
- })
596
- balances = result.balances
597
- } catch (balanceError) {
598
- const balanceErrorMessage = getFullErrorMessage(balanceError)
599
- const isCorsError =
600
- balanceErrorMessage.includes("Cross-Origin") ||
601
- balanceErrorMessage.includes("CORS") ||
602
- balanceErrorMessage.includes("Same Origin Policy")
603
- const isNetworkError =
604
- balanceErrorMessage.includes("fetch failed") ||
605
- balanceErrorMessage.includes("network")
599
+ // Get token balance using async method
600
+ let balances: any[] = []
601
+ try {
602
+ const result = await getTokenBalancesWithPrices({
603
+ account: walletClient.account!.address,
604
+ indexerGatewayClient,
605
+ trailsClient,
606
+ })
607
+ balances = result.balances
608
+ } catch (balanceError) {
609
+ const balanceErrorMessage = getFullErrorMessage(balanceError)
610
+ const isCorsError =
611
+ balanceErrorMessage.includes("Cross-Origin") ||
612
+ balanceErrorMessage.includes("CORS") ||
613
+ balanceErrorMessage.includes("Same Origin Policy")
614
+ const isNetworkError =
615
+ balanceErrorMessage.includes("fetch failed") ||
616
+ balanceErrorMessage.includes("network")
606
617
 
607
- if (isCorsError || isNetworkError) {
608
- logger.console.warn(
609
- "[trails-sdk] [useQuote] Network or CORS error fetching balances, proceeding with quote using zero balance:",
610
- {
611
- error: balanceErrorMessage,
612
- account: walletClient.account!.address,
613
- },
614
- )
615
- // Continue with empty balances instead of crashing
616
- balances = []
617
- } else {
618
- throw balanceError
618
+ if (isCorsError || isNetworkError) {
619
+ logger.console.warn(
620
+ "[trails-sdk] [useQuote] Network or CORS error fetching balances, proceeding with quote using zero balance:",
621
+ {
622
+ error: balanceErrorMessage,
623
+ account: walletClient.account!.address,
624
+ },
625
+ )
626
+ // Continue with empty balances instead of crashing
627
+ balances = []
628
+ } else {
629
+ throw balanceError
630
+ }
619
631
  }
620
- }
621
632
 
622
- const originTokenBalance = balances.find(
623
- (b) =>
624
- b.chainId === fromChainId &&
625
- (b.contractAddress?.toLowerCase() ===
626
- fromTokenAddress.toLowerCase() ||
627
- (!b.contractAddress && fromTokenAddress === zeroAddress)),
628
- )
629
-
630
- const originTokenBalanceAmount = originTokenBalance?.balance ?? "0"
633
+ const originTokenBalance = balances.find(
634
+ (b) =>
635
+ b.chainId === fromChainId &&
636
+ (b.contractAddress?.toLowerCase() ===
637
+ fromTokenAddress.toLowerCase() ||
638
+ (!b.contractAddress && fromTokenAddress === zeroAddress)),
639
+ )
631
640
 
632
- // Note: Disable this check for now to allow fetching a quote even when the origin balance is zero
633
- // if (originTokenBalanceAmount === "0") {
634
- // return null
635
- // }
641
+ const originTokenBalanceAmount = originTokenBalance?.balance ?? "0"
636
642
 
637
- // logger.console.log("supportedTokens", supportedTokens)
643
+ // Note: Disable this check for now to allow fetching a quote even when the origin balance is zero
644
+ // if (originTokenBalanceAmount === "0") {
645
+ // return null
646
+ // }
638
647
 
639
- // Get native token price for origin chain (for gas cost calculation)
640
- let originNativeTokenPriceUsd = 0
641
- try {
642
- const originChain = getChainInfo(fromChainId)
643
- const nativeTokenSymbol = originChain?.nativeCurrency?.symbol ?? ""
644
- if (nativeTokenSymbol) {
645
- const nativePrice = await getTokenPrice(trailsClient, {
646
- tokenSymbol: nativeTokenSymbol,
647
- tokenAddress: zeroAddress,
648
- chainId: fromChainId,
649
- })
650
- originNativeTokenPriceUsd = nativePrice?.priceUsd ?? 0
651
- logger.console.log(
652
- "[trails-sdk] [useQuote] Origin native token price:",
653
- {
654
- chainId: fromChainId,
655
- symbol: nativeTokenSymbol,
656
- priceUsd: originNativeTokenPriceUsd,
657
- },
658
- )
659
- }
660
- } catch (error) {
661
- logger.console.error(
662
- "[trails-sdk] [useQuote] Error getting origin native token price:",
663
- error,
664
- )
665
- }
648
+ // logger.console.log("supportedTokens", supportedTokens)
666
649
 
667
- // Helper function to fetch decimals on-chain if not found in token list
668
- const fetchDecimalsOnChain = async (
669
- tokenAddress: string,
670
- chainId: number,
671
- ): Promise<number | null> => {
650
+ // Get native token price for origin chain (for gas cost calculation)
651
+ let originNativeTokenPriceUsd = 0
672
652
  try {
673
- // Use the hook-based RPC clients that are already available
674
- const publicClient =
675
- chainId === fromChainId
676
- ? originPublicClient
677
- : chainId === toChainId
678
- ? destinationPublicClient
679
- : null
680
-
681
- if (!publicClient) {
682
- logger.console.warn(
683
- `[trails-sdk] No RPC client available for chain ${chainId}`,
653
+ const originChain = getChainInfo(fromChainId)
654
+ const nativeTokenSymbol = originChain?.nativeCurrency?.symbol ?? ""
655
+ if (nativeTokenSymbol) {
656
+ const nativePrice = await getTokenPrice(trailsClient, {
657
+ tokenSymbol: nativeTokenSymbol,
658
+ tokenAddress: zeroAddress,
659
+ chainId: fromChainId,
660
+ })
661
+ originNativeTokenPriceUsd = nativePrice?.priceUsd ?? 0
662
+ logger.console.log(
663
+ "[trails-sdk] [useQuote] Origin native token price:",
664
+ {
665
+ chainId: fromChainId,
666
+ symbol: nativeTokenSymbol,
667
+ priceUsd: originNativeTokenPriceUsd,
668
+ },
684
669
  )
685
- return null
686
- }
687
-
688
- // For native tokens, return 18 decimals
689
- if (tokenAddress.toLowerCase() === zeroAddress.toLowerCase()) {
690
- const chainInfo = getChainInfo(chainId)
691
- return chainInfo?.nativeCurrency.decimals ?? 18
692
670
  }
693
-
694
- const decimals = await publicClient.readContract({
695
- address: tokenAddress as `0x${string}`,
696
- abi: erc20Abi,
697
- functionName: "decimals",
698
- })
699
-
700
- logger.console.log(
701
- `[trails-sdk] Fetched decimals on-chain for token ${tokenAddress} on chain ${chainId}: ${decimals}`,
702
- )
703
- return decimals
704
671
  } catch (error) {
705
672
  logger.console.error(
706
- `[trails-sdk] Error fetching decimals on-chain for token ${tokenAddress} on chain ${chainId}:`,
673
+ "[trails-sdk] [useQuote] Error getting origin native token price:",
707
674
  error,
708
675
  )
709
- return null
710
676
  }
711
- }
712
677
 
713
- const originToken = supportedTokens?.find(
714
- (token) =>
715
- token.contractAddress?.toLowerCase() ===
716
- fromTokenAddress?.toLowerCase() && token.chainId === fromChainId,
717
- )
718
- const destinationToken = supportedTokens?.find(
719
- (token) =>
720
- token.contractAddress?.toLowerCase() ===
721
- toTokenAddress?.toLowerCase() && token.chainId === toChainId,
722
- )
678
+ // Helper function to fetch decimals on-chain if not found in token list
679
+ const fetchDecimalsOnChain = async (
680
+ tokenAddress: string,
681
+ chainId: number,
682
+ ): Promise<number | null> => {
683
+ try {
684
+ // Use the hook-based RPC clients that are already available
685
+ const publicClient =
686
+ chainId === fromChainId
687
+ ? originPublicClient
688
+ : chainId === toChainId
689
+ ? destinationPublicClient
690
+ : null
723
691
 
724
- let sourceTokenDecimals = originToken?.decimals
725
- if (!sourceTokenDecimals && fromTokenAddress && fromChainId) {
726
- logger.console.warn(
727
- "[trails-sdk] [useQuote] Source token decimals not found in token list, fetching on-chain:",
728
- {
729
- originToken,
730
- fromTokenAddress,
731
- fromChainId,
732
- },
692
+ if (!publicClient) {
693
+ logger.console.warn(
694
+ `[trails-sdk] No RPC client available for chain ${chainId}`,
695
+ )
696
+ return null
697
+ }
698
+
699
+ // For native tokens, return 18 decimals
700
+ if (tokenAddress.toLowerCase() === zeroAddress.toLowerCase()) {
701
+ const chainInfo = getChainInfo(chainId)
702
+ return chainInfo?.nativeCurrency.decimals ?? 18
703
+ }
704
+
705
+ const decimals = await publicClient.readContract({
706
+ address: tokenAddress as `0x${string}`,
707
+ abi: erc20Abi,
708
+ functionName: "decimals",
709
+ })
710
+
711
+ logger.console.log(
712
+ `[trails-sdk] Fetched decimals on-chain for token ${tokenAddress} on chain ${chainId}: ${decimals}`,
713
+ )
714
+ return decimals
715
+ } catch (error) {
716
+ logger.console.error(
717
+ `[trails-sdk] Error fetching decimals on-chain for token ${tokenAddress} on chain ${chainId}:`,
718
+ error,
719
+ )
720
+ return null
721
+ }
722
+ }
723
+
724
+ const originToken = supportedTokens?.find(
725
+ (token) =>
726
+ token.contractAddress?.toLowerCase() ===
727
+ fromTokenAddress?.toLowerCase() &&
728
+ token.chainId === fromChainId,
733
729
  )
734
- const onChainDecimals = await fetchDecimalsOnChain(
735
- fromTokenAddress,
736
- fromChainId,
730
+ const destinationToken = supportedTokens?.find(
731
+ (token) =>
732
+ token.contractAddress?.toLowerCase() ===
733
+ toTokenAddress?.toLowerCase() && token.chainId === toChainId,
737
734
  )
738
- if (onChainDecimals !== null) {
739
- sourceTokenDecimals = onChainDecimals
740
- }
741
- }
742
- if (!sourceTokenDecimals) {
743
- logger.console.error(
744
- "[trails-sdk] [useQuote] Source token decimals not found:",
745
- {
746
- originToken,
735
+
736
+ let sourceTokenDecimals = originToken?.decimals
737
+ if (!sourceTokenDecimals && fromTokenAddress && fromChainId) {
738
+ logger.console.warn(
739
+ "[trails-sdk] [useQuote] Source token decimals not found in token list, fetching on-chain:",
740
+ {
741
+ originToken,
742
+ fromTokenAddress,
743
+ fromChainId,
744
+ },
745
+ )
746
+ const onChainDecimals = await fetchDecimalsOnChain(
747
747
  fromTokenAddress,
748
748
  fromChainId,
749
- },
750
- )
751
- throw new Error("Source token decimals not found")
752
- }
753
- let destinationTokenDecimals = destinationToken?.decimals
754
- if (!destinationTokenDecimals && toTokenAddress && toChainId) {
755
- logger.console.warn(
756
- "[trails-sdk] Destination token decimals not found in token list, fetching on-chain:",
757
- {
758
- destinationToken,
759
- toTokenAddress,
760
- toChainId,
761
- },
762
- )
763
- const onChainDecimals = await fetchDecimalsOnChain(
764
- toTokenAddress,
765
- toChainId,
766
- )
767
- if (onChainDecimals !== null) {
768
- destinationTokenDecimals = onChainDecimals
749
+ )
750
+ if (onChainDecimals !== null) {
751
+ sourceTokenDecimals = onChainDecimals
752
+ }
769
753
  }
770
- }
771
- if (!destinationTokenDecimals) {
772
- logger.console.error(
773
- "[trails-sdk] Destination token decimals not found:",
774
- {
775
- destinationToken,
754
+ if (!sourceTokenDecimals) {
755
+ logger.console.error(
756
+ "[trails-sdk] [useQuote] Source token decimals not found:",
757
+ {
758
+ originToken,
759
+ fromTokenAddress,
760
+ fromChainId,
761
+ },
762
+ )
763
+ throw new Error("Source token decimals not found")
764
+ }
765
+ let destinationTokenDecimals = destinationToken?.decimals
766
+ if (!destinationTokenDecimals && toTokenAddress && toChainId) {
767
+ logger.console.warn(
768
+ "[trails-sdk] Destination token decimals not found in token list, fetching on-chain:",
769
+ {
770
+ destinationToken,
771
+ toTokenAddress,
772
+ toChainId,
773
+ },
774
+ )
775
+ const onChainDecimals = await fetchDecimalsOnChain(
776
776
  toTokenAddress,
777
777
  toChainId,
778
- },
779
- )
780
- throw new Error("Destination token decimals not found")
781
- }
782
- const destinationTokenSymbol = destinationToken?.symbol ?? ""
783
- const originTokenSymbol = originToken?.symbol ?? ""
778
+ )
779
+ if (onChainDecimals !== null) {
780
+ destinationTokenDecimals = onChainDecimals
781
+ }
782
+ }
783
+ if (!destinationTokenDecimals) {
784
+ logger.console.error(
785
+ "[trails-sdk] Destination token decimals not found:",
786
+ {
787
+ destinationToken,
788
+ toTokenAddress,
789
+ toChainId,
790
+ },
791
+ )
792
+ throw new Error("Destination token decimals not found")
793
+ }
794
+ const destinationTokenSymbol = destinationToken?.symbol ?? ""
795
+ const originTokenSymbol = originToken?.symbol ?? ""
784
796
 
785
- // For qr-code and exchange fund methods, use a default amount of 100 tokens
786
- // since the user will deposit from an external wallet (their connected wallet balance may be 0)
787
- const effectiveOriginTokenBalance =
788
- fundMethod === "qr-code" || fundMethod === "onramp-exchange"
789
- ? parseUnits("100", sourceTokenDecimals).toString()
790
- : originTokenBalanceAmount
797
+ // For qr-code and exchange fund methods, use a default amount of 100 tokens
798
+ // since the user will deposit from an external wallet (their connected wallet balance may be 0)
799
+ const effectiveOriginTokenBalance =
800
+ fundMethod === "qr-code" || fundMethod === "onramp-exchange"
801
+ ? parseUnits("100", sourceTokenDecimals).toString()
802
+ : originTokenBalanceAmount
791
803
 
792
- const options: PrepareSendOptions = {
793
- account: walletClient.account!,
794
- originTokenAddress: fromTokenAddress,
795
- originChainId: fromChainId,
796
- originTokenBalance: effectiveOriginTokenBalance,
797
- destinationChainId: toChainId,
798
- recipient: effectiveToAddress,
799
- destinationTokenAddress: toTokenAddress,
800
- swapAmount: debouncedSwapAmount.toString(),
801
- tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
802
- originTokenSymbol: originTokenSymbol,
803
- destinationTokenSymbol: destinationTokenSymbol,
804
- destinationCalldata: toCalldata as string,
805
- client: walletClient,
806
- trailsClient,
807
- sourceTokenDecimals,
808
- destinationTokenDecimals,
809
- onTransactionStateChange: onStatusUpdate ?? (() => {}),
810
- slippageTolerance: slippageTolerance?.toString(),
811
- swapProvider: effectiveSwapProvider,
812
- bridgeProvider: effectiveBridgeProvider,
813
- paymasterUrl: paymasterUrl,
814
- selectedFeeOption: selectedFeeOption ?? null,
815
- abortSignal: combinedAbortSignal,
816
- originNativeTokenPriceUsd: originNativeTokenPriceUsd,
817
- commitIntentFn: commitIntentMutation.mutateAsync,
818
- executeIntentFn: executeIntentMutation.mutateAsync,
819
- checkoutOnHandlers,
820
- sequenceIndexerUrl,
821
- sequenceProjectAccessKey,
822
- originPublicClient: originPublicClient ?? undefined,
823
- destinationPublicClient: destinationPublicClient ?? undefined,
824
- isSmartWallet: isSmartWallet ?? undefined,
825
- trailsApiKey,
826
- trailsApiUrl,
827
- fundMethod: fundMethod ?? undefined,
828
- onramp: onramp ?? undefined,
829
- }
804
+ const options: PrepareSendOptions = {
805
+ account: walletClient.account!,
806
+ originTokenAddress: fromTokenAddress,
807
+ originChainId: fromChainId,
808
+ originTokenBalance: effectiveOriginTokenBalance,
809
+ destinationChainId: toChainId,
810
+ recipient: effectiveToAddress,
811
+ destinationTokenAddress: toTokenAddress,
812
+ swapAmount: debouncedSwapAmount.toString(),
813
+ tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
814
+ originTokenSymbol: originTokenSymbol,
815
+ destinationTokenSymbol: destinationTokenSymbol,
816
+ destinationCalldata: toCalldata as string,
817
+ client: walletClient,
818
+ trailsClient,
819
+ sourceTokenDecimals,
820
+ destinationTokenDecimals,
821
+ onTransactionStateChange: onStatusUpdate ?? (() => {}),
822
+ slippageTolerance: slippageTolerance?.toString(),
823
+ swapProvider: effectiveSwapProvider,
824
+ bridgeProvider: effectiveBridgeProvider,
825
+ paymasterUrl: paymasterUrl,
826
+ selectedFeeOption: selectedFeeOption ?? null,
827
+ abortSignal: combinedAbortSignal,
828
+ originNativeTokenPriceUsd: originNativeTokenPriceUsd,
829
+ commitIntentFn: commitIntentMutation.mutateAsync,
830
+ executeIntentFn: executeIntentMutation.mutateAsync,
831
+ checkoutOnHandlers,
832
+ sequenceIndexerUrl,
833
+ sequenceProjectAccessKey,
834
+ originPublicClient: originPublicClient ?? undefined,
835
+ destinationPublicClient: destinationPublicClient ?? undefined,
836
+ isSmartWallet: isSmartWallet ?? undefined,
837
+ trailsApiKey,
838
+ trailsApiUrl,
839
+ fundMethod: fundMethod ?? undefined,
840
+ onramp: onramp ?? undefined,
841
+ }
830
842
 
831
- logger.console.log("[trails-sdk] options", options)
843
+ logger.console.log("[trails-sdk] options", options)
832
844
 
833
- const {
834
- quote: prepareSendQuote,
835
- send: prepareSendSend,
836
- feeOptions: prepareSendFeeOptions,
837
- } = await prepareSend(options)
845
+ const {
846
+ quote: prepareSendQuote,
847
+ send: prepareSendSend,
848
+ feeOptions: prepareSendFeeOptions,
849
+ } = await prepareSend(options)
838
850
 
839
- const quote: Quote = {
840
- // Backward compatible fields
841
- fromAmount: prepareSendQuote.originAmount,
842
- toAmount: prepareSendQuote.destinationAmount,
843
- fromAmountMin: prepareSendQuote.originAmountMin,
844
- toAmountMin: prepareSendQuote.destinationAmountMin,
845
- fromAmountUsdDisplay: prepareSendQuote.originAmountUsdDisplay ?? "",
846
- toAmountUsdDisplay:
847
- prepareSendQuote.destinationAmountUsdDisplay ?? "",
848
- toAmountMinUsdDisplay:
849
- prepareSendQuote.destinationAmountMinUsdDisplay ?? "",
850
- // PrepareSendQuote compatible fields
851
- originAmount: prepareSendQuote.originAmount,
852
- originAmountMin: prepareSendQuote.originAmountMin,
853
- destinationAmount: prepareSendQuote.destinationAmount,
854
- destinationAmountMin: prepareSendQuote.destinationAmountMin,
855
- originAmountUsdDisplay: prepareSendQuote.originAmountUsdDisplay ?? "",
856
- destinationAmountUsdDisplay:
857
- prepareSendQuote.destinationAmountUsdDisplay ?? "",
858
- originAmountDisplay: prepareSendQuote.originAmountDisplay ?? "",
859
- destinationAmountDisplay:
860
- prepareSendQuote.destinationAmountDisplay ?? "",
861
- originAmountMinDisplay: prepareSendQuote.originAmountMinDisplay ?? "",
862
- originAmountMinUsdFormatted:
863
- prepareSendQuote.originAmountMinUsdFormatted ?? "",
864
- originAmountMinUsdDisplay:
865
- prepareSendQuote.originAmountMinUsdDisplay ?? "",
866
- destinationAmountMinDisplay:
867
- prepareSendQuote.destinationAmountMinDisplay ?? "",
868
- destinationAmountMinUsdFormatted:
869
- prepareSendQuote.destinationAmountMinUsdFormatted ?? "",
870
- destinationAmountMinUsdDisplay:
871
- prepareSendQuote.destinationAmountMinUsdDisplay ?? "",
872
- originAmountUsdFormatted:
873
- prepareSendQuote.originAmountUsdFormatted ?? "",
874
- destinationAmountUsdFormatted:
875
- prepareSendQuote.destinationAmountUsdFormatted ?? "",
876
- // Common fields
877
- originToken: prepareSendQuote.originToken,
878
- destinationToken: prepareSendQuote.destinationToken,
879
- originChain: prepareSendQuote.originChain,
880
- destinationChain: prepareSendQuote.destinationChain,
881
- fees: prepareSendQuote.fees,
882
- priceImpact: prepareSendQuote.priceImpact,
883
- priceImpactUsd: prepareSendQuote.priceImpactUsd ?? null,
884
- priceImpactUsdDisplay: prepareSendQuote.priceImpactUsdDisplay ?? "",
885
- completionEstimateSeconds: prepareSendQuote.completionEstimateSeconds,
886
- completionEstimateDisplay:
887
- prepareSendQuote.completionEstimateDisplay ?? "",
888
- slippageTolerance: prepareSendQuote.slippageTolerance,
889
- transactionStates: prepareSendQuote.transactionStates ?? [],
890
- originTokenRate: prepareSendQuote.originTokenRate ?? "",
891
- destinationTokenRate: prepareSendQuote.destinationTokenRate ?? "",
892
- routeProviders: prepareSendQuote.routeProviders ?? [],
893
- gasCostUsd: prepareSendQuote.gasCostUsd ?? null,
894
- gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? "",
895
- gasCost: prepareSendQuote.gasCost ?? "",
896
- gasCostFormatted: prepareSendQuote.gasCostFormatted ?? "",
897
- totalFeesUsd: prepareSendQuote.totalFeesUsd ?? null,
898
- totalFeesUsdDisplay: prepareSendQuote.totalFeesUsdDisplay ?? "",
899
- totalGasUsd: prepareSendQuote.totalGasUsd ?? null,
900
- totalGasUsdDisplay: prepareSendQuote.totalGasUsdDisplay ?? "",
901
- intentId: prepareSendQuote.intentId ?? null,
902
- originAmountFormatted: prepareSendQuote.originAmountFormatted ?? "",
903
- destinationAmountFormatted:
904
- prepareSendQuote.destinationAmountFormatted ?? "",
905
- originDepositAddress: prepareSendQuote.originDepositAddress ?? "",
906
- destinationDepositAddress:
907
- prepareSendQuote.destinationDepositAddress ?? "",
908
- destinationAddress: prepareSendQuote.destinationAddress ?? "",
909
- destinationCalldata: prepareSendQuote.destinationCalldata ?? "",
910
- // Fee breakdown fields
911
- trailsFeeBreakdown: prepareSendQuote.trailsFeeBreakdown ?? null,
912
- originGasUsd: prepareSendQuote.originGasUsd ?? null,
913
- originGasUsdDisplay: prepareSendQuote.originGasUsdDisplay ?? "",
914
- destinationGasUsd: prepareSendQuote.destinationGasUsd ?? null,
915
- destinationGasUsdDisplay:
916
- prepareSendQuote.destinationGasUsdDisplay ?? "",
917
- providerFeeUsd: prepareSendQuote.providerFeeUsd ?? null,
918
- providerFeeUsdDisplay: prepareSendQuote.providerFeeUsdDisplay ?? "",
919
- trailsFeeUsd: prepareSendQuote.trailsFeeUsd ?? null,
920
- trailsFeeUsdDisplay: prepareSendQuote.trailsFeeUsdDisplay ?? "",
921
- totalProviderFeesUsd: prepareSendQuote.totalProviderFeesUsd ?? null,
922
- totalProviderFeesUsdDisplay:
923
- prepareSendQuote.totalProviderFeesUsdDisplay ?? "",
924
- noSufficientBalance: prepareSendQuote.noSufficientBalance ?? false,
925
- expiresAt: prepareSendQuote.expiresAt ?? null,
926
- }
851
+ const quote: Quote = {
852
+ // Backward compatible fields
853
+ fromAmount: prepareSendQuote.originAmount,
854
+ toAmount: prepareSendQuote.destinationAmount,
855
+ fromAmountMin: prepareSendQuote.originAmountMin,
856
+ toAmountMin: prepareSendQuote.destinationAmountMin,
857
+ fromAmountUsdDisplay: prepareSendQuote.originAmountUsdDisplay ?? "",
858
+ toAmountUsdDisplay:
859
+ prepareSendQuote.destinationAmountUsdDisplay ?? "",
860
+ toAmountMinUsdDisplay:
861
+ prepareSendQuote.destinationAmountMinUsdDisplay ?? "",
862
+ // PrepareSendQuote compatible fields
863
+ originAmount: prepareSendQuote.originAmount,
864
+ originAmountMin: prepareSendQuote.originAmountMin,
865
+ destinationAmount: prepareSendQuote.destinationAmount,
866
+ destinationAmountMin: prepareSendQuote.destinationAmountMin,
867
+ originAmountUsdDisplay:
868
+ prepareSendQuote.originAmountUsdDisplay ?? "",
869
+ destinationAmountUsdDisplay:
870
+ prepareSendQuote.destinationAmountUsdDisplay ?? "",
871
+ originAmountDisplay: prepareSendQuote.originAmountDisplay ?? "",
872
+ destinationAmountDisplay:
873
+ prepareSendQuote.destinationAmountDisplay ?? "",
874
+ originAmountMinDisplay:
875
+ prepareSendQuote.originAmountMinDisplay ?? "",
876
+ originAmountMinUsdFormatted:
877
+ prepareSendQuote.originAmountMinUsdFormatted ?? "",
878
+ originAmountMinUsdDisplay:
879
+ prepareSendQuote.originAmountMinUsdDisplay ?? "",
880
+ destinationAmountMinDisplay:
881
+ prepareSendQuote.destinationAmountMinDisplay ?? "",
882
+ destinationAmountMinUsdFormatted:
883
+ prepareSendQuote.destinationAmountMinUsdFormatted ?? "",
884
+ destinationAmountMinUsdDisplay:
885
+ prepareSendQuote.destinationAmountMinUsdDisplay ?? "",
886
+ originAmountUsdFormatted:
887
+ prepareSendQuote.originAmountUsdFormatted ?? "",
888
+ destinationAmountUsdFormatted:
889
+ prepareSendQuote.destinationAmountUsdFormatted ?? "",
890
+ // Common fields
891
+ originToken: prepareSendQuote.originToken,
892
+ destinationToken: prepareSendQuote.destinationToken,
893
+ originChain: prepareSendQuote.originChain,
894
+ destinationChain: prepareSendQuote.destinationChain,
895
+ fees: prepareSendQuote.fees,
896
+ priceImpact: prepareSendQuote.priceImpact,
897
+ priceImpactUsd: prepareSendQuote.priceImpactUsd ?? null,
898
+ priceImpactUsdDisplay: prepareSendQuote.priceImpactUsdDisplay ?? "",
899
+ completionEstimateSeconds:
900
+ prepareSendQuote.completionEstimateSeconds,
901
+ completionEstimateDisplay:
902
+ prepareSendQuote.completionEstimateDisplay ?? "",
903
+ slippageTolerance: prepareSendQuote.slippageTolerance,
904
+ transactionStates: prepareSendQuote.transactionStates ?? [],
905
+ originTokenRate: prepareSendQuote.originTokenRate ?? "",
906
+ destinationTokenRate: prepareSendQuote.destinationTokenRate ?? "",
907
+ originTokenPriceUsd: prepareSendQuote.originTokenPriceUsd ?? 0,
908
+ destinationTokenPriceUsd:
909
+ prepareSendQuote.destinationTokenPriceUsd ?? 0,
910
+ routeProviders: prepareSendQuote.routeProviders ?? [],
911
+ gasCostUsd: prepareSendQuote.gasCostUsd ?? null,
912
+ gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? "",
913
+ gasCost: prepareSendQuote.gasCost ?? "",
914
+ gasCostFormatted: prepareSendQuote.gasCostFormatted ?? "",
915
+ totalFeesUsd: prepareSendQuote.totalFeesUsd ?? null,
916
+ totalFeesUsdDisplay: prepareSendQuote.totalFeesUsdDisplay ?? "",
917
+ totalGasUsd: prepareSendQuote.totalGasUsd ?? null,
918
+ totalGasUsdDisplay: prepareSendQuote.totalGasUsdDisplay ?? "",
919
+ intentId: prepareSendQuote.intentId ?? null,
920
+ originAmountFormatted: prepareSendQuote.originAmountFormatted ?? "",
921
+ destinationAmountFormatted:
922
+ prepareSendQuote.destinationAmountFormatted ?? "",
923
+ originDepositAddress: prepareSendQuote.originDepositAddress ?? "",
924
+ destinationDepositAddress:
925
+ prepareSendQuote.destinationDepositAddress ?? "",
926
+ destinationAddress: prepareSendQuote.destinationAddress ?? "",
927
+ destinationCalldata: prepareSendQuote.destinationCalldata ?? "",
928
+ // Fee breakdown fields
929
+ trailsFeeBreakdown: prepareSendQuote.trailsFeeBreakdown ?? null,
930
+ originGasUsd: prepareSendQuote.originGasUsd ?? null,
931
+ originGasUsdDisplay: prepareSendQuote.originGasUsdDisplay ?? "",
932
+ destinationGasUsd: prepareSendQuote.destinationGasUsd ?? null,
933
+ destinationGasUsdDisplay:
934
+ prepareSendQuote.destinationGasUsdDisplay ?? "",
935
+ providerFeeUsd: prepareSendQuote.providerFeeUsd ?? null,
936
+ providerFeeUsdDisplay: prepareSendQuote.providerFeeUsdDisplay ?? "",
937
+ trailsFeeUsd: prepareSendQuote.trailsFeeUsd ?? null,
938
+ trailsFeeUsdDisplay: prepareSendQuote.trailsFeeUsdDisplay ?? "",
939
+ totalProviderFeesUsd: prepareSendQuote.totalProviderFeesUsd ?? null,
940
+ totalProviderFeesUsdDisplay:
941
+ prepareSendQuote.totalProviderFeesUsdDisplay ?? "",
942
+ noSufficientBalance: prepareSendQuote.noSufficientBalance ?? false,
943
+ expiresAt: prepareSendQuote.expiresAt ?? null,
944
+ }
927
945
 
928
- const sendTransaction = async (
929
- options?: SendOptions,
930
- ): Promise<SwapReturn> => {
931
- // Use fee option from send() call if provided, otherwise fall back to prop value
932
- const effectiveFeeOption =
933
- options?.selectedFeeOption !== undefined
934
- ? options.selectedFeeOption
935
- : (selectedFeeOption ?? null)
946
+ const sendTransaction = async (
947
+ options?: SendOptions,
948
+ ): Promise<SwapReturn> => {
949
+ // Use fee option from send() call if provided, otherwise fall back to prop value
950
+ const effectiveFeeOption =
951
+ options?.selectedFeeOption !== undefined
952
+ ? options.selectedFeeOption
953
+ : (selectedFeeOption ?? null)
936
954
 
937
- const {
938
- depositUserTxnReceipt,
939
- destinationIntentTransaction,
940
- totalCompletionSeconds,
941
- } = await prepareSendSend({
942
- selectedFeeOption: effectiveFeeOption,
943
- onOriginSend: options?.onOriginSend,
944
- depositTransactionHash: options?.depositTransactionHash,
945
- skipCommit: options?.skipCommit,
946
- commitOnly: options?.commitOnly,
947
- })
955
+ const {
956
+ depositUserTxnReceipt,
957
+ destinationIntentTransaction,
958
+ totalCompletionSeconds,
959
+ } = await prepareSendSend({
960
+ selectedFeeOption: effectiveFeeOption,
961
+ onOriginSend: options?.onOriginSend,
962
+ depositTransactionHash: options?.depositTransactionHash,
963
+ skipCommit: options?.skipCommit,
964
+ commitOnly: options?.commitOnly,
965
+ })
948
966
 
949
- return {
950
- originTransaction: {
951
- transactionHash: depositUserTxnReceipt?.transactionHash,
952
- explorerUrl: getExplorerUrl({
953
- txHash: depositUserTxnReceipt?.transactionHash as string,
954
- chainId: fromChainId,
955
- }),
956
- receipt: depositUserTxnReceipt,
957
- },
958
- destinationTransaction: {
959
- transactionHash: destinationIntentTransaction?.txnHash,
960
- explorerUrl: getExplorerUrl({
961
- txHash: destinationIntentTransaction?.txnHash as string,
962
- chainId: toChainId,
963
- }),
964
- receipt: destinationIntentTransaction,
965
- },
966
- totalCompletionSeconds,
967
+ return {
968
+ originTransaction: {
969
+ transactionHash: depositUserTxnReceipt?.transactionHash,
970
+ explorerUrl: getExplorerUrl({
971
+ txHash: depositUserTxnReceipt?.transactionHash as string,
972
+ chainId: fromChainId,
973
+ }),
974
+ receipt: depositUserTxnReceipt,
975
+ },
976
+ destinationTransaction: {
977
+ transactionHash: destinationIntentTransaction?.txnHash,
978
+ explorerUrl: getExplorerUrl({
979
+ txHash: destinationIntentTransaction?.txnHash as string,
980
+ chainId: toChainId,
981
+ }),
982
+ receipt: destinationIntentTransaction,
983
+ },
984
+ totalCompletionSeconds,
985
+ }
967
986
  }
968
- }
969
987
 
970
- return {
971
- quote,
972
- send: sendTransaction,
973
- feeOptions: prepareSendFeeOptions?.feeOptions ?? [],
988
+ // Clear the timeout on successful completion
989
+ clearTimeout(timeoutId)
990
+
991
+ return {
992
+ quote,
993
+ send: sendTransaction,
994
+ feeOptions: prepareSendFeeOptions?.feeOptions ?? [],
995
+ }
996
+ } finally {
997
+ // Always clear the timeout
998
+ clearTimeout(timeoutId)
974
999
  }
975
1000
  } catch (error) {
1001
+ // Check if this was aborted due to timeout
1002
+ if (
1003
+ (error as any)?.name === "AbortError" ||
1004
+ abortControllerRef.current.signal.aborted
1005
+ ) {
1006
+ logger.console.error(
1007
+ "[trails-sdk] [useQuote] Quote request timed out:",
1008
+ error,
1009
+ )
1010
+ throw new Error("No route available")
1011
+ }
1012
+
976
1013
  logger.console.error(
977
1014
  "[trails-sdk] [useQuote] Error getting quote:",
978
1015
  error,
@@ -999,7 +1036,7 @@ export function useQuote({
999
1036
  refetchOnWindowFocus: false, // Don't refetch when window regains focus
1000
1037
  refetchOnMount: "always", // Always refetch on component remount to ensure fresh quote
1001
1038
  refetchInterval: false, // Disable automatic polling
1002
- retry: 2, // Limit retry attempts
1039
+ retry: false, // Disable automatic retries - only make 1 request
1003
1040
  refetchOnReconnect: true, // Refetch when network reconnects
1004
1041
  })
1005
1042