0xtrails 0.2.4 → 0.2.6

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 (212) hide show
  1. package/dist/aave.d.ts +8 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/abortController.d.ts +8 -0
  4. package/dist/abortController.d.ts.map +1 -0
  5. package/dist/{ccip-BlV1Mry3.js → ccip-Xjh9d1gb.js} +7 -7
  6. package/dist/config.d.ts +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/error.d.ts +1 -0
  11. package/dist/error.d.ts.map +1 -1
  12. package/dist/estimate.d.ts +52 -0
  13. package/dist/estimate.d.ts.map +1 -1
  14. package/dist/fees.d.ts +19 -0
  15. package/dist/fees.d.ts.map +1 -0
  16. package/dist/{index-BNWCIGfQ.js → index-BnhdZ8Ho.js} +76406 -75798
  17. package/dist/index.js +726 -520
  18. package/dist/intents.d.ts +40 -0
  19. package/dist/intents.d.ts.map +1 -1
  20. package/dist/metaTxnMonitor.d.ts +3 -3
  21. package/dist/metaTxnMonitor.d.ts.map +1 -1
  22. package/dist/metaTxns.d.ts +3 -3
  23. package/dist/metaTxns.d.ts.map +1 -1
  24. package/dist/morpho.d.ts +8 -0
  25. package/dist/morpho.d.ts.map +1 -1
  26. package/dist/prepareSend.d.ts +19 -75
  27. package/dist/prepareSend.d.ts.map +1 -1
  28. package/dist/queryParams.d.ts.map +1 -1
  29. package/dist/relayer.d.ts +6 -6
  30. package/dist/relayer.d.ts.map +1 -1
  31. package/dist/sequenceWallet.d.ts +2 -2
  32. package/dist/sequenceWallet.d.ts.map +1 -1
  33. package/dist/tokens.d.ts.map +1 -1
  34. package/dist/transactions.d.ts +4 -2
  35. package/dist/transactions.d.ts.map +1 -1
  36. package/dist/wallets.d.ts.map +1 -1
  37. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  38. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  39. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  40. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  41. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  42. package/dist/widget/components/ClassicSwap.d.ts +4 -2
  43. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  44. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  45. package/dist/widget/components/ConnectedWallets.d.ts +4 -0
  46. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  47. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  48. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  49. package/dist/widget/components/Earn.d.ts +2 -2
  50. package/dist/widget/components/Earn.d.ts.map +1 -1
  51. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  52. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  53. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  54. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  55. package/dist/widget/components/Fund.d.ts +2 -2
  56. package/dist/widget/components/Fund.d.ts.map +1 -1
  57. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  58. package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +13 -7
  59. package/dist/widget/components/FundSwap.d.ts.map +1 -0
  60. package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
  61. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
  62. package/dist/widget/components/Identicon.d.ts.map +1 -1
  63. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  64. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  65. package/dist/widget/components/Modal.d.ts.map +1 -1
  66. package/dist/widget/components/Pay.d.ts +2 -2
  67. package/dist/widget/components/Pay.d.ts.map +1 -1
  68. package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
  69. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
  70. package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +14 -36
  71. package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
  72. package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +19 -10
  73. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
  74. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  75. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  76. package/dist/widget/components/Receipt.d.ts.map +1 -1
  77. package/dist/widget/components/Receive.d.ts.map +1 -1
  78. package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
  79. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
  80. package/dist/widget/components/Recipients.d.ts.map +1 -1
  81. package/dist/widget/components/RequiredPropsError.d.ts +8 -0
  82. package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
  83. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  84. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  85. package/dist/widget/components/Swap.d.ts +3 -2
  86. package/dist/widget/components/Swap.d.ts.map +1 -1
  87. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  88. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  89. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  90. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  91. package/dist/widget/components/TokenImage.d.ts +1 -0
  92. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  93. package/dist/widget/components/TokenList.d.ts.map +1 -1
  94. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  95. package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
  96. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
  97. package/dist/widget/components/Tooltip.d.ts +9 -0
  98. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  99. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  100. package/dist/widget/components/WaasFeeOptions.d.ts +9 -0
  101. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
  102. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  103. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  104. package/dist/widget/components/WalletList.d.ts.map +1 -1
  105. package/dist/widget/css/compiled.css +2 -0
  106. package/dist/widget/css/index.css +554 -0
  107. package/dist/widget/hooks/useBack.d.ts +1 -0
  108. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  109. package/dist/widget/hooks/useCheckout.d.ts +1 -1
  110. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  111. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  112. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  113. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
  114. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  115. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  116. package/dist/widget/hooks/useQuote.d.ts +83 -0
  117. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  118. package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
  119. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
  120. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  121. package/dist/widget/hooks/useSendForm.d.ts +2 -2
  122. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  123. package/dist/widget/index.js +2 -2
  124. package/dist/widget/widget.d.ts +9 -4
  125. package/dist/widget/widget.d.ts.map +1 -1
  126. package/package.json +18 -12
  127. package/src/aave.ts +32 -0
  128. package/src/abortController.ts +35 -0
  129. package/src/config.ts +12 -4
  130. package/src/constants.ts +5 -0
  131. package/src/error.ts +19 -1
  132. package/src/estimate.ts +416 -5
  133. package/src/fees.ts +199 -0
  134. package/src/intents.ts +161 -11
  135. package/src/metaTxnMonitor.ts +3 -3
  136. package/src/metaTxns.ts +3 -5
  137. package/src/morpho.ts +32 -0
  138. package/src/prepareSend.ts +714 -550
  139. package/src/queryParams.ts +2 -1
  140. package/src/relayer.ts +11 -11
  141. package/src/sequenceWallet.ts +2 -2
  142. package/src/tokens.ts +7 -1
  143. package/src/trails.ts +3 -3
  144. package/src/transactions.ts +62 -18
  145. package/src/wallets.ts +8 -0
  146. package/src/widget/compiled.css +2 -2
  147. package/src/widget/components/AccountActionsDropdown.tsx +3 -13
  148. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  149. package/src/widget/components/AccountSettings.tsx +48 -54
  150. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  151. package/src/widget/components/ClassicSwap.tsx +131 -213
  152. package/src/widget/components/ConnectWallet.tsx +8 -38
  153. package/src/widget/components/ConnectedWallets.tsx +132 -77
  154. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  155. package/src/widget/components/Earn.tsx +82 -593
  156. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  157. package/src/widget/components/FeeBreakdown.tsx +155 -0
  158. package/src/widget/components/Fund.tsx +41 -108
  159. package/src/widget/components/FundMethods.tsx +82 -159
  160. package/src/widget/components/FundSwap.tsx +52 -0
  161. package/src/widget/components/FundingMethodSelectorButton.tsx +70 -0
  162. package/src/widget/components/Identicon.tsx +164 -95
  163. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  164. package/src/widget/components/Modal.tsx +0 -8
  165. package/src/widget/components/Pay.tsx +214 -237
  166. package/src/widget/components/PercentageMaxButtons.tsx +77 -0
  167. package/src/widget/components/PoolDeposit.tsx +569 -0
  168. package/src/widget/components/PoolWithdraw.tsx +884 -0
  169. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  170. package/src/widget/components/QuoteDetails.tsx +43 -12
  171. package/src/widget/components/Receipt.tsx +16 -2
  172. package/src/widget/components/Receive.tsx +0 -2
  173. package/src/widget/components/RecipientSelectorButton.tsx +44 -0
  174. package/src/widget/components/Recipients.tsx +63 -157
  175. package/src/widget/components/RequiredPropsError.tsx +33 -0
  176. package/src/widget/components/ScreenHeader.tsx +62 -34
  177. package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
  178. package/src/widget/components/Swap.tsx +4 -45
  179. package/src/widget/components/SwapSettings.tsx +2 -14
  180. package/src/widget/components/ThemeProvider.tsx +2 -1
  181. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  182. package/src/widget/components/TokenImage.tsx +22 -5
  183. package/src/widget/components/TokenList.tsx +0 -1
  184. package/src/widget/components/TokenSelector.tsx +63 -53
  185. package/src/widget/components/TokenSelectorButton.tsx +98 -0
  186. package/src/widget/components/Tooltip.tsx +51 -0
  187. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  188. package/src/widget/components/UserPreferences.tsx +6 -24
  189. package/src/widget/components/WaasFeeOptions.tsx +450 -0
  190. package/src/widget/components/WalletConfirmation.tsx +76 -14
  191. package/src/widget/components/WalletConnect.tsx +93 -29
  192. package/src/widget/components/WalletList.tsx +4 -2
  193. package/src/widget/hooks/useBack.tsx +2 -0
  194. package/src/widget/hooks/useCheckout.ts +36 -20
  195. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  196. package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
  197. package/src/widget/hooks/usePayMessage.tsx +86 -11
  198. package/src/widget/hooks/useQuote.ts +413 -0
  199. package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
  200. package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
  201. package/src/widget/hooks/useSendForm.ts +32 -6
  202. package/src/widget/index.css +27 -0
  203. package/src/widget/widget.tsx +326 -283
  204. package/dist/widget/components/FundSendForm.d.ts.map +0 -1
  205. package/dist/widget/components/PaySendForm.d.ts.map +0 -1
  206. package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
  207. package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
  208. package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
  209. package/src/widget/components/FundSendForm.tsx +0 -903
  210. package/src/widget/components/PaySendForm.tsx +0 -869
  211. package/src/widget/components/SimpleSwap.tsx +0 -983
  212. package/src/widget/hooks/useSwapSettings.tsx +0 -100
