0xtrails 0.7.0 → 0.8.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.
Files changed (102) hide show
  1. package/dist/{ccip-fConRNoG.js → ccip-uMWNlvmJ.js} +34 -34
  2. package/dist/fees.d.ts.map +1 -1
  3. package/dist/{index-BbajxCG_.js → index-BiPwqVkZ.js} +31527 -28874
  4. package/dist/index.d.ts +8 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +478 -456
  7. package/dist/intents.d.ts +10 -4
  8. package/dist/intents.d.ts.map +1 -1
  9. package/dist/prepareSend.d.ts +1 -1
  10. package/dist/prepareSend.d.ts.map +1 -1
  11. package/dist/prices.d.ts +2 -2
  12. package/dist/prices.d.ts.map +1 -1
  13. package/dist/refund.d.ts +116 -0
  14. package/dist/refund.d.ts.map +1 -0
  15. package/dist/tokenBalances.d.ts +1 -1
  16. package/dist/tokenBalances.d.ts.map +1 -1
  17. package/dist/transactionIntent/handlers/crossChain.d.ts +4 -3
  18. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  19. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +3 -3
  20. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  21. package/dist/transactionIntent/quote/normalizeQuote.d.ts +1 -2
  22. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  23. package/dist/transactionIntent/quote/quoteHelpers.d.ts +3 -3
  24. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  25. package/dist/transactionIntent/types.d.ts +5 -4
  26. package/dist/transactionIntent/types.d.ts.map +1 -1
  27. package/dist/transactions.d.ts +4 -0
  28. package/dist/transactions.d.ts.map +1 -1
  29. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  30. package/dist/widget/components/ClassicSwap.d.ts +2 -1
  31. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  32. package/dist/widget/components/Earn.d.ts +2 -1
  33. package/dist/widget/components/Earn.d.ts.map +1 -1
  34. package/dist/widget/components/ErrorDisplay.d.ts.map +1 -1
  35. package/dist/widget/components/Fund.d.ts +2 -1
  36. package/dist/widget/components/Fund.d.ts.map +1 -1
  37. package/dist/widget/components/FundSwap.d.ts +2 -1
  38. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  39. package/dist/widget/components/Pay.d.ts +2 -1
  40. package/dist/widget/components/Pay.d.ts.map +1 -1
  41. package/dist/widget/components/PoolDeposit.d.ts +2 -1
  42. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  43. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  44. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  45. package/dist/widget/components/Swap.d.ts +2 -1
  46. package/dist/widget/components/Swap.d.ts.map +1 -1
  47. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  48. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  49. package/dist/widget/css/compiled.css +1 -1
  50. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  51. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  52. package/dist/widget/hooks/useGetIntent.d.ts +18 -0
  53. package/dist/widget/hooks/useGetIntent.d.ts.map +1 -0
  54. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  55. package/dist/widget/hooks/useQuote.d.ts +10 -7
  56. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  57. package/dist/widget/hooks/useSendForm.d.ts +3 -2
  58. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  59. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  60. package/dist/widget/hooks/useTrailsSendTransaction.d.ts.map +1 -1
  61. package/dist/widget/index.js +3 -3
  62. package/dist/widget/widget.d.ts +2 -1
  63. package/dist/widget/widget.d.ts.map +1 -1
  64. package/package.json +5 -12
  65. package/src/fees.ts +8 -2
  66. package/src/index.ts +33 -1
  67. package/src/intents.ts +34 -7
  68. package/src/prepareSend.ts +6 -4
  69. package/src/prices.ts +6 -6
  70. package/src/refund.ts +914 -0
  71. package/src/tokenBalances.ts +4 -14
  72. package/src/transactionIntent/handlers/crossChain.ts +21 -10
  73. package/src/transactionIntent/handlers/sameChainSameToken.ts +12 -8
  74. package/src/transactionIntent/quote/normalizeQuote.ts +29 -27
  75. package/src/transactionIntent/quote/quoteHelpers.ts +5 -9
  76. package/src/transactionIntent/types.ts +5 -3
  77. package/src/transactions.ts +5 -0
  78. package/src/widget/compiled.css +1 -1
  79. package/src/widget/components/AccountIntentTransactionHistory.tsx +197 -5
  80. package/src/widget/components/ClassicSwap.tsx +6 -3
  81. package/src/widget/components/Earn.tsx +6 -3
  82. package/src/widget/components/ErrorDisplay.tsx +6 -4
  83. package/src/widget/components/Fund.tsx +6 -3
  84. package/src/widget/components/FundSwap.tsx +2 -1
  85. package/src/widget/components/Pay.tsx +15 -7
  86. package/src/widget/components/PoolDeposit.tsx +6 -3
  87. package/src/widget/components/QuoteDetails.tsx +34 -38
  88. package/src/widget/components/Swap.tsx +2 -1
  89. package/src/widget/components/TokenImage.tsx +3 -1
  90. package/src/widget/components/TransactionDetails.tsx +108 -0
  91. package/src/widget/hooks/useAmountUsd.ts +0 -3
  92. package/src/widget/hooks/useDefaultTokenSelection.tsx +0 -3
  93. package/src/widget/hooks/useGetIntent.ts +53 -0
  94. package/src/widget/hooks/useIntentTransactionHistory.ts +85 -3
  95. package/src/widget/hooks/useQuote.ts +16 -10
  96. package/src/widget/hooks/useSendForm.ts +30 -15
  97. package/src/widget/hooks/useTokenList.ts +2 -4
  98. package/src/widget/hooks/useTrailsSendTransaction.ts +2 -1
  99. package/src/widget/widget.tsx +12 -6
  100. package/dist/sequenceWallet.d.ts +0 -67
  101. package/dist/sequenceWallet.d.ts.map +0 -1
  102. package/src/sequenceWallet.ts +0 -532
