0xtrails 0.2.2 → 0.2.5

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 (194) hide show
  1. package/dist/aave.d.ts +8 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/{ccip-ConT1gDe.js → ccip-CXlshvBY.js} +1 -1
  4. package/dist/chains.d.ts +5 -1
  5. package/dist/chains.d.ts.map +1 -1
  6. package/dist/config.d.ts +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +5 -4
  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/{index-CMh8uEbV.js → index-_QuyGrjU.js} +86304 -83380
  15. package/dist/index.d.ts +4 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -2
  18. package/dist/intentEntrypoint.d.ts +0 -8
  19. package/dist/intentEntrypoint.d.ts.map +1 -1
  20. package/dist/intents.d.ts +40 -0
  21. package/dist/intents.d.ts.map +1 -1
  22. package/dist/metaTxnMonitor.d.ts +5 -4
  23. package/dist/metaTxnMonitor.d.ts.map +1 -1
  24. package/dist/metaTxns.d.ts +3 -3
  25. package/dist/metaTxns.d.ts.map +1 -1
  26. package/dist/morpho.d.ts +8 -0
  27. package/dist/morpho.d.ts.map +1 -1
  28. package/dist/prepareSend.d.ts +16 -6
  29. package/dist/prepareSend.d.ts.map +1 -1
  30. package/dist/queryParams.d.ts.map +1 -1
  31. package/dist/relayer.d.ts +10 -7
  32. package/dist/relayer.d.ts.map +1 -1
  33. package/dist/sequenceWallet.d.ts +3 -2
  34. package/dist/sequenceWallet.d.ts.map +1 -1
  35. package/dist/tokenBalances.d.ts +7 -0
  36. package/dist/tokenBalances.d.ts.map +1 -1
  37. package/dist/tokens.d.ts +2 -1
  38. package/dist/tokens.d.ts.map +1 -1
  39. package/dist/trails.d.ts +2 -2
  40. package/dist/trails.d.ts.map +1 -1
  41. package/dist/wallets.d.ts.map +1 -1
  42. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  43. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  44. package/dist/widget/components/ClassicSwap.d.ts +2 -0
  45. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  46. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  47. package/dist/widget/components/ConnectedWallets.d.ts +4 -0
  48. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  49. package/dist/widget/components/Earn.d.ts.map +1 -1
  50. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  51. package/dist/widget/components/Fund.d.ts +1 -0
  52. package/dist/widget/components/Fund.d.ts.map +1 -1
  53. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  54. package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +11 -5
  55. package/dist/widget/components/FundSwap.d.ts.map +1 -0
  56. package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
  57. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
  58. package/dist/widget/components/Modal.d.ts.map +1 -1
  59. package/dist/widget/components/Pay.d.ts +1 -0
  60. package/dist/widget/components/Pay.d.ts.map +1 -1
  61. package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
  62. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
  63. package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +11 -34
  64. package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
  65. package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +16 -8
  66. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
  67. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  68. package/dist/widget/components/Receive.d.ts.map +1 -1
  69. package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
  70. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
  71. package/dist/widget/components/Recipients.d.ts.map +1 -1
  72. package/dist/widget/components/RefundWarning.d.ts +1 -0
  73. package/dist/widget/components/RefundWarning.d.ts.map +1 -1
  74. package/dist/widget/components/RequiredPropsError.d.ts +8 -0
  75. package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
  76. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  77. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  78. package/dist/widget/components/Swap.d.ts +1 -0
  79. package/dist/widget/components/Swap.d.ts.map +1 -1
  80. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  81. package/dist/widget/components/TokenImage.d.ts +1 -0
  82. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  83. package/dist/widget/components/TokenList.d.ts.map +1 -1
  84. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  85. package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
  86. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
  87. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  88. package/dist/widget/components/WaasFeeOptions.d.ts +8 -0
  89. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
  90. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  91. package/dist/widget/components/WalletList.d.ts.map +1 -1
  92. package/dist/widget/css/compiled.css +2 -0
  93. package/dist/widget/css/index.css +554 -0
  94. package/dist/widget/hooks/useBack.d.ts +6 -0
  95. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  96. package/dist/widget/hooks/useCheckout.d.ts +1 -1
  97. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  98. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  99. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  100. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
  101. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  102. package/dist/widget/hooks/useInitialRedirect.d.ts +7 -0
  103. package/dist/widget/hooks/useInitialRedirect.d.ts.map +1 -0
  104. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  105. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  106. package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
  107. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
  108. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  109. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  110. package/dist/widget/index.js +1 -1
  111. package/dist/widget/widget.d.ts +4 -4
  112. package/dist/widget/widget.d.ts.map +1 -1
  113. package/package.json +30 -23
  114. package/src/aave.ts +32 -0
  115. package/src/chains.ts +23 -3
  116. package/src/config.ts +12 -4
  117. package/src/constants.ts +11 -16
  118. package/src/error.ts +20 -2
  119. package/src/estimate.ts +416 -5
  120. package/src/index.ts +8 -3
  121. package/src/intentEntrypoint.ts +0 -15
  122. package/src/intents.ts +161 -11
  123. package/src/metaTxnMonitor.ts +28 -22
  124. package/src/metaTxns.ts +3 -3
  125. package/src/morpho.ts +32 -0
  126. package/src/prepareSend.ts +710 -458
  127. package/src/queryParams.ts +2 -1
  128. package/src/relayer.ts +15 -16
  129. package/src/sequenceWallet.ts +7 -3
  130. package/src/tokenBalances.ts +47 -0
  131. package/src/tokens.ts +17 -1
  132. package/src/trails.ts +2 -2
  133. package/src/wallets.ts +8 -0
  134. package/src/widget/compiled.css +2 -2
  135. package/src/widget/components/AccountActionsDropdown.tsx +9 -15
  136. package/src/widget/components/AccountIntentTransactionHistory.tsx +1 -1
  137. package/src/widget/components/AccountSettings.tsx +10 -27
  138. package/src/widget/components/ChainFilterDropdown.tsx +1 -1
  139. package/src/widget/components/ChainList.tsx +1 -1
  140. package/src/widget/components/ClassicSwap.tsx +111 -155
  141. package/src/widget/components/ConnectWallet.tsx +10 -39
  142. package/src/widget/components/ConnectedWallets.tsx +113 -58
  143. package/src/widget/components/Earn.tsx +73 -589
  144. package/src/widget/components/EarnPools.tsx +2 -1
  145. package/src/widget/components/Fund.tsx +81 -109
  146. package/src/widget/components/FundMethods.tsx +82 -159
  147. package/src/widget/components/FundSwap.tsx +52 -0
  148. package/src/widget/components/FundingMethodSelectorButton.tsx +60 -0
  149. package/src/widget/components/Modal.tsx +6 -2
  150. package/src/widget/components/Pay.tsx +198 -200
  151. package/src/widget/components/PercentageMaxButtons.tsx +77 -0
  152. package/src/widget/components/PoolDeposit.tsx +593 -0
  153. package/src/widget/components/PoolWithdraw.tsx +903 -0
  154. package/src/widget/components/QuoteDetails.tsx +22 -8
  155. package/src/widget/components/Receive.tsx +1 -3
  156. package/src/widget/components/RecipientSelectorButton.tsx +42 -0
  157. package/src/widget/components/Recipients.tsx +64 -156
  158. package/src/widget/components/RefundWarning.tsx +5 -1
  159. package/src/widget/components/RequiredPropsError.tsx +33 -0
  160. package/src/widget/components/ScreenHeader.tsx +5 -1
  161. package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
  162. package/src/widget/components/Swap.tsx +2 -43
  163. package/src/widget/components/SwapSettings.tsx +3 -15
  164. package/src/widget/components/TokenImage.tsx +21 -4
  165. package/src/widget/components/TokenList.tsx +0 -1
  166. package/src/widget/components/TokenSelector.tsx +2 -1
  167. package/src/widget/components/TokenSelectorButton.tsx +75 -0
  168. package/src/widget/components/UserPreferences.tsx +6 -24
  169. package/src/widget/components/WaasFeeOptions.tsx +331 -0
  170. package/src/widget/components/WalletConfirmation.tsx +55 -3
  171. package/src/widget/components/WalletList.tsx +7 -5
  172. package/src/widget/hooks/useBack.tsx +113 -9
  173. package/src/widget/hooks/useCheckout.ts +36 -20
  174. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  175. package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
  176. package/src/widget/hooks/useInitialRedirect.tsx +70 -0
  177. package/src/widget/hooks/usePayMessage.tsx +86 -11
  178. package/src/widget/hooks/useSelectedFeeToken.tsx +10 -16
  179. package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
  180. package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
  181. package/src/widget/hooks/useSendForm.ts +34 -12
  182. package/src/widget/hooks/useTokenList.ts +1 -1
  183. package/src/widget/index.css +27 -0
  184. package/src/widget/widget.tsx +245 -208
  185. package/dist/widget/components/FundSendForm.d.ts.map +0 -1
  186. package/dist/widget/components/PaySendForm.d.ts.map +0 -1
  187. package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
  188. package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
  189. package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
  190. package/src/widget/components/FundSendForm.tsx +0 -903
  191. package/src/widget/components/PaySendForm.tsx +0 -869
  192. package/src/widget/components/SimpleSwap.tsx +0 -983
  193. package/src/widget/hooks/useSwapSettings.tsx +0 -100
  194. /package/dist/{style.css → 0xtrails.css} +0 -0