@@ -1,6 +1,6 @@
1
1
  import type React from "react"
2
2
  import type { PrepareSendQuote } from "../../prepareSend.js"
3
- import { Tooltip } from "@0xsequence/design-system"
3
+ import { Tooltip } from "./Tooltip.js"
4
4
 
5
5
  interface PriceImpactWarningProps {
6
6
  quote?: PrepareSendQuote | null
@@ -1,5 +1,6 @@
1
1
  import { TokenImage } from "./TokenImage.js"
2
- import { InfoIcon, Tooltip } from "@0xsequence/design-system"
2
+ import { InfoIcon } from "@0xsequence/design-system"
3
+ import { Tooltip } from "./Tooltip.js"
3
4
  import type React from "react"
4
5
  import { getExplorerUrlForAddress } from "../../explorer.js"
5
6
  import type { PrepareSendQuote } from "../../prepareSend.js"
@@ -7,6 +8,7 @@ import { useState, useEffect, useRef } from "react"
7
8
  import { truncateAddress } from "../../utils.js"
8
9
  import { PriceImpactWarning } from "./PriceImpactWarning.js"
9
10
  import { usePriceImpactWarning } from "../hooks/usePriceImpactWarning.js"
11
+ import { FeeBreakdown } from "./FeeBreakdown.js"
10
12
 
11
13
  interface QuoteDetailsProps {
12
14
  quote?: PrepareSendQuote | null
@@ -14,6 +16,7 @@ interface QuoteDetailsProps {
14
16
  children?: React.ReactNode
15
17
  onExpand?: (isExpanded: boolean) => void
16
18
  swapMode?: boolean
19
+ compact?: boolean
17
20
  }
18
21
 
19
22
  // Helper function to format completion time
@@ -44,6 +47,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
44
47
  children,
45
48
  onExpand,
46
49
  swapMode,
50
+ compact = false,
47
51
  }) => {
48
52
  const [showCalldata, setShowCalldata] = useState(false)
49
53
  const [showOriginRate, setShowOriginRate] = useState(true)
@@ -83,12 +87,12 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
83
87
 
84
88
  {/* More Details Button - only visible when collapsed */}
85
89
  {!isExpanded && (
86
- <div className={`flex justify-center ${swapMode ? "" : "mb-4"}`}>
90
+ <div className={`flex justify-center`}>
87
91
  {swapMode ? (
88
92
  <button
89
93
  type="button"
90
94
  onClick={() => setIsExpanded(true)}
91
- className="w-full flex items-center justify-between py-2 px-4 trails-border-radius-button transition-colors cursor-pointer text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-600"
95
+ className="w-full flex items-center justify-between py-2 px-4 trails-border-radius-button transition-colors cursor-pointer text-xs"
92
96
  aria-label="Show more details"
93
97
  >
94
98
  <div className="flex items-center gap-2 text-gray-600 dark:text-gray-400 whitespace-nowrap max-w-48 truncate">
@@ -161,10 +165,10 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
161
165
  <button
162
166
  type="button"
163
167
  onClick={() => setIsExpanded(true)}
164
- className="w-full flex items-center justify-center gap-2 py-1 px-4 trails-border-radius-button transition-colors cursor-pointer text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
165
- aria-label="Show more details"
168
+ className={`w-full max-w-md flex items-center ${compact ? "justify-center" : "justify-between"} gap-2 py-1 px-1 trails-border-radius-button transition-colors cursor-pointer text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300`}
169
+ aria-label="Show transaction details"
166
170
  >
167
- <span>More Details</span>
171
+ <span>Transaction details</span>
168
172
  <svg
169
173
  className="w-3 h-3 transition-transform duration-300 ease-out"
170
174
  fill="none"
@@ -191,9 +195,11 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
191
195
  isExpanded ? "max-h-[250px] opacity-100" : "max-h-0 opacity-0"
192
196
  }`}
193
197
  >
194
- <div className="p-4 rounded-lg text-sm space-y-4 trails-bg-secondary">
198
+ <div
199
+ className={`text-sm ${swapMode ? "p-2 space-y-2" : "p-4 rounded-lg space-y-4 trails-bg-secondary"}`}
200
+ >
195
201
  {/* Close Button - only visible when expanded, at top center */}
196
- <div className="flex justify-center mb-4 -mt-2">
202
+ <div className={`flex justify-center mb-4 -mt-2`}>
197
203
  {swapMode ? (
198
204
  <button
199
205
  type="button"
@@ -367,6 +373,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
367
373
  imageUrl={quote.originToken.imageUrl}
368
374
  symbol={quote.originToken.symbol}
369
375
  chainId={quote.originChain.id}
376
+ contractAddress={quote.originToken.contractAddress}
370
377
  size={16}
371
378
  />
372
379
  {quote.originAmountDisplay} {quote.originToken.symbol}
@@ -393,6 +400,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
393
400
  imageUrl={quote.originToken.imageUrl}
394
401
  symbol={quote.originToken.symbol}
395
402
  chainId={quote.originChain.id}
403
+ contractAddress={
404
+ quote.originToken.contractAddress
405
+ }
396
406
  size={16}
397
407
  />
398
408
  {quote.originAmountDisplay}{" "}
@@ -430,6 +440,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
430
440
  imageUrl={quote.destinationToken.imageUrl}
431
441
  symbol={quote.destinationToken.symbol}
432
442
  chainId={quote.destinationChain.id}
443
+ contractAddress={
444
+ quote.destinationToken.contractAddress
445
+ }
433
446
  size={16}
434
447
  />
435
448
  {quote.destinationAmountDisplay}{" "}
@@ -457,6 +470,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
457
470
  imageUrl={quote.destinationToken.imageUrl}
458
471
  symbol={quote.destinationToken.symbol}
459
472
  chainId={quote.destinationChain.id}
473
+ contractAddress={
474
+ quote.destinationToken.contractAddress
475
+ }
460
476
  size={16}
461
477
  />
462
478
  {quote.destinationAmountDisplay}{" "}
@@ -651,7 +667,22 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
651
667
  )
652
668
  })()}
653
669
 
654
- {quote?.fees?.totalFeeAmountUsd != null && (
670
+ {/* Total Fees with optional breakdown */}
671
+ {quote?.trailsFeeBreakdown ? (
672
+ <FeeBreakdown feeBreakdown={quote.trailsFeeBreakdown}>
673
+ <div className="flex justify-between items-center w-full">
674
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1 w-full">
675
+ Total Fees:
676
+ <Tooltip message="The total fees charged for this transaction, including gas fees, bridge fees, and any platform fees. These fees are deducted from your transaction.">
677
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
678
+ </Tooltip>
679
+ </span>
680
+ <span className="font-medium text-xs text-gray-900 dark:text-white">
681
+ {quote.fees.totalFeeAmountUsdDisplay}
682
+ </span>
683
+ </div>
684
+ </FeeBreakdown>
685
+ ) : quote?.fees?.totalFeeAmountUsd != null ? (
655
686
  <div className="flex justify-between items-center">
656
687
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
657
688
  Total Fees:
@@ -663,9 +694,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
663
694
  {quote.fees.totalFeeAmountUsdDisplay}
664
695
  </span>
665
696
  </div>
666
- )}
697
+ ) : null}
667
698
 
668
- {quote?.quoteProvider && (
699
+ {quote?.quoteProvider?.name && (
669
700
  <div className="flex justify-between items-center">
670
701
  <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
671
702
  {quote.originChain.id === quote.destinationChain.id
@@ -766,7 +797,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
766
797
  )}
767
798
 
768
799
  {/* Children content */}
769
- {children && <div className="space-y-2">{children}</div>}
800
+ {children && <div className="mb-0">{children}</div>}
770
801
  </div>
771
802
  </div>
772
803
  </div>
@@ -13,6 +13,7 @@ import { truncateAddress } from "../../utils.js"
13
13
  import { formatElapsed } from "../../utils.js"
14
14
  import { ChainImage } from "./ChainImage.js"
15
15
  import { getChainInfo } from "../../chains.js"
16
+ import { useMode } from "../hooks/useMode.js"
16
17
 
17
18
  interface ReceiptProps {
18
19
  onSendAnother: () => void
@@ -86,6 +87,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
86
87
  quote,
87
88
  showCloseButton,
88
89
  }) => {
90
+ const { mode } = useMode()
89
91
  const [showContent, setShowContent] = useState(false)
90
92
  const [showRefundInfo, setShowRefundInfo] = useState(false)
91
93
  const [refundMessage, setRefundMessage] = useState<string | ReactNode | null>(
@@ -111,6 +113,18 @@ export const Receipt: React.FC<ReceiptProps> = ({
111
113
  return transactionStates.some((tx) => hasMetaTxError(tx))
112
114
  }, [transactionStates, hasMetaTxError])
113
115
 
116
+ const buttonText = useMemo(() => {
117
+ if (mode === "pay") {
118
+ return "Pay Again"
119
+ } else if (mode === "fund") {
120
+ return "Fund Again"
121
+ } else if (mode === "swap") {
122
+ return "Swap Again"
123
+ } else {
124
+ return "Send Again"
125
+ }
126
+ }, [mode])
127
+
114
128
  const {
115
129
  finalExplorerUrl,
116
130
  finalChainId,
@@ -154,7 +168,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
154
168
  // Extract QuoteDetails section to reuse in both success and failure states
155
169
  const quoteDetailsSection = quote && (
156
170
  <div className="mt-2">
157
- <QuoteDetails quote={quote} showContent={true}>
171
+ <QuoteDetails quote={quote} showContent={true} compact={true}>
158
172
  {transactionStates.length > 0 && (
159
173
  <>
160
174
  <div className="font-medium text-gray-700 dark:text-gray-300">
@@ -477,7 +491,7 @@ export const Receipt: React.FC<ReceiptProps> = ({
477
491
  onClick={onSendAnother}
478
492
  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"
479
493
  >
480
- Send Again
494
+ {buttonText}
481
495
  <ChevronRight className="w-4 h-4" />
482
496
  </button>
483
497
  </div>
@@ -60,7 +60,6 @@ export const Receive: React.FC<ReceiveProps> = ({
60
60
  onBack={onBack}
61
61
  headerContent="Receive"
62
62
  headerContentAlign="left"
63
- showAccountActions={true}
64
63
  />
65
64
 
66
65
  <div className="flex flex-col justify-center min-h-full space-y-6 pt-8">
@@ -97,7 +96,6 @@ export const Receive: React.FC<ReceiveProps> = ({
97
96
  onBack={onBack}
98
97
  headerContent={`Pay ${ensName ? ensName : truncateAddress(resolvedAddress)}`}
99
98
  headerContentAlign="left"
100
- showAccountActions={true}
101
99
  />
102
100
 
103
101
  <div className="flex flex-col justify-center min-h-full space-y-6 pt-2">
@@ -0,0 +1,44 @@
1
+ import { ChevronRight } from "lucide-react"
2
+ import type React from "react"
3
+ import { truncateAddress } from "../../utils.js"
4
+ import { Identicon } from "./Identicon.js"
5
+ import { useBack } from "../hooks/useBack.js"
6
+ import { useSelectedRecipient } from "../hooks/useSelectedRecipient.js"
7
+
8
+ export const RecipientSelectorButton: React.FC = () => {
9
+ const { setCurrentScreenWithBack } = useBack()
10
+ const { selectedRecipient } = useSelectedRecipient()
11
+
12
+ const handleClick = () => {
13
+ setCurrentScreenWithBack("recipients")
14
+ }
15
+
16
+ return (
17
+ <button
18
+ type="button"
19
+ onClick={handleClick}
20
+ className="flex items-center space-x-2 text-blue-500 hover:text-blue-600 transition-colors cursor-pointer bg-transparent border-none p-0"
21
+ title={
22
+ selectedRecipient
23
+ ? `Selected: ${selectedRecipient}`
24
+ : "Select recipient"
25
+ }
26
+ >
27
+ {selectedRecipient ? (
28
+ <>
29
+ <div className="flex items-center mr-1">
30
+ <Identicon value={selectedRecipient} size={16} />
31
+ </div>
32
+ <span className="text-sm font-medium m-0">
33
+ {truncateAddress(selectedRecipient, 4, 4)}
34
+ </span>
35
+ </>
36
+ ) : (
37
+ <span className="text-sm font-medium m-0">Select Recipient</span>
38
+ )}
39
+ <ChevronRight className="w-4 h-4 m-0" />
40
+ </button>
41
+ )
42
+ }
43
+
44
+ export default RecipientSelectorButton
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useState, useCallback } from "react"
2
2
  import type React from "react"
3
3
  import { isAddress } from "viem"
4
- import { Copy, RotateCcw } from "lucide-react"
4
+ import { Copy, RotateCcw, ChevronRight } from "lucide-react"
5
5
  import { useAccount, useConnections } from "wagmi"
6
6
  import { ScreenHeader } from "./ScreenHeader.js"
7
7
  import { SearchInputField } from "./SearchInputField.js"
@@ -12,8 +12,8 @@ import { useRecipients, type RecentRecipient } from "../hooks/useRecipients.js"
12
12
  import { useResolveEnsAddress, useResolveEnsName } from "../../ens.js"
13
13
  import { truncateAddress } from "../../utils.js"
14
14
  import { logger } from "../../logger.js"
15
- import { useMode } from "../hooks/useMode.js"
16
15
  import { useWallets, wagmiConnectorToWalletId } from "../../wallets.js"
16
+ import { ConnectedWallets } from "./ConnectedWallets.js"
17
17
 
18
18
  interface RecipientsProps {
19
19
  onBack?: () => void
@@ -26,7 +26,6 @@ export const Recipients: React.FC<RecipientsProps> = ({
26
26
  selectedRecipient = "",
27
27
  onRecipientSelect,
28
28
  }) => {
29
- const { mode } = useMode()
30
29
  const { setCurrentScreen } = useCurrentScreen()
31
30
  const { setSelectedRecipient } = useSelectedRecipient()
32
31
  const { recentRecipients, addRecentRecipient, clearRecentRecipients } =
@@ -136,9 +135,9 @@ export const Recipients: React.FC<RecipientsProps> = ({
136
135
  logger.console.log("[trails-sdk] Selected recent recipient:", recipient)
137
136
  setSelectedRecipient(recipient.address)
138
137
  onRecipientSelect?.(recipient.address)
139
- setCurrentScreen(mode === "pay" ? "send-form" : "fund-form")
138
+ setCurrentScreen("home")
140
139
  },
141
- [setSelectedRecipient, onRecipientSelect, setCurrentScreen, mode],
140
+ [setSelectedRecipient, onRecipientSelect, setCurrentScreen],
142
141
  )
143
142
 
144
143
  // Handle connected wallet selection
@@ -150,9 +149,9 @@ export const Recipients: React.FC<RecipientsProps> = ({
150
149
  )
151
150
  setSelectedRecipient(walletAddress)
152
151
  onRecipientSelect?.(walletAddress)
153
- setCurrentScreen(mode === "pay" ? "send-form" : "fund-form")
152
+ setCurrentScreen("home")
154
153
  },
155
- [setSelectedRecipient, onRecipientSelect, setCurrentScreen, mode],
154
+ [setSelectedRecipient, onRecipientSelect, setCurrentScreen],
156
155
  )
157
156
 
158
157
  // Handle copy to clipboard with success indication
@@ -202,7 +201,6 @@ export const Recipients: React.FC<RecipientsProps> = ({
202
201
  onBack={onBack}
203
202
  headerContent="Recipient address"
204
203
  headerContentAlign="left"
205
- showAccountActions={true}
206
204
  />
207
205
 
208
206
  <div className="space-y-4">
@@ -223,14 +221,23 @@ export const Recipients: React.FC<RecipientsProps> = ({
223
221
  )}
224
222
  </div>
225
223
 
224
+ {/* Connected Wallets - only show when no input */}
225
+ {connectedWallets.length > 0 && !recipientInput.trim() && (
226
+ <ConnectedWallets
227
+ showConnectNewWallet={true}
228
+ onWalletSelect={handleConnectedWalletSelect}
229
+ backScreen="recipients"
230
+ />
231
+ )}
232
+
226
233
  {/* Recent Recipients */}
227
234
  {recentRecipients.length > 0 && (
228
235
  <div className="space-y-2">
229
- <div className="text-sm font-medium text-gray-700 dark:text-gray-300 text-left">
230
- Recents
236
+ <div className="text-sm font-medium text-gray-900 dark:text-gray-100 text-left">
237
+ Recent wallets
231
238
  </div>
232
- <div className="space-y-1 max-h-48 overflow-y-auto trails-scrollbar">
233
- {recentRecipients.map((recipient) => {
239
+ <div className="trails-border-radius-container border trails-border-primary">
240
+ {recentRecipients.map((recipient, index) => {
234
241
  // Only highlight if search field contains a valid address that matches this recipient
235
242
  const searchQuery = recipientInput.trim()
236
243
  const isHighlighted =
@@ -240,53 +247,51 @@ export const Recipients: React.FC<RecipientsProps> = ({
240
247
  searchQuery.trim().toLowerCase()
241
248
 
242
249
  return (
243
- <button
244
- key={recipient.address}
245
- type="button"
246
- className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-border-radius-list-button cursor-pointer group ${
247
- isHighlighted
248
- ? "trails-list-item-selected border border-blue-200 dark:border-blue-800"
249
- : "trails-list-item"
250
- }`}
251
- onClick={() => handleRecentRecipientSelect(recipient)}
252
- onKeyDown={(e) => {
253
- if (e.key === "Enter" || e.key === " ") {
254
- e.preventDefault()
255
- handleRecentRecipientSelect(recipient)
256
- }
257
- }}
258
- aria-label={`Select recipient ${recipient.ensName || recipient.address}`}
259
- >
260
- {/* Identicon */}
261
- <div className="relative flex-shrink-0 mr-2">
262
- <Identicon
263
- value={recipient.address}
264
- size={32}
265
- className="flex-shrink-0"
266
- />
267
- </div>
268
-
269
- <div className="flex-1 min-w-0 text-left">
270
- <div className="flex items-start">
271
- <div className="flex-1">
272
- <div className="flex items-center">
273
- <span className="text-sm font-medium text-gray-900 dark:text-white">
250
+ <div key={recipient.address}>
251
+ <button
252
+ type="button"
253
+ className={`w-full text-left px-3 py-4 text-sm flex items-center justify-between cursor-pointer transition-colors ${
254
+ isHighlighted
255
+ ? "trails-list-item-selected border border-blue-200 dark:border-blue-800"
256
+ : "trails-text-primary trails-hover-bg"
257
+ }`}
258
+ onClick={() => handleRecentRecipientSelect(recipient)}
259
+ onKeyDown={(e) => {
260
+ if (e.key === "Enter" || e.key === " ") {
261
+ e.preventDefault()
262
+ handleRecentRecipientSelect(recipient)
263
+ }
264
+ }}
265
+ aria-label={`Select recipient ${recipient.ensName || recipient.address}`}
266
+ >
267
+ <div className="flex items-center gap-3">
268
+ <Identicon
269
+ value={recipient.address}
270
+ size={32}
271
+ className="flex-shrink-0"
272
+ />
273
+ <div className="text-left">
274
+ <div className="flex items-center space-x-1">
275
+ <span className="text-sm font-bold">
274
276
  {truncateAddress(recipient.address, 8, 4)}
275
277
  </span>
276
- <button
277
- type="button"
278
- onClick={(e) =>
278
+ <div
279
+ onClick={(e) => {
280
+ e.preventDefault()
281
+ e.stopPropagation()
279
282
  handleCopyAddress(recipient.address, e)
280
- }
281
- className={`ml-1 p-0.5 rounded opacity-0 group-hover:opacity-100 transition-all duration-200 cursor-pointer ${
283
+ }}
284
+ onMouseDown={(e) => e.stopPropagation()}
285
+ onMouseUp={(e) => e.stopPropagation()}
286
+ className={`p-0.5 rounded transition-all duration-200 cursor-pointer z-10 relative ${
282
287
  copiedAddress === recipient.address
283
- ? "bg-green-100 dark:bg-green-900/30 opacity-100"
288
+ ? "bg-green-100 dark:bg-green-900/30"
284
289
  : "hover:bg-gray-200 dark:hover:bg-gray-600"
285
290
  }`}
286
291
  title={
287
292
  copiedAddress === recipient.address
288
293
  ? "Copied!"
289
- : "Copy address"
294
+ : "Copy full address"
290
295
  }
291
296
  >
292
297
  {copiedAddress === recipient.address ? (
@@ -308,7 +313,7 @@ export const Recipients: React.FC<RecipientsProps> = ({
308
313
  ) : (
309
314
  <Copy className="w-3 h-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" />
310
315
  )}
311
- </button>
316
+ </div>
312
317
  </div>
313
318
  {recipient.ensName && (
314
319
  <div className="text-xs text-gray-500 dark:text-gray-400">
@@ -317,112 +322,13 @@ export const Recipients: React.FC<RecipientsProps> = ({
317
322
  )}
318
323
  </div>
319
324
  </div>
320
- </div>
321
- </button>
322
- )
323
- })}
324
- </div>
325
- </div>
326
- )}
327
-
328
- {/* Connected Wallets - only show when no input */}
329
- {connectedWallets.length > 0 && !recipientInput.trim() && (
330
- <div className="space-y-2">
331
- <div className="flex items-center gap-2">
332
- <div className="text-sm font-medium text-gray-700 dark:text-gray-300 text-left">
333
- Connected Wallets
334
- </div>
335
- </div>
336
- <div className="space-y-1 max-h-48 overflow-y-auto trails-scrollbar">
337
- {connectedWallets.map((wallet) => {
338
- // Only highlight if search field contains a valid address that matches this wallet
339
- const searchQuery = recipientInput.trim()
340
- const isHighlighted =
341
- searchQuery &&
342
- isAddress(searchQuery) &&
343
- wallet.address.toLowerCase() ===
344
- searchQuery.trim().toLowerCase()
345
-
346
- return (
347
- <button
348
- key={wallet.address}
349
- type="button"
350
- className={`w-full py-2 px-4 flex items-center space-x-3 transition-all duration-200 trails-border-radius-list-button cursor-pointer group ${
351
- isHighlighted
352
- ? "trails-list-item-selected border border-blue-200 dark:border-blue-800"
353
- : "trails-list-item"
354
- }`}
355
- onClick={() => handleConnectedWalletSelect(wallet.address)}
356
- onKeyDown={(e) => {
357
- if (e.key === "Enter" || e.key === " ") {
358
- e.preventDefault()
359
- handleConnectedWalletSelect(wallet.address)
360
- }
361
- }}
362
- aria-label={`Select wallet ${wallet.walletConfig?.name || "Wallet"} - ${wallet.address}`}
363
- >
364
- {/* Identicon */}
365
- <div className="relative flex-shrink-0 mr-2">
366
- <Identicon
367
- value={wallet.address}
368
- size={32}
369
- className="flex-shrink-0"
370
- />
371
- </div>
372
-
373
- <div className="flex-1 min-w-0 text-left">
374
- <div className="flex items-start">
375
- <div className="flex-1">
376
- <div className="flex items-center">
377
- <span className="text-sm font-medium text-gray-900 dark:text-white">
378
- {truncateAddress(wallet.address, 8, 4)}
379
- </span>
380
- <button
381
- type="button"
382
- onClick={(e) =>
383
- handleCopyAddress(wallet.address, e)
384
- }
385
- className={`ml-1 p-0.5 rounded opacity-0 group-hover:opacity-100 transition-all duration-200 cursor-pointer ${
386
- copiedAddress === wallet.address
387
- ? "bg-green-100 dark:bg-green-900/30 opacity-100"
388
- : "hover:bg-gray-200 dark:hover:bg-gray-600"
389
- }`}
390
- title={
391
- copiedAddress === wallet.address
392
- ? "Copied!"
393
- : "Copy address"
394
- }
395
- >
396
- {copiedAddress === wallet.address ? (
397
- <svg
398
- className="w-3 h-3 text-green-600 dark:text-green-400"
399
- fill="none"
400
- viewBox="0 0 24 24"
401
- stroke="currentColor"
402
- aria-label="Copied"
403
- >
404
- <title>Copied</title>
405
- <path
406
- strokeLinecap="round"
407
- strokeLinejoin="round"
408
- strokeWidth={2}
409
- d="M5 13l4 4L19 7"
410
- />
411
- </svg>
412
- ) : (
413
- <Copy className="w-3 h-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300" />
414
- )}
415
- </button>
416
- </div>
417
- <div className="text-xs text-gray-500 dark:text-gray-400">
418
- {wallet.walletConfig?.name ||
419
- wallet.connector?.name ||
420
- "Wallet"}
421
- </div>
422
- </div>
423
- </div>
424
- </div>
425
- </button>
325
+ <ChevronRight className="w-5 h-5 text-gray-400" />
326
+ </button>
327
+ {/* Add divider between items */}
328
+ {index < recentRecipients.length - 1 && (
329
+ <div className="border-b border-gray-200 dark:border-gray-700"></div>
330
+ )}
331
+ </div>
426
332
  )
427
333
  })}
428
334
  </div>
@@ -0,0 +1,33 @@
1
+ import type React from "react"
2
+ import { ErrorDisplay } from "./ErrorDisplay.js"
3
+
4
+ interface RequiredPropsErrorProps {
5
+ missingProps: string[]
6
+ componentName: string
7
+ }
8
+
9
+ export const RequiredPropsError: React.FC<RequiredPropsErrorProps> = ({
10
+ missingProps,
11
+ componentName,
12
+ }) => {
13
+ if (missingProps.length === 0) {
14
+ return null
15
+ }
16
+
17
+ const isMultiple = missingProps.length > 1
18
+
19
+ const prettifiedMessage = `Missing required ${componentName} prop${isMultiple ? "s" : ""}`
20
+
21
+ const detailedMessage = `The following required props are missing for the ${componentName} component:
22
+ ${missingProps.map((prop) => `- ${prop}`).join("\n")}
23
+
24
+ Please ensure all required props are provided when rendering the ${componentName} component.`
25
+
26
+ return (
27
+ <ErrorDisplay
28
+ errorPrettified={prettifiedMessage}
29
+ error={detailedMessage}
30
+ severity="error"
31
+ />
32
+ )
33
+ }