0xtrails 0.1.2 → 0.1.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 (158) hide show
  1. package/dist/address.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +86 -1
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/apiClient.d.ts +1 -1
  5. package/dist/apiClient.d.ts.map +1 -1
  6. package/dist/{ccip-BmFTEOaB.js → ccip-dLSEJjCf.js} +55 -55
  7. package/dist/cctpqueue.d.ts +1 -1
  8. package/dist/cctpqueue.d.ts.map +1 -1
  9. package/dist/chains.d.ts +9 -3
  10. package/dist/chains.d.ts.map +1 -1
  11. package/dist/constants.d.ts +1 -0
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/decoders.d.ts +58 -0
  14. package/dist/decoders.d.ts.map +1 -0
  15. package/dist/ens.d.ts +13 -0
  16. package/dist/ens.d.ts.map +1 -0
  17. package/dist/error.d.ts +9 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/{index-BPsVj7zK.js → index-BXbaLmtt.js} +28779 -25738
  20. package/dist/index.js +2 -2
  21. package/dist/intents.d.ts +4 -4
  22. package/dist/intents.d.ts.map +1 -1
  23. package/dist/lifi.d.ts +4 -0
  24. package/dist/lifi.d.ts.map +1 -0
  25. package/dist/metaTxns.d.ts +1 -1
  26. package/dist/metaTxns.d.ts.map +1 -1
  27. package/dist/mode.d.ts +1 -1
  28. package/dist/mode.d.ts.map +1 -1
  29. package/dist/preconditions.d.ts +1 -1
  30. package/dist/preconditions.d.ts.map +1 -1
  31. package/dist/prepareSend.d.ts +32 -24
  32. package/dist/prepareSend.d.ts.map +1 -1
  33. package/dist/prices.d.ts +3 -1
  34. package/dist/prices.d.ts.map +1 -1
  35. package/dist/proxyCaller.d.ts +0 -1
  36. package/dist/proxyCaller.d.ts.map +1 -1
  37. package/dist/relaySdk.d.ts.map +1 -1
  38. package/dist/relayer.d.ts.map +1 -1
  39. package/dist/tokenBalances.d.ts +1 -1
  40. package/dist/tokenBalances.d.ts.map +1 -1
  41. package/dist/tokens.d.ts +2 -1
  42. package/dist/tokens.d.ts.map +1 -1
  43. package/dist/trails.d.ts +4 -4
  44. package/dist/trails.d.ts.map +1 -1
  45. package/dist/transactions.d.ts +4 -0
  46. package/dist/transactions.d.ts.map +1 -1
  47. package/dist/utils.d.ts +6 -0
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/wallets.d.ts +247 -5
  50. package/dist/wallets.d.ts.map +1 -1
  51. package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
  52. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  53. package/dist/widget/components/ConnectWallet.d.ts +1 -0
  54. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  55. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
  56. package/dist/widget/components/ErrorDisplay.d.ts +9 -0
  57. package/dist/widget/components/ErrorDisplay.d.ts.map +1 -0
  58. package/dist/widget/components/FundSendForm.d.ts +2 -2
  59. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  60. package/dist/widget/components/OriginTransferInformation.d.ts +10 -0
  61. package/dist/widget/components/OriginTransferInformation.d.ts.map +1 -0
  62. package/dist/widget/components/PaySendForm.d.ts +2 -2
  63. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  64. package/dist/widget/components/QrCode.d.ts +1 -1
  65. package/dist/widget/components/QrCode.d.ts.map +1 -1
  66. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  67. package/dist/widget/components/Receipt.d.ts.map +1 -1
  68. package/dist/widget/components/Receive.d.ts +12 -0
  69. package/dist/widget/components/Receive.d.ts.map +1 -0
  70. package/dist/widget/components/RefundAddressInput.d.ts +13 -0
  71. package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
  72. package/dist/widget/components/Swap.d.ts +47 -0
  73. package/dist/widget/components/Swap.d.ts.map +1 -0
  74. package/dist/widget/components/SwapDisplay.d.ts +9 -0
  75. package/dist/widget/components/SwapDisplay.d.ts.map +1 -0
  76. package/dist/widget/components/TokenList.d.ts +0 -2
  77. package/dist/widget/components/TokenList.d.ts.map +1 -1
  78. package/dist/widget/components/TokenSelector.d.ts +26 -0
  79. package/dist/widget/components/TokenSelector.d.ts.map +1 -0
  80. package/dist/widget/components/TransferPendingVertical.d.ts +2 -0
  81. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  82. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  83. package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
  84. package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
  85. package/dist/widget/components/WalletList.d.ts.map +1 -1
  86. package/dist/widget/components/YellowWarningAnimation.d.ts +2 -0
  87. package/dist/widget/components/YellowWarningAnimation.d.ts.map +1 -0
  88. package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
  89. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  90. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  91. package/dist/widget/hooks/useDebugScreens.d.ts +22 -0
  92. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -0
  93. package/dist/widget/hooks/useSendForm.d.ts +12 -6
  94. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  95. package/dist/widget/hooks/useTokenList.d.ts +2 -3
  96. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  97. package/dist/widget/index.js +1 -1
  98. package/dist/widget/widget.d.ts.map +1 -1
  99. package/package.json +19 -15
  100. package/src/aave.ts +13 -13
  101. package/src/address.ts +3 -0
  102. package/src/analytics.ts +192 -8
  103. package/src/apiClient.ts +1 -1
  104. package/src/cctpqueue.ts +1 -1
  105. package/src/chains.ts +45 -7
  106. package/src/constants.ts +7 -4
  107. package/src/decoders.ts +310 -0
  108. package/src/ens.ts +32 -0
  109. package/src/error.ts +101 -1
  110. package/src/intents.ts +10 -2
  111. package/src/lifi.ts +58 -0
  112. package/src/metaTxns.ts +1 -1
  113. package/src/mode.ts +1 -1
  114. package/src/morpho.ts +3 -3
  115. package/src/pools.ts +18 -18
  116. package/src/preconditions.ts +1 -1
  117. package/src/prepareSend.ts +463 -113
  118. package/src/prices.ts +26 -1
  119. package/src/proxyCaller.ts +2 -14
  120. package/src/relaySdk.ts +1 -0
  121. package/src/relayer.ts +8 -0
  122. package/src/tokenBalances.ts +24 -17
  123. package/src/tokens.ts +147 -22
  124. package/src/trails.ts +4 -4
  125. package/src/transactions.ts +35 -17
  126. package/src/utils.ts +28 -0
  127. package/src/wallets.ts +275 -35
  128. package/src/widget/compiled.css +2 -2
  129. package/src/widget/components/ChainFilterDropdown.tsx +42 -33
  130. package/src/widget/components/ChainImage.tsx +1 -1
  131. package/src/widget/components/ConnectWallet.tsx +92 -128
  132. package/src/widget/components/DebugScreensDropdown.tsx +6 -0
  133. package/src/widget/components/ErrorDisplay.tsx +150 -0
  134. package/src/widget/components/FundSendForm.tsx +78 -11
  135. package/src/widget/components/OriginTransferInformation.tsx +59 -0
  136. package/src/widget/components/PaySendForm.tsx +80 -13
  137. package/src/widget/components/QRCodeDeposit.tsx +6 -6
  138. package/src/widget/components/QrCode.tsx +278 -17
  139. package/src/widget/components/QuoteDetails.tsx +93 -25
  140. package/src/widget/components/Receipt.tsx +296 -103
  141. package/src/widget/components/Receive.tsx +146 -0
  142. package/src/widget/components/RecentTokens.tsx +1 -1
  143. package/src/widget/components/RefundAddressInput.tsx +149 -0
  144. package/src/widget/components/Swap.tsx +769 -0
  145. package/src/widget/components/SwapDisplay.tsx +68 -0
  146. package/src/widget/components/TokenList.tsx +27 -363
  147. package/src/widget/components/TokenSelector.tsx +405 -0
  148. package/src/widget/components/TransferPendingVertical.tsx +162 -112
  149. package/src/widget/components/WalletConnect.tsx +9 -7
  150. package/src/widget/components/WalletConnectionPending.tsx +157 -0
  151. package/src/widget/components/WalletList.tsx +6 -5
  152. package/src/widget/components/YellowWarningAnimation.tsx +146 -0
  153. package/src/widget/hooks/useAmountUsd.ts +3 -8
  154. package/src/widget/hooks/useCheckout.ts +3 -2
  155. package/src/widget/hooks/useDebugScreens.ts +583 -0
  156. package/src/widget/hooks/useSendForm.ts +111 -35
  157. package/src/widget/hooks/useTokenList.ts +155 -122
  158. package/src/widget/widget.tsx +503 -523