@@ -0,0 +1,60 @@
1
+ import { ChevronRight, QrCode, Send } from "lucide-react"
2
+ import type React from "react"
3
+ import { truncateAddress } from "../../utils.js"
4
+ import { useAccount } from "wagmi"
5
+ import { Identicon } from "./Identicon.js"
6
+ import { useBack } from "../hooks/useBack.js"
7
+ import { useSelectedFundMethod } from "../hooks/useSelectedFundMethod.js"
8
+
9
+ export const FundingMethodSelectorButton: React.FC = () => {
10
+ const { address, isConnected } = useAccount()
11
+ const { setCurrentScreenWithBack } = useBack()
12
+ const { selectedFundMethod } = useSelectedFundMethod()
13
+
14
+ const handleClick = () => {
15
+ if (isConnected && address) {
16
+ // If wallet is connected, go to fund methods
17
+ setCurrentScreenWithBack("fund-methods")
18
+ } else {
19
+ // If no wallet connected, go to connect wallet
20
+ setCurrentScreenWithBack("connect")
21
+ }
22
+ }
23
+
24
+ return (
25
+ <button
26
+ type="button"
27
+ onClick={handleClick}
28
+ className="flex items-center space-x-2 text-blue-500 hover:text-blue-600 transition-colors cursor-pointer bg-transparent border-none p-0"
29
+ title={
30
+ isConnected && address ? "Select funding method" : "Connect wallet"
31
+ }
32
+ >
33
+ {isConnected && address ? (
34
+ selectedFundMethod === "qr-code" ? (
35
+ <>
36
+ <QrCode className="w-4 h-4" />
37
+ <span className="text-sm font-medium">QR Code</span>
38
+ </>
39
+ ) : selectedFundMethod === "exchange" ? (
40
+ <>
41
+ <Send className="w-4 h-4" />
42
+ <span className="text-sm font-medium">Exchange</span>
43
+ </>
44
+ ) : (
45
+ <>
46
+ <Identicon value={address} size={16} />
47
+ <span className="text-sm font-medium">
48
+ {truncateAddress(address, 4, 2)}
49
+ </span>
50
+ </>
51
+ )
52
+ ) : (
53
+ <span className="text-sm font-medium">Connect Wallet</span>
54
+ )}
55
+ <ChevronRight className="w-4 h-4" />
56
+ </button>
57
+ )
58
+ }
59
+
60
+ export default FundingMethodSelectorButton
@@ -73,9 +73,13 @@ const Modal: React.FC<ModalProps> = ({ isOpen, onClose, children }) => {
73
73
  <button
74
74
  type="button"
75
75
  onClick={onClose}
76
- className="absolute right-2 top-2 p-1 rounded-full transition-colors cursor-pointer z-10 hover:bg-gray-100 text-gray-600 dark:hover:bg-gray-800 dark:text-gray-400 z-20"
76
+ className="absolute right-4 top-6 flex h-8 w-8 justify-center items-center rounded-full bg-gray-50 dark:bg-gray-700 cursor-pointer transition-colors text-gray-900 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600 z-20"
77
+ title="Close"
77
78
  >
78
- <X className="h-4 w-4" />
79
+ <X
80
+ className="h-4 w-4"
81
+ style={{ transform: "translate(0, -2px)" }}
82
+ />
79
83
  </button>
80
84
  {children}
81
85
  </motion.div>
@@ -1,7 +1,8 @@
1
- import { ChevronRight, Search, Loader2, ChevronDown } from "lucide-react"
1
+ import { Search, Loader2, ChevronRight } from "lucide-react"
2
2
  import { useEffect, useState, useMemo, useRef, useCallback } from "react"
3
3
  import type React from "react"
4
4
  import type { Account, WalletClient } from "viem"
5
+ import { zeroAddress } from "viem"
5
6
  import type { TransactionState } from "../../transactions.js"
6
7
  import type { OnCompleteProps } from "../hooks/useSendForm.js"
7
8
  import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
@@ -31,6 +32,10 @@ import type { PrepareSendQuote } from "../../prepareSend.js"
31
32
  import type { SupportedToken } from "../../tokens.js"
32
33
  import { logger } from "../../logger.js"
33
34
  import { RefundWarning } from "./RefundWarning.js"
35
+ import { TokenSelectorButton } from "./TokenSelectorButton.js"
36
+ import { RequiredPropsError } from "./RequiredPropsError.js"
37
+ import { FundingMethodSelectorButton } from "./FundingMethodSelectorButton.js"
38
+ import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
34
39
 
35
40
  interface PayProps {
36
41
  selectedToken?: any // Origin token (optional - user can select)
@@ -45,6 +50,7 @@ interface PayProps {
45
50
  onSend: (amount: string, recipient: string) => void
46
51
  paymasterUrls?: Array<{ chainId: number; url: string }>
47
52
  gasless?: boolean
53
+ isSequenceWallet?: boolean
48
54
  setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
49
55
  quoteProvider?: string
50
56
  fundMethod?: string
@@ -82,6 +88,7 @@ export const Pay: React.FC<PayProps> = ({
82
88
  onSend,
83
89
  paymasterUrls,
84
90
  gasless,
91
+ isSequenceWallet = false,
85
92
  setWalletConfirmRetryHandler,
86
93
  quoteProvider,
87
94
  fundMethod,
@@ -143,6 +150,18 @@ export const Pay: React.FC<PayProps> = ({
143
150
  allSupportedTokens: true, // Show all tokens for destination selection
144
151
  })
145
152
 
153
+ // Get origin token balance for display
154
+ const originTokenBalance = useMemo(() => {
155
+ if (!originToken || !filteredTokensFormatted) return null
156
+
157
+ return filteredTokensFormatted.find(
158
+ (token) =>
159
+ token.contractAddress?.toLowerCase() ===
160
+ originToken.contractAddress?.toLowerCase() &&
161
+ token.chainId === originToken.chainId,
162
+ )
163
+ }, [originToken, filteredTokensFormatted])
164
+
146
165
  // Use useSendForm for quote functionality with EXACT_OUTPUT trade type
147
166
  const {
148
167
  amountUsdDisplay,
@@ -314,10 +333,12 @@ export const Pay: React.FC<PayProps> = ({
314
333
  const destTokenToUse = globalDestinationToken || defaultDestinationToken
315
334
 
316
335
  if (destTokenToUse && !isLoadingDefaults) {
317
- logger.console.log(
318
- "[trails-sdk] Initializing destination token:",
319
- destTokenToUse,
320
- )
336
+ logger.console.log("[trails-sdk] Initializing destination token:", {
337
+ symbol: destTokenToUse.symbol,
338
+ chainId: destTokenToUse.chainId,
339
+ name: destTokenToUse.name,
340
+ contractAddress: destTokenToUse.contractAddress,
341
+ })
321
342
 
322
343
  // Set destination token if not already set by global state
323
344
  if (!globalDestinationToken && defaultDestinationToken) {
@@ -403,21 +424,21 @@ export const Pay: React.FC<PayProps> = ({
403
424
  return inputDisplayValue
404
425
  }, [inputDisplayValue])
405
426
 
406
- // Dynamic font size based on input length
427
+ // Dynamic font size based on input length for destination amount
407
428
  const inputStyles = useMemo(() => {
408
429
  const inputLength = displayAmount.length
409
430
  let fontSize: string
410
431
 
411
432
  if (inputLength > 12) {
412
- fontSize = "0.875rem"
413
- } else if (inputLength > 9) {
414
433
  fontSize = "1rem"
434
+ } else if (inputLength > 9) {
435
+ fontSize = "1.25rem"
415
436
  } else if (inputLength > 6) {
416
- fontSize = "1.125rem"
437
+ fontSize = "1.5rem"
417
438
  } else if (inputLength > 3) {
418
- fontSize = "1.25rem"
439
+ fontSize = "1.75rem"
419
440
  } else {
420
- fontSize = "1.5rem"
441
+ fontSize = "2rem"
421
442
  }
422
443
 
423
444
  return {
@@ -426,6 +447,29 @@ export const Pay: React.FC<PayProps> = ({
426
447
  }
427
448
  }, [displayAmount.length])
428
449
 
450
+ // Dynamic font size based on input length for origin amount (Pay with section)
451
+ const originInputStyles = useMemo(() => {
452
+ const inputLength = (prepareSendQuote?.originAmountFormatted || "").length
453
+ let fontSize: string
454
+
455
+ if (inputLength > 12) {
456
+ fontSize = "1rem"
457
+ } else if (inputLength > 9) {
458
+ fontSize = "1.25rem"
459
+ } else if (inputLength > 6) {
460
+ fontSize = "1.5rem"
461
+ } else if (inputLength > 3) {
462
+ fontSize = "1.75rem"
463
+ } else {
464
+ fontSize = "2rem"
465
+ }
466
+
467
+ return {
468
+ fontSize,
469
+ transition: "all 0.1s ease-in-out",
470
+ }
471
+ }, [prepareSendQuote?.originAmountFormatted])
472
+
429
473
  const handleOriginTokenSelect = useCallback(
430
474
  (token: any) => {
431
475
  const formattedToken = {
@@ -519,7 +563,6 @@ export const Pay: React.FC<PayProps> = ({
519
563
  onBack={() => setShowOriginTokenSelector(false)}
520
564
  headerContent="Select Token"
521
565
  headerContentAlign="left"
522
- showAccountActions={true}
523
566
  />
524
567
  <TokenSelector
525
568
  onTokenSelect={handleOriginTokenSelect}
@@ -566,7 +609,6 @@ export const Pay: React.FC<PayProps> = ({
566
609
  onBack={() => setShowDestinationTokenSelector(false)}
567
610
  headerContent="Select Token"
568
611
  headerContentAlign="left"
569
- showAccountActions={true}
570
612
  />
571
613
  <TokenSelector
572
614
  onTokenSelect={handleDestinationTokenSelect}
@@ -603,11 +645,35 @@ export const Pay: React.FC<PayProps> = ({
603
645
  )
604
646
  }
605
647
 
648
+ // Check for required props
649
+ const missingRequiredProps = []
650
+ if (!toAmount) missingRequiredProps.push("toAmount")
651
+ if (!toToken) missingRequiredProps.push("toToken")
652
+ if (!toRecipient) missingRequiredProps.push("toRecipient")
653
+
606
654
  // Check if this is a payment request (all required props are set)
607
655
  const isPaymentRequest = !!(toToken && toAmount && toChainId && toRecipient)
608
656
 
657
+ // If required props are missing, only show the error
658
+ if (missingRequiredProps.length > 0) {
659
+ return (
660
+ <div className="space-y-4">
661
+ <ScreenHeader
662
+ onBack={onBack}
663
+ headerContent="Pay"
664
+ headerContentAlign="left"
665
+ showAccountActions={true}
666
+ />
667
+ <RequiredPropsError
668
+ missingProps={missingRequiredProps}
669
+ componentName="Pay"
670
+ />
671
+ </div>
672
+ )
673
+ }
674
+
609
675
  return (
610
- <div className="space-y-4">
676
+ <div className="space-y-2">
611
677
  <ScreenHeader
612
678
  onBack={onBack}
613
679
  headerContent="Pay"
@@ -621,7 +687,7 @@ export const Pay: React.FC<PayProps> = ({
621
687
  {/* Payment Request Header */}
622
688
  <div className="space-y-1 trails-bg-secondary trails-border-radius-container p-3">
623
689
  <div className="flex justify-start">
624
- <div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden">
690
+ <div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden min-w-full">
625
691
  {payMessage}
626
692
  </div>
627
693
  </div>
@@ -629,10 +695,10 @@ export const Pay: React.FC<PayProps> = ({
629
695
 
630
696
  {/* Origin Token Selection for Payment Request */}
631
697
  <div className="space-y-1">
632
- <div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary">
698
+ <div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary min-h-[120px] flex flex-col">
633
699
  {/* Amount to Pay Label */}
634
700
  <div className="flex justify-between items-center mb-2">
635
- <div className="text-sm font-bold trails-text-secondary text-left">
701
+ <div className="text-sm font-medium trails-text-secondary text-left">
636
702
  Pay with
637
703
  {fundMethod === "qr-code"
638
704
  ? " QR Code"
@@ -640,82 +706,38 @@ export const Pay: React.FC<PayProps> = ({
640
706
  ? " Exchange"
641
707
  : ""}
642
708
  </div>
709
+ <FundingMethodSelectorButton />
643
710
  </div>
644
711
 
645
- <div className="flex items-center space-x-2">
712
+ <div className="flex items-center space-x-2 flex-1">
646
713
  {/* Amount Display - Non-editable */}
647
714
  <div className="flex-1">
648
- <div
649
- className="flex items-center justify-start cursor-text"
650
- onClick={() => paymentRequestInputRef.current?.focus()}
651
- >
652
- <div className="flex items-center">
653
- <input
654
- ref={paymentRequestInputRef}
655
- type="text"
656
- value={prepareSendQuote?.originAmountFormatted || ""}
657
- readOnly={true}
658
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary ${
659
- isLoadingQuote ? "animate-pulse" : ""
660
- }`}
661
- style={{
662
- fontSize: inputStyles.fontSize,
663
- width: prepareSendQuote?.originAmountFormatted
664
- ? `${Math.max(prepareSendQuote.originAmountFormatted.length - 1 + 0.5, 1)}ch`
665
- : "1ch",
666
- minWidth: "1ch",
667
- maxWidth: "200px",
668
- padding: "0",
669
- margin: "0",
670
- transition: "all 0.1s ease-in-out",
671
- }}
672
- />
673
- <span
674
- className="font-bold text-gray-400 dark:text-gray-500"
675
- style={{
676
- fontSize: inputStyles.fontSize,
677
- marginLeft: "0.1em",
678
- padding: "0",
679
- transition: "all 0.2s ease-in-out",
680
- }}
681
- >
682
- {originToken?.symbol || "TOKEN"}
683
- </span>
684
- {isLoadingQuote && (
685
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
686
- )}
687
- </div>
715
+ <div className="flex items-center space-x-2">
716
+ <input
717
+ ref={paymentRequestInputRef}
718
+ type="text"
719
+ value={prepareSendQuote?.originAmountFormatted || ""}
720
+ placeholder={`0 ${originToken?.symbol || ""}`.trim()}
721
+ readOnly={true}
722
+ className={`w-full bg-transparent font-bold trails-text-primary border-none outline-none ${
723
+ isLoadingQuote ? "animate-pulse" : ""
724
+ }`}
725
+ style={originInputStyles}
726
+ />
727
+ {isLoadingQuote && (
728
+ <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
729
+ )}
688
730
  </div>
689
731
  </div>
690
732
 
691
- {/* Origin Token Selection Button */}
692
- <button
693
- type="button"
694
- onClick={() => setShowOriginTokenSelector(true)}
695
- className="flex items-center space-x-2 trails-bg-card hover:trails-hover-bg trails-border-radius-input px-2.5 py-1.5 border trails-border-primary transition-colors cursor-pointer"
696
- >
697
- {originToken ? (
698
- <>
699
- <TokenImage
700
- symbol={originToken.symbol}
701
- imageUrl={originToken.imageUrl}
702
- chainId={originToken.chainId}
703
- size={20}
704
- />
705
- <span className="font-medium trails-text-primary text-sm">
706
- {originToken.symbol}
707
- </span>
708
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
709
- </>
710
- ) : (
711
- <>
712
- <span className="font-medium trails-text-muted text-sm">
713
- Select Token
714
- </span>
715
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
716
- </>
717
- )}
718
- </button>
733
+ {/* Origin Token Selection */}
734
+ <div className="relative">
735
+ <TokenSelectorButton
736
+ token={originToken}
737
+ chainId={originToken?.chainId}
738
+ onSelect={() => setShowOriginTokenSelector(true)}
739
+ />
740
+ </div>
719
741
  </div>
720
742
 
721
743
  {/* Bottom Info Row */}
@@ -729,9 +751,59 @@ export const Pay: React.FC<PayProps> = ({
729
751
  <span>&nbsp;</span>
730
752
  )}
731
753
  </div>
732
- <div className="text-xs trails-text-muted">
733
- <span>&nbsp;</span>
734
- </div>
754
+
755
+ {/* Origin Token Balance and Percentage Buttons */}
756
+ {originToken && originTokenBalance?.balanceFormatted && (
757
+ <div className="flex items-center space-x-2">
758
+ <button
759
+ type="button"
760
+ className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
761
+ onClick={() => {
762
+ if (originTokenBalance.balanceFormatted) {
763
+ const balance = parseFloat(
764
+ originTokenBalance.balanceFormatted,
765
+ )
766
+ if (!Number.isNaN(balance)) {
767
+ setTokenAmountForBackend(balance.toFixed(6))
768
+ }
769
+ }
770
+ }}
771
+ onKeyDown={(e) => {
772
+ if (e.key === "Enter" || e.key === " ") {
773
+ e.preventDefault()
774
+ if (originTokenBalance.balanceFormatted) {
775
+ const balance = parseFloat(
776
+ originTokenBalance.balanceFormatted,
777
+ )
778
+ if (!Number.isNaN(balance)) {
779
+ setTokenAmountForBackend(balance.toFixed(6))
780
+ }
781
+ }
782
+ }
783
+ }}
784
+ title="Click to use full balance"
785
+ >
786
+ Balance: {originTokenBalance.balanceFormatted}
787
+ </button>
788
+
789
+ {/* Percentage Buttons - Only show if toAmount is not set */}
790
+ {!toAmount && (
791
+ <PercentageMaxButtons
792
+ userBalance={originTokenBalance.balanceFormatted}
793
+ isNativeToken={
794
+ originToken.contractAddress === zeroAddress ||
795
+ originToken.contractAddress === undefined
796
+ }
797
+ gasCostFormatted={prepareSendQuote?.gasCostFormatted}
798
+ chainId={originToken.chainId}
799
+ onAmountSelect={(amount) => {
800
+ setTokenAmountForBackend(amount)
801
+ }}
802
+ className="opacity-100"
803
+ />
804
+ )}
805
+ </div>
806
+ )}
735
807
  </div>
736
808
  </div>
737
809
  </div>
@@ -817,12 +889,7 @@ export const Pay: React.FC<PayProps> = ({
817
889
  >
818
890
  <div className="text-left">
819
891
  <div className="font-medium trails-text-primary text-sm">
820
- Pay with
821
- {fundMethod === "qr-code"
822
- ? " QR Code"
823
- : fundMethod === "exchange"
824
- ? " Exchange"
825
- : ""}
892
+ Payment method
826
893
  </div>
827
894
  </div>
828
895
 
@@ -866,113 +933,46 @@ export const Pay: React.FC<PayProps> = ({
866
933
 
867
934
  <div className="space-y-1">
868
935
  {/* Destination Amount Input Section - Like Fund.tsx but for destination token */}
869
- <div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary">
936
+ <div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary min-h-[120px] flex flex-col">
870
937
  {/* Amount to Pay Label */}
871
938
  <div className="flex justify-between items-center mb-2">
872
939
  <div className="text-sm font-medium trails-text-secondary text-left">
873
940
  Recipient receives
874
941
  </div>
942
+ <FundingMethodSelectorButton />
875
943
  </div>
876
944
 
877
- <div className="flex items-center space-x-2">
945
+ <div className="flex items-center space-x-2 flex-1">
878
946
  {/* Amount Input */}
879
947
  <div className="flex-1">
880
- <div
881
- className="flex items-center justify-start cursor-text"
882
- onClick={() => inputRef.current?.focus()}
883
- >
884
- <div className="flex items-center">
885
- <input
886
- ref={inputRef}
887
- type="text"
888
- value={displayAmount}
889
- onChange={(e) => handleAmountChange(e.target.value)}
890
- placeholder={`0 ${selectedDestToken?.symbol || ""}`}
891
- readOnly={!!toAmount}
892
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary ${
893
- isLoadingQuote ? "animate-pulse" : ""
894
- }`}
895
- style={{
896
- fontSize: inputStyles.fontSize,
897
- width: `${Math.max((displayAmount || "0").length - 1 + 0.5, 1)}ch`,
898
- minWidth: "1ch",
899
- maxWidth: "270px",
900
- padding: "0",
901
- margin: "0",
902
- transition: "all 0.1s ease-in-out",
903
- }}
904
- inputMode="decimal"
905
- />
906
- <span
907
- className="font-bold text-gray-400 dark:text-gray-500"
908
- style={{
909
- fontSize: inputStyles.fontSize,
910
- marginLeft: "0.1em",
911
- padding: "0",
912
- transition: "all 0.2s ease-in-out",
913
- }}
914
- >
915
- {selectedDestToken?.symbol?.slice(0, 4) || "TOKEN"}
916
- </span>
917
- {isLoadingQuote && (
918
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
919
- )}
920
- </div>
948
+ <div className="flex items-center space-x-2">
949
+ <input
950
+ ref={inputRef}
951
+ type="text"
952
+ value={displayAmount}
953
+ onChange={(e) => handleAmountChange(e.target.value)}
954
+ placeholder={`0 ${selectedDestToken?.symbol || ""}`}
955
+ readOnly={!!toAmount}
956
+ className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
957
+ isLoadingQuote ? "animate-pulse" : ""
958
+ }`}
959
+ style={inputStyles}
960
+ />
961
+ {isLoadingQuote && (
962
+ <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
963
+ )}
921
964
  </div>
922
965
  </div>
923
966
 
924
- {/* Destination Token Selection Button */}
925
- {toToken ? (
926
- /* Non-clickable display when toToken is provided */
927
- <div className="flex items-center space-x-2 trails-bg-card trails-border-radius-input px-2.5 py-1.5 border trails-border-primary">
928
- {selectedDestToken ? (
929
- <>
930
- <TokenImage
931
- symbol={selectedDestToken.symbol}
932
- imageUrl={selectedDestToken.imageUrl}
933
- chainId={globalDestinationToken?.chainId || toChainId}
934
- size={20}
935
- />
936
- <span className="font-medium trails-text-primary text-sm">
937
- {selectedDestToken.symbol}
938
- </span>
939
- </>
940
- ) : (
941
- <span className="font-medium trails-text-muted text-sm">
942
- Select Token
943
- </span>
944
- )}
945
- </div>
946
- ) : (
947
- /* Clickable button when toToken is not provided */
948
- <button
949
- type="button"
950
- onClick={() => setShowDestinationTokenSelector(true)}
951
- className="flex items-center space-x-2 trails-bg-card hover:trails-hover-bg trails-border-radius-input px-2.5 py-1.5 border trails-border-primary transition-colors cursor-pointer"
952
- >
953
- {selectedDestToken ? (
954
- <>
955
- <TokenImage
956
- symbol={selectedDestToken.symbol}
957
- imageUrl={selectedDestToken.imageUrl}
958
- chainId={globalDestinationToken?.chainId || toChainId}
959
- size={20}
960
- />
961
- <span className="font-medium trails-text-primary text-sm">
962
- {selectedDestToken.symbol}
963
- </span>
964
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
965
- </>
966
- ) : (
967
- <>
968
- <span className="font-medium trails-text-muted text-sm">
969
- Select Token
970
- </span>
971
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
972
- </>
973
- )}
974
- </button>
975
- )}
967
+ {/* Destination Token Selection */}
968
+ <div className="relative">
969
+ <TokenSelectorButton
970
+ token={selectedDestToken}
971
+ chainId={globalDestinationToken?.chainId || toChainId}
972
+ onSelect={() => setShowDestinationTokenSelector(true)}
973
+ unselectable={!!toToken}
974
+ />
975
+ </div>
976
976
  </div>
977
977
 
978
978
  {/* Bottom Info Row */}
@@ -985,9 +985,6 @@ export const Pay: React.FC<PayProps> = ({
985
985
  <span>&nbsp;</span>
986
986
  )}
987
987
  </div>
988
- <div className="text-xs trails-text-muted">
989
- <span>&nbsp;</span>
990
- </div>
991
988
  </div>
992
989
  </div>
993
990
  </div>
@@ -999,6 +996,7 @@ export const Pay: React.FC<PayProps> = ({
999
996
  fundMethod={fundMethod}
1000
997
  isSenderContractOnOrigin={isSenderContractOnOrigin}
1001
998
  isSenderContractOnDestination={isSenderContractOnDestination}
999
+ isSequenceWallet={isSequenceWallet}
1002
1000
  />
1003
1001
 
1004
1002
  {/* Error Display */}
@@ -1040,6 +1038,13 @@ export const Pay: React.FC<PayProps> = ({
1040
1038
  chainId={originToken?.chainId}
1041
1039
  />
1042
1040
 
1041
+ {/* Quote Details */}
1042
+ {prepareSendQuote && (
1043
+ <div className="space-y-2">
1044
+ <QuoteDetails quote={prepareSendQuote} showContent={true} />
1045
+ </div>
1046
+ )}
1047
+
1043
1048
  {/* Pay Button */}
1044
1049
  <form onSubmit={handleSubmit}>
1045
1050
  <button
@@ -1078,13 +1083,6 @@ export const Pay: React.FC<PayProps> = ({
1078
1083
  )}
1079
1084
  </button>
1080
1085
  </form>
1081
-
1082
- {/* Quote Details */}
1083
- {prepareSendQuote && (
1084
- <div className="space-y-2">
1085
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
1086
- </div>
1087
- )}
1088
1086
  </div>
1089
1087
  )
1090
1088
  }