@@ -4,6 +4,7 @@ import { ChainImage } from "./ChainImage.js"
4
4
  import { getCommonTokenImageUrl } from "../../tokens.js"
5
5
  import { logger } from "../../logger.js"
6
6
  import { commonTokenImages } from "../../tokens.js"
7
+ import { useChainInfo } from "../../chains.js"
7
8
 
8
9
  function normalizeImageUrl(
9
10
  imageUrl?: string | null,
@@ -51,6 +52,7 @@ export const TokenImage: React.FC<TokenImageProps> = ({
51
52
  height: number
52
53
  } | null>(null)
53
54
  const previousUrlRef = useRef<string | null>(null)
55
+ const chainInfo = useChainInfo(chainId || undefined)
54
56
 
55
57
  const effectiveImageUrl = useMemo<string | null>(() => {
56
58
  return normalizeImageUrl(imageUrl, symbol, chainId, contractAddress)
@@ -118,7 +120,7 @@ export const TokenImage: React.FC<TokenImageProps> = ({
118
120
  height: size,
119
121
  backgroundColor: symbol === "ETH" ? "#627eeb" : undefined,
120
122
  }}
121
- title={`${symbol}${chainId ? ` on chain ${chainId}` : ""}`}
123
+ title={`${symbol}${chainId ? ` on ${chainInfo?.name || "chain"} (${chainId})` : ""}`}
122
124
  >
123
125
  {shouldShowText ? (
124
126
  <div
@@ -1,10 +1,12 @@
1
1
  import type React from "react"
2
+ import { useState } from "react"
2
3
  import type { IntentTransaction } from "../../transactions.js"
3
4
  import { TokenImage } from "./TokenImage.js"
4
5
  import { ChainImage } from "./ChainImage.js"
5
6
  import { getExplorerUrlForAddress } from "../../explorer.js"
6
7
  import { truncateAddress } from "../../utils.js"
7
8
  import { formatRawAmount } from "../../tokenBalances.js"
9
+ import { logger } from "../../logger.js"
8
10
 
9
11
  interface TransactionDetailsProps {
10
12
  transaction: IntentTransaction
@@ -15,8 +17,114 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
15
17
  transaction,
16
18
  className = "",
17
19
  }) => {
20
+ const [copiedIntentId, setCopiedIntentId] = useState<string | null>(null)
21
+
22
+ const handleCopyIntentId = async (intentId: string, e: React.MouseEvent) => {
23
+ e.preventDefault()
24
+ e.stopPropagation()
25
+ try {
26
+ await navigator.clipboard.writeText(intentId)
27
+ setCopiedIntentId(intentId)
28
+ setTimeout(() => setCopiedIntentId(null), 2000) // Clear after 2 seconds
29
+ logger.console.log(
30
+ "[trails-sdk] Successfully copied intent ID:",
31
+ intentId,
32
+ )
33
+ } catch (error) {
34
+ logger.console.error("[trails-sdk] Failed to copy intent ID:", error)
35
+ // Fallback for older browsers or when clipboard API is not available
36
+ try {
37
+ const textArea = document.createElement("textarea")
38
+ textArea.value = intentId
39
+ document.body.appendChild(textArea)
40
+ textArea.select()
41
+ document.execCommand("copy")
42
+ document.body.removeChild(textArea)
43
+ setCopiedIntentId(intentId)
44
+ setTimeout(() => setCopiedIntentId(null), 2000)
45
+ logger.console.log(
46
+ "[trails-sdk] Successfully copied intent ID using fallback:",
47
+ intentId,
48
+ )
49
+ } catch (fallbackError) {
50
+ logger.console.error(
51
+ "[trails-sdk] Fallback copy also failed:",
52
+ fallbackError,
53
+ )
54
+ }
55
+ }
56
+ }
57
+
18
58
  return (
19
59
  <div className={`space-y-2 text-sm ${className}`}>
60
+ {/* Intent ID */}
61
+ {transaction.intentId && (
62
+ <div className="flex justify-between items-center">
63
+ <span className="text-xs text-gray-600 dark:text-gray-400">
64
+ Intent ID:
65
+ </span>
66
+ <div className="flex items-center gap-1">
67
+ <span className="text-xs font-mono text-gray-900 dark:text-gray-100">
68
+ {truncateAddress(transaction.intentId)}
69
+ </span>
70
+ <button
71
+ type="button"
72
+ onClick={(e) => {
73
+ if (transaction.intentId) {
74
+ handleCopyIntentId(transaction.intentId, e)
75
+ }
76
+ }}
77
+ onMouseDown={(e) => e.stopPropagation()}
78
+ onMouseUp={(e) => e.stopPropagation()}
79
+ className={`p-0.5 rounded transition-all duration-200 cursor-pointer z-10 relative ${
80
+ copiedIntentId === transaction.intentId
81
+ ? "bg-green-100 dark:bg-green-900/30"
82
+ : "hover:bg-gray-200 dark:hover:bg-gray-600"
83
+ }`}
84
+ title={
85
+ copiedIntentId === transaction.intentId
86
+ ? "Copied!"
87
+ : "Copy full intent ID"
88
+ }
89
+ >
90
+ {copiedIntentId === transaction.intentId ? (
91
+ <svg
92
+ className="w-3 h-3 text-green-600 dark:text-green-400"
93
+ fill="none"
94
+ viewBox="0 0 24 24"
95
+ stroke="currentColor"
96
+ aria-label="Copied"
97
+ >
98
+ <title>Copied</title>
99
+ <path
100
+ strokeLinecap="round"
101
+ strokeLinejoin="round"
102
+ strokeWidth={2}
103
+ d="M5 13l4 4L19 7"
104
+ />
105
+ </svg>
106
+ ) : (
107
+ <svg
108
+ className="w-3 h-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
109
+ fill="none"
110
+ viewBox="0 0 24 24"
111
+ stroke="currentColor"
112
+ aria-label="Copy"
113
+ >
114
+ <title>Copy</title>
115
+ <path
116
+ strokeLinecap="round"
117
+ strokeLinejoin="round"
118
+ strokeWidth={2}
119
+ d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
120
+ />
121
+ </svg>
122
+ )}
123
+ </button>
124
+ </div>
125
+ </div>
126
+ )}
127
+
20
128
  {/* Origin Intent */}
21
129
  {transaction.originIntentAddress && transaction.originToken && (
22
130
  <div className="flex justify-between items-center">
@@ -2,7 +2,6 @@ import { useQuery } from "@tanstack/react-query"
2
2
  import { useTokenPrice } from "../../prices.js"
3
3
  import { formatUsdAmountDisplay } from "../../tokenBalances.js"
4
4
  import { useTokenAddress } from "../../tokens.js"
5
- import { useTrailsClient } from "../../trailsClient.js"
6
5
 
7
6
  type UseAmountUsdProps = {
8
7
  amount?: string | null
@@ -14,7 +13,6 @@ export function useAmountUsd({ amount, token, chainId }: UseAmountUsdProps): {
14
13
  amountUsd: number | null
15
14
  amountUsdFormatted: string
16
15
  } {
17
- const trailsClient = useTrailsClient()
18
16
  const isTokenAddress = token?.startsWith("0x")
19
17
  const resolvedTokenAddress = useTokenAddress({
20
18
  chainId,
@@ -30,7 +28,6 @@ export function useAmountUsd({ amount, token, chainId }: UseAmountUsdProps): {
30
28
  chainId: Number(chainId),
31
29
  }
32
30
  : null,
33
- trailsClient,
34
31
  )
35
32
 
36
33
  const { data: amountUsd } = useQuery({
@@ -9,7 +9,6 @@ import { useTargetAmount } from "./useTargetAmount.js"
9
9
  import { useTokenBalances } from "../../tokenBalances.js"
10
10
  import type { Token } from "../../tokens.js"
11
11
  import { isNativeToken } from "../../utils.js"
12
- import { useIndexerGatewayClient } from "../../indexerClient.js"
13
12
  import { useSupportedTokens } from "../../tokens.js"
14
13
  import { getChainInfo } from "../../chains.js"
15
14
  import { logger } from "../../logger.js"
@@ -99,11 +98,9 @@ function useDefaultTokenSelectionInternal(): UseDefaultTokenSelectionReturn {
99
98
  const { toToken, toChainId } = useWidgetProps()
100
99
  const { targetAmountUsd } = useTargetAmount()
101
100
  const { address } = useAccount()
102
- const indexerGatewayClient = useIndexerGatewayClient()
103
101
 
104
102
  const { sortedTokens, isLoadingSortedTokens } = useTokenBalances(
105
103
  address as Address.Address,
106
- indexerGatewayClient,
107
104
  )
108
105
 
109
106
  const { supportedTokens, isLoadingTokens: isLoadingSupportedTokens } =
@@ -0,0 +1,53 @@
1
+ import { useQuery } from "@tanstack/react-query"
2
+ import { useTrailsClient } from "../../trailsClient.js"
3
+ import { getIntent } from "../../intents.js"
4
+ import { useTrails } from "../providers/TrailsProvider.js"
5
+
6
+ export interface UseGetIntentParams {
7
+ intentId: string | undefined
8
+ enabled?: boolean
9
+ }
10
+
11
+ export interface UseGetIntentReturn {
12
+ intent: import("@0xtrails/api").Intent | null
13
+ isLoading: boolean
14
+ isError: boolean
15
+ error: Error | null
16
+ refetch: () => void
17
+ }
18
+
19
+ /**
20
+ * Hook to fetch intent data from the Trails API
21
+ * @param params - Object containing intentId and optional enabled flag
22
+ * @returns Intent data, loading state, error state, and refetch function
23
+ */
24
+ export function useGetIntent({
25
+ intentId,
26
+ enabled = true,
27
+ }: UseGetIntentParams): UseGetIntentReturn {
28
+ const trailsConfig = useTrails()
29
+ const trailsClient = useTrailsClient()
30
+
31
+ const { data, isLoading, isError, error, refetch } = useQuery({
32
+ queryKey: ["getIntent", intentId],
33
+ queryFn: async () => {
34
+ if (!intentId || !trailsConfig.trailsApiKey) {
35
+ throw new Error("Intent ID and API key required")
36
+ }
37
+
38
+ const response = await getIntent(trailsClient, intentId)
39
+ return response.intent
40
+ },
41
+ enabled: enabled && !!intentId && !!trailsConfig.trailsApiKey,
42
+ })
43
+
44
+ return {
45
+ intent: data ?? null,
46
+ isLoading,
47
+ isError,
48
+ error: error as Error | null,
49
+ refetch: () => {
50
+ refetch()
51
+ },
52
+ }
53
+ }
@@ -39,6 +39,8 @@ function mapApiStatusToUiStatus(apiStatus: string): string {
39
39
  return "failed"
40
40
  case "CREATED":
41
41
  return "created"
42
+ case "ABORTED":
43
+ return "aborted"
42
44
  default:
43
45
  return apiStatus?.toLowerCase() || "unknown"
44
46
  }
@@ -50,16 +52,55 @@ function mapApiStatusToUiStatus(apiStatus: string): string {
50
52
  function mapIntentSummaryToTransaction(
51
53
  intent: IntentSummary,
52
54
  ): IntentTransaction {
55
+ // Determine effective status based on transaction status fields
56
+ // Priority: destinationTransactionStatus > originTransactionStatus > depositTransactionStatus > main status
57
+ let effectiveStatus = mapApiStatusToUiStatus(intent.status)
58
+
59
+ const depositStatus = intent.depositTransactionStatus
60
+ const originStatus = intent.originTransactionStatus
61
+ const destinationStatus = intent.destinationTransactionStatus
62
+
63
+ // If destinationTransactionStatus exists and is not ABORTED, use it
64
+ if (destinationStatus && destinationStatus !== "ABORTED") {
65
+ effectiveStatus = mapApiStatusToUiStatus(destinationStatus)
66
+ }
67
+ // If destinationTransactionStatus doesn't exist but originTransactionStatus exists and is not ABORTED, use it
68
+ else if (!destinationStatus && originStatus && originStatus !== "ABORTED") {
69
+ effectiveStatus = mapApiStatusToUiStatus(originStatus)
70
+ }
71
+ // If depositTransactionStatus is ABORTED but origin/destination have non-ABORTED statuses, don't use aborted
72
+ // Only use aborted if deposit is ABORTED and origin/destination are also ABORTED or don't exist
73
+ else if (depositStatus === "ABORTED") {
74
+ // Check if origin or destination have non-ABORTED statuses
75
+ const hasNonAbortedOrigin = originStatus && originStatus !== "ABORTED"
76
+ const hasNonAbortedDestination =
77
+ destinationStatus && destinationStatus !== "ABORTED"
78
+
79
+ if (!hasNonAbortedOrigin && !hasNonAbortedDestination) {
80
+ // Only mark as aborted if no non-aborted statuses exist
81
+ effectiveStatus = "aborted"
82
+ }
83
+ }
84
+ // If originTransactionStatus is ABORTED and no destination status, use aborted
85
+ else if (originStatus === "ABORTED" && !destinationStatus) {
86
+ effectiveStatus = "aborted"
87
+ }
88
+ // If destinationTransactionStatus is ABORTED, use aborted
89
+ else if (destinationStatus === "ABORTED") {
90
+ effectiveStatus = "aborted"
91
+ }
92
+
53
93
  return {
54
94
  originIntentAddress: intent.originIntentAddress,
55
95
  destinationIntentAddress: intent.destinationIntentAddress,
56
96
  mainSigner: intent.ownerAddress,
97
+ intentId: intent.intentId,
57
98
  metaTxnId: intent.intentId,
58
99
  txnHash:
59
100
  intent.originTransactionHash ||
60
101
  intent.destinationTransactionHash ||
61
102
  undefined,
62
- executionStatus: mapApiStatusToUiStatus(intent.status),
103
+ executionStatus: effectiveStatus,
63
104
  originChainId: intent.originChainId,
64
105
  destinationChainId: intent.destinationChainId,
65
106
  originTokenAddress: intent.originTokenAddress,
@@ -70,6 +111,9 @@ function mapIntentSummaryToTransaction(
70
111
  createdAt: intent.createdAt,
71
112
  originTransactionHash: intent.originTransactionHash,
72
113
  destinationTransactionHash: intent.destinationTransactionHash,
114
+ depositTransactionStatus: intent.depositTransactionStatus,
115
+ originTransactionStatus: intent.originTransactionStatus,
116
+ destinationTransactionStatus: intent.destinationTransactionStatus,
73
117
  originToken: intent.originTokenMetadata
74
118
  ? ({
75
119
  symbol: intent.originTokenMetadata.symbol || "",
@@ -514,6 +558,40 @@ function getEffectiveExecutionStatus(
514
558
  createdAt,
515
559
  } = transaction
516
560
 
561
+ // Check for aborted status in transaction status fields
562
+ // Priority: destinationTransactionStatus > originTransactionStatus > depositTransactionStatus
563
+ const depositStatus = transaction.depositTransactionStatus
564
+ const originStatus = transaction.originTransactionStatus
565
+ const destinationStatus = transaction.destinationTransactionStatus
566
+
567
+ // If destinationTransactionStatus exists and is not ABORTED, use it (don't mark as aborted)
568
+ if (destinationStatus && destinationStatus !== "ABORTED") {
569
+ return mapApiStatusToUiStatus(destinationStatus)
570
+ }
571
+ // If destinationTransactionStatus doesn't exist but originTransactionStatus exists and is not ABORTED, use it
572
+ if (!destinationStatus && originStatus && originStatus !== "ABORTED") {
573
+ return mapApiStatusToUiStatus(originStatus)
574
+ }
575
+ // If depositTransactionStatus is ABORTED but origin/destination have non-ABORTED statuses, don't use aborted
576
+ if (depositStatus === "ABORTED") {
577
+ const hasNonAbortedOrigin = originStatus && originStatus !== "ABORTED"
578
+ const hasNonAbortedDestination =
579
+ destinationStatus && destinationStatus !== "ABORTED"
580
+
581
+ // Only mark as aborted if no non-aborted statuses exist
582
+ if (!hasNonAbortedOrigin && !hasNonAbortedDestination) {
583
+ return "aborted"
584
+ }
585
+ }
586
+ // If originTransactionStatus is ABORTED and no destination status, use aborted
587
+ if (originStatus === "ABORTED" && !destinationStatus) {
588
+ return "aborted"
589
+ }
590
+ // If destinationTransactionStatus is ABORTED, use aborted
591
+ if (destinationStatus === "ABORTED") {
592
+ return "aborted"
593
+ }
594
+
517
595
  // If we decoded a refund, return "refunded"
518
596
  if (decodedEvents.refunded) {
519
597
  return "refunded"
@@ -524,8 +602,12 @@ function getEffectiveExecutionStatus(
524
602
  return "failed"
525
603
  }
526
604
 
527
- // If API provided a status, use it
528
- if (executionStatus && executionStatus !== "unknown") {
605
+ // If API provided a status, use it (but skip if it's "executing" when we know it's aborted)
606
+ if (
607
+ executionStatus &&
608
+ executionStatus !== "unknown" &&
609
+ executionStatus !== "executing"
610
+ ) {
529
611
  return executionStatus
530
612
  }
531
613
 
@@ -23,7 +23,7 @@ import type { PrepareSendFees } from "../../prepareSend.js"
23
23
  import { getTokenPrice } from "../../prices.js"
24
24
  import { useCommitIntent, useExecuteIntent } from "../../mutations.js"
25
25
  import type { CheckoutOnHandlers } from "./useCheckout.js"
26
- import type { FeeOption } from "@0xtrails/api"
26
+ import type { FeeOption, RouteProvider } from "@0xtrails/api"
27
27
 
28
28
  /**
29
29
  * Configuration options for the `useQuote` hook.
@@ -55,8 +55,10 @@ export type UseQuoteProps = {
55
55
  slippageTolerance?: string | number | null
56
56
  /** Optional callback function that receives transaction state updates during swap execution. */
57
57
  onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
58
- /** Optional quote provider ID to use. If not specified, the best available provider will be selected. */
59
- quoteProvider?: string | null
58
+ /** Optional quote swap provider ID to use. If not specified, the best available provider will be selected. */
59
+ swapProvider?: RouteProvider | null
60
+ /** Optional quote bridge provider ID to use. If not specified, the best available provider will be selected. */
61
+ bridgeProvider?: RouteProvider | null
60
62
  /**
61
63
  * Optional checkout event handlers for tracking the states of a Trails transaction.
62
64
  *
@@ -123,7 +125,7 @@ export type SwapReturn = {
123
125
  /**
124
126
  * Information about a quote provider (LiFi, CCTP, Relay, etc.) used for routing the swap.
125
127
  */
126
- export type QuoteProviderInfo = {
128
+ export type RouteProviderInfo = {
127
129
  id: string
128
130
  name: string
129
131
  url: string
@@ -152,7 +154,7 @@ export type Quote = {
152
154
  completionEstimateSeconds: number
153
155
  transactionStates?: TransactionState[]
154
156
  originTokenRate?: string
155
- quoteProvider?: QuoteProviderInfo | null
157
+ routeProviders?: RouteProviderInfo[]
156
158
  destinationTokenRate?: string
157
159
  fromAmountUsdDisplay?: string
158
160
  toAmountUsdDisplay?: string
@@ -271,7 +273,8 @@ export type UseQuoteReturn = {
271
273
  * @param props.slippageTolerance - Slippage tolerance as a decimal string or number (e.g., '0.005' for 0.5%, '0.01' for 1%). Defaults to a reasonable value if not provided
272
274
  * @param props.toCalldata - Optional custom calldata for the destination chain. Used for executing arbitrary contract calls (e.g., swap and mint an NFT on destination chain)
273
275
  * @param props.onStatusUpdate - Optional callback function that receives transaction state updates during swap execution
274
- * @param props.quoteProvider - Optional quote provider ID to use (auto, lifi, relay, cctp, direct). If not specified, the best available provider will be selected
276
+ * @param props.swapProvider - Optional swap provider ID to use (auto, lifi, relay, cctp, direct). If not specified, the best available provider will be selected
277
+ * @param props.bridgeProvider - Optional bridge provider ID to use (auto, lifi, relay, cctp, direct). If not specified, the best available provider will be selected
275
278
  * @param props.checkoutOnHandlers - Optional event handlers for tracking checkout/transaction lifecycle events
276
279
  * @param props.paymasterUrl - Optional paymaster URL for gasless ERC-4337 transactions
277
280
  * @param props.selectedFeeOption - Optional fee option selection (e.g., user selects USDC as the fee token instead of ETH)
@@ -314,7 +317,8 @@ export function useQuote({
314
317
  slippageTolerance,
315
318
  onStatusUpdate,
316
319
  checkoutOnHandlers,
317
- quoteProvider,
320
+ swapProvider,
321
+ bridgeProvider,
318
322
  paymasterUrl,
319
323
  selectedFeeOption,
320
324
  nodeGatewayEnv,
@@ -382,7 +386,8 @@ export function useQuote({
382
386
  toCalldata,
383
387
  tradeType,
384
388
  slippageTolerance,
385
- quoteProvider,
389
+ swapProvider,
390
+ bridgeProvider,
386
391
  apiKey,
387
392
  isSmartWallet,
388
393
  ],
@@ -628,7 +633,8 @@ export function useQuote({
628
633
  dryMode: false,
629
634
  onTransactionStateChange: onStatusUpdate ?? (() => {}),
630
635
  slippageTolerance: slippageTolerance?.toString(),
631
- quoteProvider: quoteProvider,
636
+ swapProvider: swapProvider,
637
+ bridgeProvider: bridgeProvider,
632
638
  paymasterUrl: paymasterUrl,
633
639
  selectedFeeOption: selectedFeeOption ?? null,
634
640
  abortSignal: combinedAbortSignal,
@@ -668,7 +674,7 @@ export function useQuote({
668
674
  transactionStates: prepareSendQuote.transactionStates,
669
675
  originTokenRate: prepareSendQuote.originTokenRate,
670
676
  destinationTokenRate: prepareSendQuote.destinationTokenRate,
671
- quoteProvider: prepareSendQuote.quoteProvider,
677
+ routeProviders: prepareSendQuote.routeProviders,
672
678
  fromAmountUsdDisplay:
673
679
  prepareSendQuote.originAmountUsdDisplay ?? undefined,
674
680
  toAmountUsdDisplay:
@@ -1,4 +1,4 @@
1
- import type { TokenPrice, FeeOption } from "@0xtrails/api"
1
+ import type { TokenPrice, FeeOption, RouteProvider } from "@0xtrails/api"
2
2
  import { JsonEncode } from "@0xtrails/api"
3
3
  import type React from "react"
4
4
  import { useCallback, useEffect, useMemo, useState, useRef } from "react"
@@ -89,7 +89,8 @@ export type UseSendProps = {
89
89
  selectedToken?: Token
90
90
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
91
91
  tradeType?: TradeType
92
- quoteProvider?: string
92
+ swapProvider?: string
93
+ bridgeProvider?: string
93
94
  fundMethod?: string
94
95
  mode?: "pay" | "fund" | "earn" | "swap"
95
96
  onNavigateToOnramp?: (
@@ -175,7 +176,8 @@ export function useSendForm({
175
176
  onComplete,
176
177
  setWalletConfirmRetryHandler,
177
178
  tradeType = TradeType.EXACT_OUTPUT,
178
- quoteProvider,
179
+ swapProvider,
180
+ bridgeProvider,
179
181
  fundMethod,
180
182
  mode,
181
183
  onNavigateToOnramp,
@@ -189,9 +191,9 @@ export function useSendForm({
189
191
  // Read isSmartWallet from widget props
190
192
  const { isSmartWallet } = useWidgetProps()
191
193
 
192
- // Auto-set quoteProvider to "lifi" if either from or to chain is etherlink
193
- const effectiveQuoteProvider = useMemo(() => {
194
- if (!quoteProvider || quoteProvider === "auto") {
194
+ // Auto-set swapProvider and bridgeProvider to "lifi" if either from or to chain is etherlink
195
+ const effectiveSwapProvider = useMemo(() => {
196
+ if (!swapProvider || swapProvider === "auto") {
195
197
  if (
196
198
  selectedToken?.chainId === etherlink.id ||
197
199
  toChainId === etherlink.id
@@ -199,8 +201,20 @@ export function useSendForm({
199
201
  return "lifi"
200
202
  }
201
203
  }
202
- return quoteProvider
203
- }, [quoteProvider, selectedToken?.chainId, toChainId])
204
+ return swapProvider
205
+ }, [swapProvider, selectedToken?.chainId, toChainId])
206
+
207
+ const effectiveBridgeProvider = useMemo(() => {
208
+ if (!bridgeProvider || bridgeProvider === "auto") {
209
+ if (
210
+ selectedToken?.chainId === etherlink.id ||
211
+ toChainId === etherlink.id
212
+ ) {
213
+ return "lifi"
214
+ }
215
+ }
216
+ return bridgeProvider
217
+ }, [bridgeProvider, selectedToken?.chainId, toChainId])
204
218
 
205
219
  const [amount, setAmount] = useState(
206
220
  tradeType === TradeType.EXACT_INPUT ? "" : (toAmount ?? ""),
@@ -561,10 +575,7 @@ export function useSendForm({
561
575
  return input
562
576
  }, [selectedDestToken, destTokenAddress, selectedDestinationChain?.id])
563
577
 
564
- const { tokenPrices: destTokenPrices } = useTokenPrices(
565
- destTokenPricesInput,
566
- trailsClient,
567
- )
578
+ const { tokenPrices: destTokenPrices } = useTokenPrices(destTokenPricesInput)
568
579
 
569
580
  const { tokenPrices: sourceTokenPrices } = useTokenPrices(
570
581
  selectedToken
@@ -576,7 +587,6 @@ export function useSendForm({
576
587
  },
577
588
  ]
578
589
  : [],
579
- trailsClient,
580
590
  )
581
591
 
582
592
  // Update selectedChain when toChainId prop changes
@@ -1071,7 +1081,11 @@ export function useSendForm({
1071
1081
  p.chainId.toString() === (selectedToken.chainId || 0).toString(),
1072
1082
  )?.url ?? undefined,
1073
1083
  originNativeTokenPriceUsd: nativeTokenPriceUsd,
1074
- quoteProvider: effectiveQuoteProvider,
1084
+ swapProvider: effectiveSwapProvider as RouteProvider | null | undefined,
1085
+ bridgeProvider: effectiveBridgeProvider as
1086
+ | RouteProvider
1087
+ | null
1088
+ | undefined,
1075
1089
  mode,
1076
1090
  fundMethod,
1077
1091
  checkoutOnHandlers,
@@ -1166,7 +1180,8 @@ export function useSendForm({
1166
1180
  selectedDestToken,
1167
1181
  selectedDestinationChain,
1168
1182
  selectedToken,
1169
- effectiveQuoteProvider,
1183
+ effectiveSwapProvider,
1184
+ effectiveBridgeProvider,
1170
1185
  fundMethod,
1171
1186
  amountRaw,
1172
1187
  checkoutOnHandlers,
@@ -13,7 +13,6 @@ import {
13
13
  formatPriceFields,
14
14
  } from "../../tokenBalances.js"
15
15
  import { useSupportedTokens, useGetTokenImageUrl } from "../../tokens.js"
16
- import { useIndexerGatewayClient } from "../../indexerClient.js"
17
16
  import { useTrailsClient } from "../../trailsClient.js"
18
17
  import { useTokenPrices } from "../../prices.js"
19
18
  import { logger } from "../../logger.js"
@@ -182,14 +181,13 @@ export function useTokenList({
182
181
  const [selectedToken, setSelectedToken] = useState<Token | null>(null)
183
182
  const [searchQuery, setSearchQuery] = useState("")
184
183
  const { address } = useAccount()
185
- const indexerGatewayClient = useIndexerGatewayClient()
186
184
  const trailsClient = useTrailsClient()
187
185
  const { getTokenImageUrl } = useGetTokenImageUrl()
188
186
  const {
189
187
  sortedTokens: allSortedTokens,
190
188
  isLoadingSortedTokens,
191
189
  balanceError,
192
- } = useTokenBalances(address as Address.Address, indexerGatewayClient)
190
+ } = useTokenBalances(address as Address.Address)
193
191
 
194
192
  // Use cached supported tokens hook
195
193
  const { supportedTokens, isLoadingTokens: isLoadingSupportedTokens } =
@@ -364,7 +362,7 @@ export function useTokenList({
364
362
  const {
365
363
  tokenPrices: supportedTokenPrices,
366
364
  isLoadingTokenPrices: isLoadingSupportedTokenPrices,
367
- } = useTokenPrices(supportedTokensForPricing, trailsClient)
365
+ } = useTokenPrices(supportedTokensForPricing)
368
366
 
369
367
  // Determine loading state based on fund method and allSupportedTokens
370
368
  const isLoadingTokens =
@@ -481,7 +481,8 @@ export function useTrailsSendTransaction(
481
481
  paymasterUrl: params?.paymasterUrl,
482
482
  slippageTolerance: params?.slippageTolerance?.toString(),
483
483
  originNativeTokenPriceUsd,
484
- quoteProvider: params?.quoteProvider,
484
+ swapProvider: params?.swapProvider,
485
+ bridgeProvider: params?.bridgeProvider,
485
486
  commitIntentFn: commitIntentMutation.mutateAsync,
486
487
  executeIntentFn: executeIntentMutation.mutateAsync,
487
488
  checkoutOnHandlers: params?.checkoutOnHandlers,
@@ -199,7 +199,8 @@ export type TrailsWidgetProps = {
199
199
  buttonText?: string
200
200
  customCss?: string | Record<string, string>
201
201
  disableCss?: boolean
202
- quoteProvider?: string
202
+ swapProvider?: string
203
+ bridgeProvider?: string
203
204
  slippageTolerance?: number | string
204
205
  priceImpactWarningThresholdBps?: number
205
206
  priceImpactWarningMessage?: string
@@ -479,7 +480,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
479
480
  onClose,
480
481
  paymasterUrls,
481
482
  buttonText,
482
- quoteProvider,
483
+ swapProvider,
484
+ bridgeProvider,
483
485
  priceImpactWarningThresholdBps,
484
486
  priceImpactWarningMessage,
485
487
  priceImpactFallbackBridgeUrl,
@@ -1960,7 +1962,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
1960
1962
  onError={handleSendError}
1961
1963
  paymasterUrls={paymasterUrls}
1962
1964
  setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
1963
- quoteProvider={quoteProvider}
1965
+ swapProvider={swapProvider}
1966
+ bridgeProvider={bridgeProvider}
1964
1967
  fundMethod={selectedFundMethod}
1965
1968
  onNavigateToOnramp={handleNavigateToOnramp}
1966
1969
  onAmountUpdate={(amount: string) => {
@@ -2015,7 +2018,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
2015
2018
  onError={handleSendError}
2016
2019
  paymasterUrls={paymasterUrls}
2017
2020
  setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
2018
- quoteProvider={quoteProvider}
2021
+ swapProvider={swapProvider}
2022
+ bridgeProvider={bridgeProvider}
2019
2023
  fundMethod={selectedFundMethod}
2020
2024
  onNavigateToOnramp={handleNavigateToOnramp}
2021
2025
  checkoutOnHandlers={checkoutOnHandlers}
@@ -2271,7 +2275,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
2271
2275
  onSend={handleOnSend}
2272
2276
  paymasterUrls={paymasterUrls}
2273
2277
  setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
2274
- quoteProvider={quoteProvider}
2278
+ swapProvider={swapProvider}
2279
+ bridgeProvider={bridgeProvider}
2275
2280
  fundMethod={selectedFundMethod}
2276
2281
  onNavigateToOnramp={handleNavigateToOnramp}
2277
2282
  checkoutOnHandlers={checkoutOnHandlers}
@@ -2311,7 +2316,8 @@ const WidgetContent = forwardRef<TrailsWidgetRef>((_, ref) => {
2311
2316
  onError={handleSendError}
2312
2317
  paymasterUrls={paymasterUrls}
2313
2318
  setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
2314
- quoteProvider={quoteProvider}
2319
+ swapProvider={swapProvider}
2320
+ bridgeProvider={bridgeProvider}
2315
2321
  fundMethod={selectedFundMethod}
2316
2322
  onNavigateToOnramp={handleNavigateToOnramp}
2317
2323
  onAmountUpdate={undefined}