@@ -390,7 +390,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
390
390
  </div>
391
391
  )}
392
392
 
393
- {quote?.originAddress && (
393
+ {quote?.originDepositAddress && (
394
394
  <div className="flex justify-between items-center">
395
395
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
396
396
  Origin Deposit Address:
@@ -400,14 +400,14 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
400
400
  </span>
401
401
  <a
402
402
  href={getExplorerUrlForAddress({
403
- address: quote.originAddress,
403
+ address: quote.originDepositAddress,
404
404
  chainId: quote.originChain.id,
405
405
  })}
406
406
  target="_blank"
407
407
  rel="noopener noreferrer"
408
408
  className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
409
409
  >
410
- {truncateAddress(quote.originAddress)}
410
+ {truncateAddress(quote.originDepositAddress)}
411
411
  <svg
412
412
  className="w-3 h-3"
413
413
  fill="none"
@@ -426,11 +426,49 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
426
426
  </div>
427
427
  )}
428
428
 
429
+ {quote?.destinationDepositAddress &&
430
+ quote?.destinationDepositAddress !==
431
+ quote?.originDepositAddress && (
432
+ <div className="flex justify-between items-center">
433
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
434
+ Destination Deposit Address:
435
+ <Tooltip message="This is the address that will receive the tokens after any swap and/or bridge from the origin chain">
436
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
437
+ </Tooltip>
438
+ </span>
439
+ <a
440
+ href={getExplorerUrlForAddress({
441
+ address: quote.destinationDepositAddress,
442
+ chainId: quote.destinationChain.id,
443
+ })}
444
+ target="_blank"
445
+ rel="noopener noreferrer"
446
+ className="font-mono text-xs hover:underline flex items-center gap-1 text-gray-700 dark:text-gray-300"
447
+ >
448
+ {truncateAddress(quote.destinationDepositAddress)}
449
+ <svg
450
+ className="w-3 h-3"
451
+ fill="none"
452
+ stroke="currentColor"
453
+ viewBox="0 0 24 24"
454
+ aria-hidden="true"
455
+ >
456
+ <path
457
+ strokeLinecap="round"
458
+ strokeLinejoin="round"
459
+ strokeWidth={2}
460
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
461
+ />
462
+ </svg>
463
+ </a>
464
+ </div>
465
+ )}
466
+
429
467
  {quote?.destinationAddress && (
430
468
  <div className="flex justify-between items-center">
431
469
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
432
470
  Destination Target Address:
433
- <Tooltip message="This is the address that will receive the tokens after any swap and/or bridge">
471
+ <Tooltip message="This is the final execution address or recipient address">
434
472
  <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
435
473
  </Tooltip>
436
474
  </span>
@@ -476,28 +514,58 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
476
514
  </div>
477
515
  )}
478
516
 
479
- {quote?.priceImpact != null && (
480
- <div className="space-y-1">
481
- <div className="flex justify-between items-center">
482
- <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
483
- Price Impact:
484
- <Tooltip message="The percentage change in the token price caused by your trade. Higher impact means your trade affects the market price more, potentially resulting in worse rates.">
485
- <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
486
- </Tooltip>
487
- </span>
488
- <span className="font-medium text-xs text-gray-900 dark:text-white">
489
- {quote.priceImpact}%
490
- </span>
491
- </div>
492
- {quote.priceImpactUsdDisplay && (
493
- <div className="text-right">
494
- <span className="text-xs text-gray-500 dark:text-gray-400">
495
- {quote.priceImpactUsdDisplay}
496
- </span>
517
+ {quote?.priceImpact != null &&
518
+ (() => {
519
+ const priceImpactNum = Math.abs(Number(quote.priceImpact))
520
+ return (
521
+ <div className="space-y-1">
522
+ <div className="flex justify-between items-center">
523
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
524
+ Price Impact:
525
+ <Tooltip message="The percentage change in the token price caused by your trade. Higher impact means your trade affects the market price more, potentially resulting in worse rates.">
526
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
527
+ </Tooltip>
528
+ </span>
529
+ <span
530
+ className={`font-medium text-xs flex items-center gap-1 ${
531
+ priceImpactNum > 5
532
+ ? "text-red-600 dark:text-red-400"
533
+ : priceImpactNum > 0.5
534
+ ? "text-orange-600 dark:text-orange-400"
535
+ : "text-gray-900 dark:text-white"
536
+ }`}
537
+ >
538
+ {priceImpactNum > 0.5 && (
539
+ <span title="High price impact">
540
+ <svg
541
+ className="w-3 h-3"
542
+ fill="none"
543
+ stroke="currentColor"
544
+ viewBox="0 0 24 24"
545
+ aria-hidden="true"
546
+ >
547
+ <path
548
+ strokeLinecap="round"
549
+ strokeLinejoin="round"
550
+ strokeWidth={2}
551
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
552
+ />
553
+ </svg>
554
+ </span>
555
+ )}
556
+ {quote.priceImpact}%
557
+ </span>
558
+ </div>
559
+ {quote.priceImpactUsdDisplay && (
560
+ <div className="text-right">
561
+ <span className="text-xs text-gray-500 dark:text-gray-400">
562
+ ≈ {quote.priceImpactUsdDisplay}
563
+ </span>
564
+ </div>
565
+ )}
497
566
  </div>
498
- )}
499
- </div>
500
- )}
567
+ )
568
+ })()}
501
569
 
502
570
  {quote?.fees?.totalFeeAmountUsd != null && (
503
571
  <div className="flex justify-between items-center">
@@ -1,13 +1,16 @@
1
1
  import { useQuery } from "@tanstack/react-query"
2
2
  import type React from "react"
3
- import { useEffect, useState } from "react"
3
+ import { useEffect, useState, useCallback, useMemo } from "react"
4
+ import { ChevronRight } from "lucide-react"
4
5
  import type { TransactionState } from "../../transactions.js"
5
6
  import { GreenCheckAnimation } from "./GreenCheckAnimation.js"
7
+ import { YellowWarningAnimation } from "./YellowWarningAnimation.js"
6
8
  import { getTxTimeDiff } from "../../transactions.js"
7
9
  import { QuoteDetails } from "./QuoteDetails.js"
8
10
  import type { PrepareSendQuote } from "../../prepareSend.js"
9
11
  import { truncateAddress } from "../../address.js"
10
12
  import { formatElapsed } from "../../utils.js"
13
+ import { ChainImage } from "./ChainImage.js"
11
14
 
12
15
  interface ReceiptProps {
13
16
  onSendAnother: () => void
@@ -55,8 +58,9 @@ function useTxTimeDiff(firstTx?: TransactionState, lastTx?: TransactionState) {
55
58
 
56
59
  // Custom hook to compute finalExplorerUrl and completionTimeSeconds
57
60
  function useReceipt(transactionStates?: TransactionState[]) {
58
- const finalExplorerUrl =
59
- transactionStates?.[transactionStates.length - 1]?.explorerUrl
61
+ const lastTx = transactionStates?.[transactionStates.length - 1]
62
+ const finalExplorerUrl = lastTx?.explorerUrl
63
+ const finalChainId = lastTx?.chainId
60
64
 
61
65
  // Only consider confirmed transactions
62
66
  const confirmedTxs = (transactionStates || []).filter(
@@ -67,10 +71,11 @@ function useReceipt(transactionStates?: TransactionState[]) {
67
71
  const last = confirmedTxs[confirmedTxs.length - 1]
68
72
 
69
73
  const completionTimeSeconds = useTxTimeDiff(first, last)
70
- return { finalExplorerUrl, completionTimeSeconds }
74
+ return { finalExplorerUrl, completionTimeSeconds, finalChainId }
71
75
  }
72
76
 
73
77
  export const Receipt: React.FC<ReceiptProps> = ({
78
+ onSendAnother,
74
79
  onClose,
75
80
  renderInline = false,
76
81
  transactionStates = [],
@@ -78,6 +83,8 @@ export const Receipt: React.FC<ReceiptProps> = ({
78
83
  quote,
79
84
  }) => {
80
85
  const [showContent, setShowContent] = useState(false)
86
+ const [showRefundInfo, setShowRefundInfo] = useState(false)
87
+ const [refundMessage, setRefundMessage] = useState<string | null>(null)
81
88
 
82
89
  useEffect(() => {
83
90
  const timer = setTimeout(() => {
@@ -87,13 +94,155 @@ export const Receipt: React.FC<ReceiptProps> = ({
87
94
  return () => clearTimeout(timer)
88
95
  }, [])
89
96
 
90
- const { finalExplorerUrl, completionTimeSeconds: calculatedCompletionTime } =
91
- useReceipt(transactionStates)
97
+ // Helper function to check if a specific transaction has meta tx errors
98
+ const hasMetaTxError = useCallback((tx: TransactionState) => {
99
+ return tx?.decodedGuestModuleEvents?.some(
100
+ (event) => event.type === "CallFailed",
101
+ )
102
+ }, [])
103
+
104
+ const showMetaTxError = useMemo(() => {
105
+ return transactionStates.some((tx) => hasMetaTxError(tx))
106
+ }, [transactionStates, hasMetaTxError])
107
+
108
+ const {
109
+ finalExplorerUrl,
110
+ finalChainId,
111
+ completionTimeSeconds: calculatedCompletionTime,
112
+ } = useReceipt(transactionStates)
92
113
 
93
114
  // Use provided totalCompletionSeconds if available, otherwise use calculated time
94
115
  const completionTimeSeconds =
95
116
  totalCompletionSeconds ?? calculatedCompletionTime
96
117
 
118
+ // Detect successful refund events on origin or sweep on destination
119
+ useEffect(() => {
120
+ if (!transactionStates || transactionStates.length === 0) return
121
+
122
+ const originTx = transactionStates[0]
123
+ const destinationTx = transactionStates[transactionStates.length - 1]
124
+
125
+ // Check if this is a same-chain transaction (all transactions on same chain)
126
+ const isSameChain =
127
+ transactionStates.length <= 2 &&
128
+ transactionStates.every((tx) => tx.chainId === originTx?.chainId)
129
+
130
+ const originRefund = originTx?.decodedTrailsTokenSweeperEvents?.find(
131
+ (e) => e.type === "Refund",
132
+ )
133
+
134
+ // Only check destination sweep for cross-chain transactions
135
+ const destinationSweep = !isSameChain
136
+ ? destinationTx?.decodedTrailsTokenSweeperEvents?.find(
137
+ (e) => e.type === "Sweep",
138
+ )
139
+ : null
140
+
141
+ if (originRefund) {
142
+ setRefundMessage(
143
+ "The origin call failed, but your funds were successfully refunded on the origin chain.",
144
+ )
145
+ setShowRefundInfo(true)
146
+ return
147
+ }
148
+
149
+ if (destinationSweep) {
150
+ setRefundMessage(
151
+ "The destination call failed, but a refund was successfully handled on the destination chain.",
152
+ )
153
+ setShowRefundInfo(true)
154
+ return
155
+ }
156
+
157
+ setShowRefundInfo(false)
158
+ setRefundMessage(null)
159
+ }, [transactionStates])
160
+
161
+ // Extract QuoteDetails section to reuse in both success and failure states
162
+ const quoteDetailsSection = quote && (
163
+ <div className="mt-2">
164
+ <QuoteDetails quote={quote} showContent={true}>
165
+ {transactionStates.length > 0 && (
166
+ <>
167
+ <div className="font-medium text-gray-700 dark:text-gray-300">
168
+ Transactions:
169
+ </div>
170
+ <div className="space-y-2">
171
+ {transactionStates.map((state, i) => (
172
+ <div
173
+ key={`${state.transactionHash}-${i}`}
174
+ className="p-2 rounded bg-gray-100 dark:bg-gray-700/50"
175
+ >
176
+ <div className="flex justify-between items-center">
177
+ <span className="text-gray-600 dark:text-gray-400">
178
+ {state.label}:
179
+ </span>
180
+ <span
181
+ className={`px-2 py-0.5 rounded-full text-xs ${
182
+ state.state === "confirmed"
183
+ ? "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-400"
184
+ : "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-400"
185
+ }`}
186
+ >
187
+ {state.state.charAt(0).toUpperCase() +
188
+ state.state.slice(1)}
189
+ </span>
190
+ </div>
191
+ <div className="mt-1 flex justify-between items-center">
192
+ <span className="text-gray-600 dark:text-gray-400">
193
+ Hash:
194
+ </span>
195
+ <a
196
+ href={state.explorerUrl}
197
+ target="_blank"
198
+ rel="noopener noreferrer"
199
+ className="flex items-center gap-1 hover:underline text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
200
+ >
201
+ {hasMetaTxError(state) && (
202
+ <svg
203
+ className="w-4 h-4 text-orange-500"
204
+ fill="none"
205
+ viewBox="0 0 24 24"
206
+ stroke="currentColor"
207
+ >
208
+ <title>
209
+ An internal meta transaction failure occurred
210
+ </title>
211
+ <path
212
+ strokeLinecap="round"
213
+ strokeLinejoin="round"
214
+ strokeWidth={2}
215
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
216
+ />
217
+ </svg>
218
+ )}
219
+ <ChainImage chainId={state.chainId} size={16} />
220
+ <span>{truncateAddress(state.transactionHash)}</span>
221
+ <svg
222
+ className="w-3 h-3"
223
+ fill="none"
224
+ viewBox="0 0 24 24"
225
+ stroke="currentColor"
226
+ >
227
+ <title>External Link</title>
228
+ <path
229
+ strokeLinecap="round"
230
+ strokeLinejoin="round"
231
+ strokeWidth={2}
232
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
233
+ />
234
+ </svg>
235
+ </a>
236
+ </div>
237
+ </div>
238
+ ))}
239
+ </div>
240
+ </>
241
+ )}
242
+ </QuoteDetails>
243
+ </div>
244
+ )
245
+
97
246
  if (!finalExplorerUrl) {
98
247
  return (
99
248
  <div className="flex flex-col justify-center min-h-full space-y-6 pt-8">
@@ -126,6 +275,21 @@ export const Receipt: React.FC<ReceiptProps> = ({
126
275
  </p>
127
276
  </div>
128
277
 
278
+ {onSendAnother && (
279
+ <div className="flex justify-center">
280
+ <button
281
+ type="button"
282
+ onClick={onSendAnother}
283
+ className="inline-flex items-center gap-1 px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 trails-border-radius-button transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer font-medium"
284
+ >
285
+ Try Again
286
+ </button>
287
+ </div>
288
+ )}
289
+
290
+ {/* Quote Details - Show even in failed state */}
291
+ {quoteDetailsSection}
292
+
129
293
  {!renderInline && (
130
294
  <div className="text-center">
131
295
  <button
@@ -145,15 +309,24 @@ export const Receipt: React.FC<ReceiptProps> = ({
145
309
  <div className="flex flex-col justify-center min-h-full space-y-6 pt-8">
146
310
  <div className="text-center mb-2">
147
311
  <div className={`mx-auto flex items-center justify-center`}>
148
- <GreenCheckAnimation />
312
+ {showRefundInfo || showMetaTxError ? (
313
+ <YellowWarningAnimation />
314
+ ) : (
315
+ <GreenCheckAnimation />
316
+ )}
149
317
  </div>
150
318
 
151
319
  <div
152
320
  className={`transition-all duration-500 ease-out ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
153
321
  >
154
322
  <h2 className="mt-4 text-2xl font-bold text-gray-900 dark:text-white">
155
- Transaction Confirmed
323
+ {showRefundInfo ? "Unable to process." : "Completed"}
156
324
  </h2>
325
+ {showRefundInfo && (
326
+ <div className="mt-1 text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-1 justify-center animate-fade-in-up">
327
+ Refunded.
328
+ </div>
329
+ )}
157
330
  {completionTimeSeconds > 0 && (
158
331
  <div
159
332
  className="mt-1 text-sm text-gray-600 dark:text-gray-400 flex items-center gap-1 justify-center animate-fade-in-up"
@@ -162,28 +335,37 @@ export const Receipt: React.FC<ReceiptProps> = ({
162
335
  animationFillMode: "both",
163
336
  }}
164
337
  >
165
- <span className="inline-block animate-fade-in-up">
166
- Completed in{" "}
167
- <strong>{formatElapsed(completionTimeSeconds)}</strong>
168
- </span>
169
- {totalCompletionSeconds && totalCompletionSeconds < 15 && (
170
- <svg
171
- className="w-4 h-4 ml-1 text-yellow-400 inline"
172
- fill="none"
173
- viewBox="0 0 24 24"
174
- stroke="currentColor"
175
- aria-hidden="true"
176
- >
177
- <title>Lightning Fast</title>
178
- <path
179
- strokeLinecap="round"
180
- strokeLinejoin="round"
181
- strokeWidth={2}
182
- d="M13 10V3L4 14h7v7l9-11h-7z"
183
- fill="currentColor"
184
- />
185
- </svg>
186
- )}
338
+ <div className="inline-block animate-fade-in-up">
339
+ {showRefundInfo ? (
340
+ <span>Transaction confirmed</span>
341
+ ) : (
342
+ <span>
343
+ Completed in{" "}
344
+ <strong>{formatElapsed(completionTimeSeconds)}</strong>
345
+ </span>
346
+ )}
347
+ </div>
348
+ {totalCompletionSeconds &&
349
+ totalCompletionSeconds < 15 &&
350
+ !showMetaTxError &&
351
+ !showRefundInfo && (
352
+ <svg
353
+ className="w-4 h-4 ml-1 text-yellow-400 inline"
354
+ fill="none"
355
+ viewBox="0 0 24 24"
356
+ stroke="currentColor"
357
+ aria-hidden="true"
358
+ >
359
+ <title>Lightning Fast</title>
360
+ <path
361
+ strokeLinecap="round"
362
+ strokeLinejoin="round"
363
+ strokeWidth={2}
364
+ d="M13 10V3L4 14h7v7l9-11h-7z"
365
+ fill="currentColor"
366
+ />
367
+ </svg>
368
+ )}
187
369
  <style>{`
188
370
  @keyframes fadeInUp {
189
371
  0% { opacity: 0; transform: translateY(16px); }
@@ -192,6 +374,33 @@ export const Receipt: React.FC<ReceiptProps> = ({
192
374
  `}</style>
193
375
  </div>
194
376
  )}
377
+
378
+ {(showMetaTxError || showRefundInfo) && (
379
+ <div
380
+ className="mt-2 text-sm text-orange-600 dark:text-orange-400 flex items-center gap-2 justify-center animate-fade-in-up"
381
+ style={{
382
+ animation: "fadeInUp 0.7s cubic-bezier(0.23, 1, 0.32, 1)",
383
+ animationFillMode: "both",
384
+ }}
385
+ >
386
+ <svg
387
+ className="w-4 h-4 text-orange-500"
388
+ fill="none"
389
+ viewBox="0 0 24 24"
390
+ stroke="currentColor"
391
+ aria-hidden="true"
392
+ >
393
+ <title>Internal Meta Transaction Error</title>
394
+ <path
395
+ strokeLinecap="round"
396
+ strokeLinejoin="round"
397
+ strokeWidth={2}
398
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
399
+ />
400
+ </svg>
401
+ <span>Although one or more errors occurred</span>
402
+ </div>
403
+ )}
195
404
  </div>
196
405
  </div>
197
406
 
@@ -204,6 +413,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
204
413
  rel="noopener noreferrer"
205
414
  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"
206
415
  >
416
+ {finalChainId && <ChainImage chainId={finalChainId} size={16} />}
207
417
  View on Explorer
208
418
  <svg
209
419
  className="w-4 h-4 ml-1 text-black dark:text-blue-300"
@@ -222,9 +432,64 @@ export const Receipt: React.FC<ReceiptProps> = ({
222
432
  </a>
223
433
  </div>
224
434
 
435
+ {showRefundInfo && refundMessage && (
436
+ <div
437
+ className="mt-2 text-sm text-yellow-700 dark:text-yellow-400 flex items-center gap-2 justify-center animate-fade-in-up"
438
+ style={{
439
+ animation: "fadeInUp 0.7s cubic-bezier(0.23, 1, 0.32, 1)",
440
+ animationFillMode: "both",
441
+ }}
442
+ >
443
+ <div className="rounded-xl bg-warning">
444
+ <div className="flex bg-background-overlay rounded-xl p-4 w-full flex-col gap-3">
445
+ <div className="flex w-full gap-2 justify-between">
446
+ <div className="flex flex-col gap-1">
447
+ <div className="text-sm font-medium text-gray-900 dark:text-white text-left">
448
+ Refund Information
449
+ </div>
450
+ <div className="text-sm text-gray-700 dark:text-gray-300 text-left">
451
+ {refundMessage}
452
+ </div>
453
+ </div>
454
+ <div className="flex items-center">
455
+ <svg
456
+ className="w-5 h-5 text-yellow-600 dark:text-yellow-400"
457
+ fill="none"
458
+ viewBox="0 0 24 24"
459
+ stroke="currentColor"
460
+ aria-hidden="true"
461
+ >
462
+ <title>Information</title>
463
+ <path
464
+ strokeLinecap="round"
465
+ strokeLinejoin="round"
466
+ strokeWidth={2}
467
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
468
+ />
469
+ </svg>
470
+ </div>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ </div>
475
+ )}
476
+
225
477
  <div
226
478
  className={`space-y-3 transition-all duration-500 ease-out delay-200 ${showContent ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}
227
479
  >
480
+ {onSendAnother && (
481
+ <div className="flex justify-center">
482
+ <button
483
+ type="button"
484
+ onClick={onSendAnother}
485
+ className="inline-flex items-center gap-1 px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 trails-border-radius-button transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer font-medium"
486
+ >
487
+ Send Again
488
+ <ChevronRight className="w-4 h-4" />
489
+ </button>
490
+ </div>
491
+ )}
492
+
228
493
  {!renderInline && (
229
494
  <button
230
495
  type="button"
@@ -236,79 +501,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
236
501
  )}
237
502
 
238
503
  {/* Quote Details */}
239
- {quote && (
240
- <div className="mt-2">
241
- <QuoteDetails quote={quote} showContent={true}>
242
- {transactionStates.length > 0 && (
243
- <>
244
- <div className="font-medium text-gray-700 dark:text-gray-300">
245
- Transactions:
246
- </div>
247
- <div className="space-y-2">
248
- {transactionStates.map((state) => (
249
- <div
250
- key={state.transactionHash}
251
- className="p-2 rounded bg-gray-100 dark:bg-gray-700/50"
252
- >
253
- <div className="flex justify-between items-center">
254
- <span className="text-gray-600 dark:text-gray-400">
255
- {state.label}:
256
- </span>
257
- <span
258
- className={`px-2 py-0.5 rounded-full text-xs ${
259
- state.state === "confirmed"
260
- ? "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-400"
261
- : "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-400"
262
- }`}
263
- >
264
- {state.state}
265
- </span>
266
- </div>
267
- <div className="mt-1 flex justify-between items-center">
268
- <span className="text-gray-600 dark:text-gray-400">
269
- Chain ID:
270
- </span>
271
- <span className="text-gray-900 dark:text-white">
272
- {state.chainId}
273
- </span>
274
- </div>
275
- <div className="mt-1 flex justify-between items-center">
276
- <span className="text-gray-600 dark:text-gray-400">
277
- Hash:
278
- </span>
279
- <a
280
- href={state.explorerUrl}
281
- target="_blank"
282
- rel="noopener noreferrer"
283
- className="flex items-center gap-1 hover:underline text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
284
- >
285
- <span>
286
- {truncateAddress(state.transactionHash)}
287
- </span>
288
- <svg
289
- className="w-3 h-3"
290
- fill="none"
291
- viewBox="0 0 24 24"
292
- stroke="currentColor"
293
- >
294
- <title>External Link</title>
295
- <path
296
- strokeLinecap="round"
297
- strokeLinejoin="round"
298
- strokeWidth={2}
299
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
300
- />
301
- </svg>
302
- </a>
303
- </div>
304
- </div>
305
- ))}
306
- </div>
307
- </>
308
- )}
309
- </QuoteDetails>
310
- </div>
311
- )}
504
+ {quoteDetailsSection}
312
505
  </div>
313
506
  </div>
314
507
  )