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
@@ -10,10 +10,13 @@ import { formatRelativeDate } from "../../time.js"
10
10
  import { SearchInputField } from "./SearchInputField.js"
11
11
  import { Identicon } from "./Identicon.js"
12
12
  import { truncateAddress } from "../../utils.js"
13
- import { useConnections, useAccount } from "wagmi"
13
+ import { useConnections, useAccount, useWalletClient } from "wagmi"
14
14
  import { ChevronDown } from "lucide-react"
15
15
  import { getExplorerUrl } from "../../explorer.js"
16
+ import { ErrorDisplay } from "./ErrorDisplay.js"
17
+ import { attemptSwitchChain } from "../../chainSwitch.js"
16
18
  import { logger } from "../../logger.js"
19
+ import { useTrailsRefund } from "../../refund.js"
17
20
 
18
21
  interface AccountIntentTransactionHistoryProps {
19
22
  accountAddress?: string
@@ -35,6 +38,8 @@ const ExecutionStatusBadge: React.FC<{ status?: string }> = ({ status }) => {
35
38
  return "bg-yellow-100 text-gray-800 dark:bg-yellow-900 dark:text-gray-200"
36
39
  case "refunded":
37
40
  return "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200"
41
+ case "aborted":
42
+ return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
38
43
  case "created":
39
44
  return "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200"
40
45
  default:
@@ -56,10 +61,136 @@ const ExecutionStatusBadge: React.FC<{ status?: string }> = ({ status }) => {
56
61
  const TransactionItem: React.FC<{
57
62
  transaction: IntentTransaction
58
63
  index: number
59
- }> = ({ transaction, index }) => {
64
+ onRefetch?: () => void
65
+ }> = ({ transaction, index, onRefetch }) => {
60
66
  const [isExpanded, setIsExpanded] = useState(false)
61
67
  const containerRef = useRef<HTMLDivElement>(null)
62
68
  const dateInfo = formatRelativeDate(transaction.createdAt)
69
+ const { data: walletClient } = useWalletClient()
70
+
71
+ // Track refund transaction hash per intent
72
+ const [refundTxHash, setRefundTxHash] = useState<`0x${string}` | null>(null)
73
+
74
+ // Check if intent is not completed
75
+ const isCompleted =
76
+ transaction.executionStatus === "executed" ||
77
+ transaction.executionStatus === "completed" ||
78
+ transaction.executionStatus === "succeeded"
79
+
80
+ // Hook for refunding intents (recover) - call early to get hasIntentBalance
81
+ const {
82
+ signPayload,
83
+ getRefundTx,
84
+ isLoadingIntent,
85
+ isLoadingBalances,
86
+ intentError,
87
+ balancesError,
88
+ hasIntentBalance,
89
+ refetchIntent,
90
+ } = useTrailsRefund({
91
+ intentId:
92
+ !isCompleted && transaction.intentId && !refundTxHash
93
+ ? transaction.intentId
94
+ : "",
95
+ walletClient: walletClient || undefined,
96
+ })
97
+
98
+ // Create a wrapper for onRefetch that also refetches intent data and balances
99
+ // This ensures the recover button state is updated when the refresh button is clicked
100
+ const enhancedOnRefetch = useMemo(() => {
101
+ if (!onRefetch) return undefined
102
+ return () => {
103
+ onRefetch()
104
+ // Refetch intent data and balances to update recover button state
105
+ if (!isCompleted && transaction.intentId && !refundTxHash) {
106
+ refetchIntent()
107
+ }
108
+ }
109
+ }, [
110
+ onRefetch,
111
+ isCompleted,
112
+ transaction.intentId,
113
+ refundTxHash,
114
+ refetchIntent,
115
+ ])
116
+
117
+ const canRecover =
118
+ !isCompleted &&
119
+ transaction.intentId &&
120
+ walletClient &&
121
+ !refundTxHash &&
122
+ hasIntentBalance
123
+
124
+ const [isRecovering, setIsRecovering] = useState(false)
125
+ const [recoverError, setRecoverError] = useState<string | null>(null)
126
+
127
+ const handleRecover = async () => {
128
+ if (!transaction.intentId || !walletClient) {
129
+ logger.console.error(
130
+ "[AccountIntentTransactionHistory] Cannot recover: missing intentId or walletClient",
131
+ )
132
+ return
133
+ }
134
+
135
+ try {
136
+ setIsRecovering(true)
137
+ setRecoverError(null)
138
+
139
+ // Sign the payload
140
+ const { signature, payload } = await signPayload()
141
+
142
+ // Get the refund transaction
143
+ const refundTx = await getRefundTx({
144
+ signedHash: signature,
145
+ payload,
146
+ })
147
+
148
+ // Ensure we're on the correct chain before sending
149
+ // attemptSwitchChain already checks if we're on the correct chain
150
+ await attemptSwitchChain({
151
+ walletClient,
152
+ desiredChainId: refundTx.chainId,
153
+ })
154
+
155
+ // Send the refund transaction with explicit chain
156
+ const hash = await walletClient.sendTransaction({
157
+ to: refundTx.to,
158
+ data: refundTx.data,
159
+ chain: refundTx.chain,
160
+ })
161
+
162
+ logger.console.log(
163
+ "[AccountIntentTransactionHistory] Refund transaction sent",
164
+ { intentId: transaction.intentId, hash },
165
+ )
166
+
167
+ // Store the transaction hash to show explorer link
168
+ setRefundTxHash(hash)
169
+ setRecoverError(null)
170
+
171
+ // Refetch history after recovery
172
+ if (enhancedOnRefetch) {
173
+ setTimeout(() => {
174
+ enhancedOnRefetch()
175
+ }, 2000)
176
+ }
177
+ } catch (error) {
178
+ logger.console.error(
179
+ "[AccountIntentTransactionHistory] Failed to recover intent",
180
+ { intentId: transaction.intentId, error },
181
+ )
182
+ // Set error message for display
183
+ const errorMessage =
184
+ error instanceof Error
185
+ ? error.message
186
+ : typeof error === "string"
187
+ ? error
188
+ : "Failed to recover intent. Please try again."
189
+ setRecoverError(errorMessage)
190
+ } finally {
191
+ setIsRecovering(false)
192
+ }
193
+ }
63
194
 
64
195
  // Determine the last known intent transaction hash and chain ID
65
196
  // Priority: destinationTransactionHash > destinationIntentTxHash > originTransactionHash > originIntentTxHash > txnHash
@@ -119,11 +250,22 @@ const TransactionItem: React.FC<{
119
250
  })
120
251
  }, [lastKnownTxHash])
121
252
 
253
+ // Get explorer URL for refund transaction if available
254
+ const refundExplorerUrl = useMemo(() => {
255
+ if (!refundTxHash || !transaction.originChainId) {
256
+ return null
257
+ }
258
+ return getExplorerUrl({
259
+ txHash: refundTxHash,
260
+ chainId: transaction.originChainId,
261
+ })
262
+ }, [refundTxHash, transaction.originChainId])
263
+
122
264
  return (
123
265
  <div className="border border-gray-200 dark:border-gray-700 rounded-lg p-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
124
266
  {/* Header with status and metadata */}
125
267
  <div className="flex items-center justify-between mb-2">
126
- <div className="flex items-center gap-2">
268
+ <div className="flex items-center gap-2 flex-wrap">
127
269
  <span className="text-xs font-medium text-gray-600 dark:text-gray-400">
128
270
  #{index + 1}
129
271
  </span>
@@ -209,9 +351,58 @@ const TransactionItem: React.FC<{
209
351
  </div>
210
352
  </div>
211
353
 
212
- {/* More Details Button - only visible when collapsed */}
354
+ {/* Action Buttons - only visible when collapsed */}
213
355
  {!isExpanded && (
214
- <div className="flex justify-center">
356
+ <div className="flex flex-col items-center gap-2">
357
+ {refundTxHash && refundExplorerUrl ? (
358
+ <a
359
+ href={refundExplorerUrl}
360
+ target="_blank"
361
+ rel="noopener noreferrer"
362
+ className="inline-flex items-center gap-1 px-3 py-1.5 rounded-md font-medium transition-colors border border-solid text-sm bg-white border-gray-200 text-black hover:bg-gray-50 hover:text-gray-900 dark:bg-gray-900 dark:border-gray-700 dark:text-blue-300 dark:hover:bg-gray-800 dark:hover:text-blue-200"
363
+ aria-label="View refund transaction on explorer"
364
+ >
365
+ View on Explorer
366
+ <svg
367
+ className="w-4 h-4 ml-1 text-black dark:text-blue-300"
368
+ fill="none"
369
+ viewBox="0 0 24 24"
370
+ stroke="currentColor"
371
+ aria-hidden="true"
372
+ >
373
+ <title>External Link</title>
374
+ <path
375
+ strokeLinecap="round"
376
+ strokeLinejoin="round"
377
+ strokeWidth={2}
378
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
379
+ />
380
+ </svg>
381
+ </a>
382
+ ) : canRecover ? (
383
+ <button
384
+ type="button"
385
+ onClick={handleRecover}
386
+ disabled={
387
+ isRecovering ||
388
+ isLoadingIntent ||
389
+ isLoadingBalances ||
390
+ !!intentError ||
391
+ !!balancesError
392
+ }
393
+ className="flex items-center justify-center gap-1 py-1 px-3 rounded-md transition-colors cursor-pointer text-xs font-medium text-white bg-orange-600 hover:bg-orange-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
394
+ aria-label="Recover intent"
395
+ >
396
+ {isRecovering || isLoadingIntent || isLoadingBalances
397
+ ? "Recovering..."
398
+ : "Recover"}
399
+ </button>
400
+ ) : null}
401
+ {recoverError && (
402
+ <div className="w-full">
403
+ <ErrorDisplay error={recoverError} severity="error" />
404
+ </div>
405
+ )}
215
406
  <button
216
407
  type="button"
217
408
  onClick={() => setIsExpanded(true)}
@@ -731,6 +922,7 @@ export const AccountIntentTransactionHistory: React.FC<
731
922
  key={`${transaction.originIntentAddress}-${transaction.destinationIntentAddress}-${index}`}
732
923
  transaction={transaction}
733
924
  index={page * ITEMS_PER_PAGE + index}
925
+ onRefetch={refetch}
734
926
  />
735
927
  ))}
736
928
  </div>
@@ -51,7 +51,8 @@ interface ClassicSwapProps {
51
51
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
52
52
  paymasterUrls?: Array<{ chainId: number; url: string }>
53
53
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
54
- quoteProvider?: string
54
+ swapProvider?: string
55
+ bridgeProvider?: string
55
56
  fundMethod?: string
56
57
  onNavigateToOnramp?: (
57
58
  props: {
@@ -90,7 +91,8 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
90
91
  onWaitingForWalletConfirm,
91
92
  paymasterUrls,
92
93
  setWalletConfirmRetryHandler,
93
- quoteProvider,
94
+ swapProvider,
95
+ bridgeProvider,
94
96
  fundMethod,
95
97
  onNavigateToOnramp,
96
98
  onAmountUpdate,
@@ -201,7 +203,8 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
201
203
  selectedToken: originToken as any,
202
204
  setWalletConfirmRetryHandler,
203
205
  tradeType: tradeType,
204
- quoteProvider,
206
+ swapProvider,
207
+ bridgeProvider,
205
208
  fundMethod,
206
209
  mode,
207
210
  onNavigateToOnramp,
@@ -24,7 +24,8 @@ interface EarnProps {
24
24
  onSend: (amount: string, recipient: string) => void
25
25
  paymasterUrls?: Array<{ chainId: number; url: string }>
26
26
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
27
- quoteProvider?: string
27
+ swapProvider?: string
28
+ bridgeProvider?: string
28
29
  fundMethod?: string
29
30
  onNavigateToOnramp?: (
30
31
  props: {
@@ -54,7 +55,8 @@ export const Earn: React.FC<EarnProps> = ({
54
55
  onSend,
55
56
  paymasterUrls,
56
57
  setWalletConfirmRetryHandler,
57
- quoteProvider,
58
+ swapProvider,
59
+ bridgeProvider,
58
60
  fundMethod,
59
61
  onNavigateToOnramp,
60
62
  checkoutOnHandlers,
@@ -117,7 +119,8 @@ export const Earn: React.FC<EarnProps> = ({
117
119
  onSend={onSend}
118
120
  paymasterUrls={paymasterUrls}
119
121
  setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
120
- quoteProvider={quoteProvider}
122
+ swapProvider={swapProvider}
123
+ bridgeProvider={bridgeProvider}
121
124
  fundMethod={fundMethod}
122
125
  onNavigateToOnramp={onNavigateToOnramp}
123
126
  checkoutOnHandlers={checkoutOnHandlers}
@@ -82,8 +82,10 @@ export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
82
82
 
83
83
  const colors = getSeverityColors()
84
84
 
85
- // Only show if we have a prettified error (either provided or computed)
86
- if (!computedErrorPrettified) {
85
+ // Show error if we have a prettified error or a raw error message
86
+ // If we can't prettify it, show the raw error
87
+ const displayError = computedErrorPrettified || error
88
+ if (!displayError) {
87
89
  return null
88
90
  }
89
91
 
@@ -107,8 +109,8 @@ export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({
107
109
  />
108
110
  </svg>
109
111
  <div className="flex-1 min-w-0">
110
- <p className={`text-sm ${colors.text}`}>{computedErrorPrettified}</p>
111
- {error && error !== computedErrorPrettified && (
112
+ <p className={`text-sm ${colors.text}`}>{displayError}</p>
113
+ {error && error !== displayError && (
112
114
  <div className="mt-2">
113
115
  <button
114
116
  type="button"
@@ -46,7 +46,8 @@ interface FundProps {
46
46
  paymasterUrls?: Array<{ chainId: number; url: string }>
47
47
  isSequenceWallet?: boolean
48
48
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
49
- quoteProvider?: string
49
+ swapProvider?: string
50
+ bridgeProvider?: string
50
51
  fundMethod?: string
51
52
  onNavigateToOnramp?: (
52
53
  props: {
@@ -82,7 +83,8 @@ export const Fund: React.FC<FundProps> = ({
82
83
  paymasterUrls,
83
84
  isSequenceWallet = false,
84
85
  setWalletConfirmRetryHandler,
85
- quoteProvider,
86
+ swapProvider,
87
+ bridgeProvider,
86
88
  fundMethod,
87
89
  onNavigateToOnramp,
88
90
  checkoutOnHandlers,
@@ -164,7 +166,8 @@ export const Fund: React.FC<FundProps> = ({
164
166
  selectedToken: originToken as any,
165
167
  setWalletConfirmRetryHandler,
166
168
  tradeType: TradeType.EXACT_INPUT,
167
- quoteProvider,
169
+ swapProvider,
170
+ bridgeProvider,
168
171
  fundMethod,
169
172
  mode,
170
173
  onNavigateToOnramp,
@@ -25,7 +25,8 @@ interface FundProps {
25
25
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
26
26
  paymasterUrls?: Array<{ chainId: number; url: string }>
27
27
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
28
- quoteProvider?: string
28
+ swapProvider?: string
29
+ bridgeProvider?: string
29
30
  fundMethod?: string
30
31
  onNavigateToOnramp?: (
31
32
  props: {
@@ -54,7 +54,8 @@ interface PayProps {
54
54
  paymasterUrls?: Array<{ chainId: number; url: string }>
55
55
  isSequenceWallet?: boolean
56
56
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
57
- quoteProvider?: string
57
+ swapProvider?: string
58
+ bridgeProvider?: string
58
59
  fundMethod?: string
59
60
  onNavigateToOnramp?: (
60
61
  props: {
@@ -92,7 +93,8 @@ export const Pay: React.FC<PayProps> = ({
92
93
  paymasterUrls,
93
94
  isSequenceWallet = false,
94
95
  setWalletConfirmRetryHandler,
95
- quoteProvider,
96
+ swapProvider,
97
+ bridgeProvider,
96
98
  fundMethod,
97
99
  onNavigateToOnramp,
98
100
  checkoutOnHandlers,
@@ -140,6 +142,7 @@ export const Pay: React.FC<PayProps> = ({
140
142
  useState(false)
141
143
  const [showDestinationChainList, setShowDestinationChainList] =
142
144
  useState(false)
145
+ const [showQuoteDetails, setShowQuoteDetails] = useState(false)
143
146
  const inputRef = useRef<HTMLInputElement>(null)
144
147
  const paymentRequestInputRef = useRef<HTMLInputElement>(null)
145
148
  const lastAutoSelectedRef = useRef<string | null>(null)
@@ -217,7 +220,8 @@ export const Pay: React.FC<PayProps> = ({
217
220
  selectedToken: originToken as any,
218
221
  setWalletConfirmRetryHandler,
219
222
  tradeType: TradeType.EXACT_OUTPUT, // Key difference: using EXACT_OUTPUT
220
- quoteProvider,
223
+ swapProvider,
224
+ bridgeProvider,
221
225
  fundMethod,
222
226
  mode,
223
227
  onNavigateToOnramp,
@@ -682,16 +686,19 @@ export const Pay: React.FC<PayProps> = ({
682
686
  </>
683
687
  ) : (
684
688
  <>
685
- ≈ {prepareSendQuote?.originAmountUsdDisplay || "$0.00"}
689
+ ≈ {prepareSendQuote?.originAmountUsdDisplay || "$0.00"}{" "}
686
690
  {prepareSendQuote?.fees?.totalFeeAmountUsd &&
687
691
  parseFloat(prepareSendQuote.fees.totalFeeAmountUsd) >
688
692
  0 ? (
689
- <span className="text-green-600 dark:text-green-400">
690
- {" "}
693
+ <button
694
+ type="button"
695
+ onClick={() => setShowQuoteDetails(true)}
696
+ className="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer transition-colors"
697
+ >
691
698
  (incl.{" "}
692
699
  {prepareSendQuote.fees.totalFeeAmountUsdDisplay}{" "}
693
700
  fees)
694
- </span>
701
+ </button>
695
702
  ) : null}
696
703
  </>
697
704
  )
@@ -993,6 +1000,7 @@ export const Pay: React.FC<PayProps> = ({
993
1000
  quote={prepareSendQuote}
994
1001
  showContent={true}
995
1002
  isRefetching={isLoadingQuote}
1003
+ initialExpanded={showQuoteDetails}
996
1004
  />
997
1005
  </div>
998
1006
  )}
@@ -54,7 +54,8 @@ interface PoolDepositProps {
54
54
  onSend: (amount: string, recipient: string) => void
55
55
  paymasterUrls?: Array<{ chainId: number; url: string }>
56
56
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
57
- quoteProvider?: string
57
+ swapProvider?: string
58
+ bridgeProvider?: string
58
59
  fundMethod?: string
59
60
  onNavigateToOnramp?: (
60
61
  props: {
@@ -84,7 +85,8 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
84
85
  onSend,
85
86
  paymasterUrls,
86
87
  setWalletConfirmRetryHandler,
87
- quoteProvider,
88
+ swapProvider,
89
+ bridgeProvider,
88
90
  fundMethod,
89
91
  onNavigateToOnramp,
90
92
  checkoutOnHandlers,
@@ -205,7 +207,8 @@ export const PoolDeposit: React.FC<PoolDepositProps> = ({
205
207
  selectedToken: originToken as any,
206
208
  setWalletConfirmRetryHandler,
207
209
  tradeType: TradeType.EXACT_INPUT,
208
- quoteProvider,
210
+ swapProvider,
211
+ bridgeProvider,
209
212
  fundMethod,
210
213
  mode,
211
214
  onNavigateToOnramp,
@@ -26,6 +26,7 @@ interface QuoteDetailsProps {
26
26
  swapMode?: boolean
27
27
  compact?: boolean
28
28
  isRefetching?: boolean
29
+ initialExpanded?: boolean
29
30
  }
30
31
 
31
32
  export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
@@ -36,10 +37,11 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
36
37
  swapMode,
37
38
  compact = false,
38
39
  isRefetching = false,
40
+ initialExpanded = false,
39
41
  }) => {
40
42
  const [showCalldata, setShowCalldata] = useState(false)
41
43
  const [showOriginRate, setShowOriginRate] = useState(true)
42
- const [isExpanded, setIsExpanded] = useState(false)
44
+ const [isExpanded, setIsExpanded] = useState(initialExpanded)
43
45
  const [intentIdCopied, setIntentIdCopied] = useState(false)
44
46
  const containerRef = useRef<HTMLDivElement>(null)
45
47
  const calldataRef = useRef<HTMLDivElement>(null)
@@ -57,6 +59,13 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
57
59
  }
58
60
  }, [showContent])
59
61
 
62
+ // Sync with initialExpanded prop changes
63
+ useEffect(() => {
64
+ if (initialExpanded !== undefined) {
65
+ setIsExpanded(initialExpanded)
66
+ }
67
+ }, [initialExpanded])
68
+
60
69
  // Notify parent when expansion state changes
61
70
  useEffect(() => {
62
71
  onExpand?.(isExpanded)
@@ -80,9 +89,11 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
80
89
 
81
90
  if (!showContent) return null
82
91
 
83
- // Use grandTotalUsd from normalized quote object
84
- const grandTotalUsd = quote?.grandTotalUsd ?? 0
85
- const grandTotalUsdDisplay = quote?.grandTotalUsdDisplay ?? "$0.00"
92
+ // Use totalFeeAmountUsd from fees prop (this is the total fee from the API)
93
+ const totalFeeUsd = quote?.fees?.totalFeeAmountUsd
94
+ ? parseFloat(quote.fees.totalFeeAmountUsd)
95
+ : 0
96
+ const totalFeeUsdDisplay = quote?.fees?.totalFeeAmountUsdDisplay ?? "$0.00"
86
97
 
87
98
  return (
88
99
  <div
@@ -136,7 +147,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
136
147
  </span>
137
148
  </div>
138
149
  )}
139
- {grandTotalUsd > 0 && (
150
+ {totalFeeUsd > 0 && (
140
151
  <div className="flex items-center gap-1">
141
152
  <svg
142
153
  aria-hidden="true"
@@ -154,7 +165,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
154
165
  d="M32 64C32 28.7 60.7 0 96 0H256c35.3 0 64 28.7 64 64V256h8c48.6 0 88 39.4 88 88v32c0 13.3 10.7 24 24 24s24-10.7 24-24V222c-27.6-7.1-48-32.2-48-62V96L384 64c-8.8-8.8-8.8-23.2 0-32s23.2-8.8 32 0l77.3 77.3c12 12 18.7 28.3 18.7 45.3V168v24 32V376c0 39.8-32.2 72-72 72s-72-32.2-72-72V344c0-22.1-17.9-40-40-40h-8V448c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32V64zM96 80v96c0 8.8 7.2 16 16 16H240c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H112c-8.8 0-16 7.2-16 16z"
155
166
  />
156
167
  </svg>
157
- <span className="font-medium">{grandTotalUsdDisplay}</span>
168
+ <span className="font-medium">{totalFeeUsdDisplay}</span>
158
169
  </div>
159
170
  )}
160
171
  <ChevronDown
@@ -214,15 +225,15 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
214
225
  {quote?.trailsFeeBreakdown &&
215
226
  (quote?.trailsFeeBreakdown.originRelayFee ||
216
227
  quote?.trailsFeeBreakdown.destinationRelayFee) &&
217
- grandTotalUsd > 0 && (
228
+ totalFeeUsd > 0 && (
218
229
  <>
219
230
  <Row variant="bold">
220
231
  <RowLabel tooltip="The total fees for this transaction, including gas costs and all provider fees">
221
232
  Fees total
222
233
  </RowLabel>
223
234
  <RowValue>
224
- <span title={`$${grandTotalUsd}`}>
225
- ≈ {grandTotalUsdDisplay}
235
+ <span title={`$${totalFeeUsd}`}>
236
+ ≈ {totalFeeUsdDisplay}
226
237
  </span>
227
238
  </RowValue>
228
239
  </Row>
@@ -328,24 +339,31 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
328
339
  const providerFee = quote.trailsFeeBreakdown.providerFee
329
340
  const isCrossChain =
330
341
  quote.originChain.id !== quote.destinationChain.id
331
- const providerName = quote.quoteProvider?.name || ""
342
+
343
+ const routeProviders = quote.routeProviders || []
344
+
345
+ // Filter out providers with empty names and get the first one
346
+ const validProviders = routeProviders.filter(
347
+ (p) => p.name,
348
+ )
349
+ const firstProvider = validProviders[0]
332
350
 
333
351
  return (
334
352
  <Row variant="light">
335
353
  <RowLabel tooltip="Fee charged by the bridge/swap provider for executing the transaction">
336
354
  {isCrossChain ? (
337
- providerName ? (
355
+ firstProvider ? (
338
356
  <>
339
357
  Bridge
340
358
  <span>
341
359
  (
342
360
  <a
343
- href={quote.quoteProvider?.url}
361
+ href={firstProvider.url}
344
362
  target="_blank"
345
363
  rel="noopener noreferrer"
346
364
  className="hover:underline inline-flex items-center gap-0.5 text-blue-500"
347
365
  >
348
- {providerName}
366
+ {firstProvider.name}
349
367
  <ExternalLink className="size-3" />
350
368
  </a>
351
369
  )
@@ -354,18 +372,18 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
354
372
  ) : (
355
373
  <>Bridge:</>
356
374
  )
357
- ) : providerName ? (
375
+ ) : firstProvider ? (
358
376
  <>
359
377
  Liquidity Provider{" "}
360
378
  <span>
361
379
  (
362
380
  <a
363
- href={quote.quoteProvider?.url}
381
+ href={firstProvider.url}
364
382
  target="_blank"
365
383
  rel="noopener noreferrer"
366
384
  className="hover:underline inline-flex items-center gap-0.5 text-blue-500"
367
385
  >
368
- {providerName}
386
+ {firstProvider.name}
369
387
  <ExternalLink className="size-3" />
370
388
  </a>
371
389
  )
@@ -384,28 +402,6 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
384
402
  )
385
403
  })()}
386
404
 
387
- {/* Swap Fees (implicit costs) */}
388
- {quote.swapFeesUsdDisplay &&
389
- (() => {
390
- const swapFeesUsd = quote.swapFeesUsd ?? 0
391
-
392
- // Only show if swap fees > 0
393
- if (swapFeesUsd <= 0) return null
394
-
395
- return (
396
- <Row variant="light">
397
- <RowLabel tooltip="Implicit costs including DEX fees, liquidity provider fees, and routing costs">
398
- Swap
399
- </RowLabel>
400
- <RowValue>
401
- <span title={`$${swapFeesUsd}`}>
402
- {quote.swapFeesUsdDisplay}
403
- </span>
404
- </RowValue>
405
- </Row>
406
- )
407
- })()}
408
-
409
405
  {/* Trails Platform Fee */}
410
406
  {hasTrailsFee &&
411
407
  quote.trailsFeeBreakdown.trailsFee &&
@@ -25,7 +25,8 @@ interface SwapProps {
25
25
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
26
26
  paymasterUrls?: Array<{ chainId: number; url: string }>
27
27
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
28
- quoteProvider?: string
28
+ swapProvider?: string
29
+ bridgeProvider?: string
29
30
  fundMethod?: string
30
31
  onNavigateToOnramp?: (
31
32
  props: {