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,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,12 +32,17 @@ 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"
39
+ import { useDynamicInputStyles } from "./DynamicInputStyles.js"
34
40
 
35
41
  interface PayProps {
36
42
  selectedToken?: any // Origin token (optional - user can select)
37
43
  onBack?: () => void
38
- account: Account
39
- walletClient: WalletClient
44
+ account?: Account
45
+ walletClient?: WalletClient
40
46
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
41
47
  onError: (error: Error | string | null) => void
42
48
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -157,6 +163,19 @@ export const Pay: React.FC<PayProps> = ({
157
163
  )
158
164
  }, [originToken, filteredTokensFormatted])
159
165
 
166
+ // Calculate effective recipient for useSendForm
167
+ const effectiveRecipient = useMemo(() => {
168
+ const recipient = selectedRecipient || toRecipient || account?.address
169
+ logger.console.log("[trails-sdk] Effective recipient calculated:", {
170
+ selectedRecipient,
171
+ toRecipient,
172
+ accountAddress: account?.address,
173
+ effectiveRecipient: recipient,
174
+ willPassToUseSendForm: recipient,
175
+ })
176
+ return recipient
177
+ }, [selectedRecipient, toRecipient, account?.address])
178
+
160
179
  // Use useSendForm for quote functionality with EXACT_OUTPUT trade type
161
180
  const {
162
181
  amountUsdDisplay,
@@ -182,7 +201,7 @@ export const Pay: React.FC<PayProps> = ({
182
201
  } = useSendForm({
183
202
  account,
184
203
  toAmount: tokenAmountForBackend || toAmount, // Use the input amount as target amount for EXACT_OUTPUT
185
- toRecipient: selectedRecipient || toRecipient || account.address,
204
+ toRecipient: effectiveRecipient,
186
205
  toChainId,
187
206
  toToken,
188
207
  toCalldata,
@@ -283,15 +302,31 @@ export const Pay: React.FC<PayProps> = ({
283
302
  setSelectedDestinationChain,
284
303
  ])
285
304
 
286
- // Initialize selected recipient from toRecipient prop or default to connected wallet
305
+ // Initialize and update selected recipient from toRecipient prop or default to connected wallet
306
+ // biome-ignore lint/correctness/useExhaustiveDependencies: selectedRecipient is intentionally excluded to avoid infinite loops
287
307
  useEffect(() => {
288
- if (toRecipient && !selectedRecipient) {
308
+ logger.console.log("[trails-sdk] toRecipient prop effect:", {
309
+ toRecipient,
310
+ selectedRecipient,
311
+ accountAddress: account?.address,
312
+ })
313
+
314
+ if (toRecipient) {
315
+ // Always sync with toRecipient prop when it changes
316
+ logger.console.log(
317
+ "[trails-sdk] Setting selectedRecipient to toRecipient:",
318
+ toRecipient,
319
+ )
289
320
  setSelectedRecipient(toRecipient)
290
321
  } else if (!selectedRecipient && account?.address) {
291
322
  // Default to connected wallet address if no recipient is set
323
+ logger.console.log(
324
+ "[trails-sdk] Setting selectedRecipient to account address:",
325
+ account.address,
326
+ )
292
327
  setSelectedRecipient(account.address)
293
328
  }
294
- }, [toRecipient, selectedRecipient, setSelectedRecipient, account?.address])
329
+ }, [toRecipient, setSelectedRecipient, account?.address])
295
330
 
296
331
  // Initialize and update amount from toAmount prop
297
332
  useEffect(() => {
@@ -328,10 +363,12 @@ export const Pay: React.FC<PayProps> = ({
328
363
  const destTokenToUse = globalDestinationToken || defaultDestinationToken
329
364
 
330
365
  if (destTokenToUse && !isLoadingDefaults) {
331
- logger.console.log(
332
- "[trails-sdk] Initializing destination token:",
333
- destTokenToUse,
334
- )
366
+ logger.console.log("[trails-sdk] Initializing destination token:", {
367
+ symbol: destTokenToUse.symbol,
368
+ chainId: destTokenToUse.chainId,
369
+ name: destTokenToUse.name,
370
+ contractAddress: destTokenToUse.contractAddress,
371
+ })
335
372
 
336
373
  // Set destination token if not already set by global state
337
374
  if (!globalDestinationToken && defaultDestinationToken) {
@@ -417,28 +454,13 @@ export const Pay: React.FC<PayProps> = ({
417
454
  return inputDisplayValue
418
455
  }, [inputDisplayValue])
419
456
 
420
- // Dynamic font size based on input length
421
- const inputStyles = useMemo(() => {
422
- const inputLength = displayAmount.length
423
- let fontSize: string
424
-
425
- if (inputLength > 12) {
426
- fontSize = "0.875rem"
427
- } else if (inputLength > 9) {
428
- fontSize = "1rem"
429
- } else if (inputLength > 6) {
430
- fontSize = "1.125rem"
431
- } else if (inputLength > 3) {
432
- fontSize = "1.25rem"
433
- } else {
434
- fontSize = "1.5rem"
435
- }
457
+ // Dynamic font size based on input length for destination amount
458
+ const inputStyles = useDynamicInputStyles({ inputValue: displayAmount })
436
459
 
437
- return {
438
- fontSize,
439
- transition: "all 0.1s ease-in-out",
440
- }
441
- }, [displayAmount.length])
460
+ // Dynamic font size based on input length for origin amount (Pay with section)
461
+ const originInputStyles = useDynamicInputStyles({
462
+ inputValue: prepareSendQuote?.originAmountFormatted || "",
463
+ })
442
464
 
443
465
  const handleOriginTokenSelect = useCallback(
444
466
  (token: any) => {
@@ -533,7 +555,6 @@ export const Pay: React.FC<PayProps> = ({
533
555
  onBack={() => setShowOriginTokenSelector(false)}
534
556
  headerContent="Select Token"
535
557
  headerContentAlign="left"
536
- showAccountActions={true}
537
558
  />
538
559
  <TokenSelector
539
560
  onTokenSelect={handleOriginTokenSelect}
@@ -580,7 +601,6 @@ export const Pay: React.FC<PayProps> = ({
580
601
  onBack={() => setShowDestinationTokenSelector(false)}
581
602
  headerContent="Select Token"
582
603
  headerContentAlign="left"
583
- showAccountActions={true}
584
604
  />
585
605
  <TokenSelector
586
606
  onTokenSelect={handleDestinationTokenSelect}
@@ -617,11 +637,35 @@ export const Pay: React.FC<PayProps> = ({
617
637
  )
618
638
  }
619
639
 
640
+ // Check for required props
641
+ const missingRequiredProps = []
642
+ if (!toAmount) missingRequiredProps.push("toAmount")
643
+ if (!toToken) missingRequiredProps.push("toToken")
644
+ if (!toRecipient) missingRequiredProps.push("toAddress")
645
+
620
646
  // Check if this is a payment request (all required props are set)
621
647
  const isPaymentRequest = !!(toToken && toAmount && toChainId && toRecipient)
622
648
 
649
+ // If required props are missing, only show the error
650
+ if (missingRequiredProps.length > 0) {
651
+ return (
652
+ <div className="space-y-4">
653
+ <ScreenHeader
654
+ onBack={onBack}
655
+ headerContent="Pay"
656
+ headerContentAlign="left"
657
+ showAccountActions={true}
658
+ />
659
+ <RequiredPropsError
660
+ missingProps={missingRequiredProps}
661
+ componentName="Pay"
662
+ />
663
+ </div>
664
+ )
665
+ }
666
+
623
667
  return (
624
- <div className="space-y-4">
668
+ <div className="space-y-2">
625
669
  <ScreenHeader
626
670
  onBack={onBack}
627
671
  headerContent="Pay"
@@ -635,18 +679,18 @@ export const Pay: React.FC<PayProps> = ({
635
679
  {/* Payment Request Header */}
636
680
  <div className="space-y-1 trails-bg-secondary trails-border-radius-container p-3">
637
681
  <div className="flex justify-start">
638
- <div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden">
682
+ <div className="flex items-center font-medium trails-text-primary text-sm whitespace-nowrap overflow-hidden min-w-full">
639
683
  {payMessage}
640
684
  </div>
641
685
  </div>
642
686
  </div>
643
687
 
644
688
  {/* Origin Token Selection for Payment Request */}
645
- <div className="space-y-1">
646
- <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">
689
+ <div className="mb-4">
690
+ <div className="pt-4 pb-4 trails-bg-secondary trails-border-radius-container p-3 group transition-all duration-200 border border-transparent min-h-[120px] flex flex-col">
647
691
  {/* Amount to Pay Label */}
648
- <div className="flex justify-between items-center mb-2">
649
- <div className="text-sm font-bold trails-text-secondary text-left">
692
+ <div className="mb-4 flex justify-between items-center">
693
+ <div className="text-sm font-medium trails-text-secondary text-left m-0">
650
694
  Pay with
651
695
  {fundMethod === "qr-code"
652
696
  ? " QR Code"
@@ -654,88 +698,44 @@ export const Pay: React.FC<PayProps> = ({
654
698
  ? " Exchange"
655
699
  : ""}
656
700
  </div>
701
+ <FundingMethodSelectorButton />
657
702
  </div>
658
703
 
659
- <div className="flex items-center space-x-2">
704
+ <div className="flex items-center space-x-2 flex-1">
660
705
  {/* Amount Display - Non-editable */}
661
706
  <div className="flex-1">
662
- <div
663
- className="flex items-center justify-start cursor-text"
664
- onClick={() => paymentRequestInputRef.current?.focus()}
665
- >
666
- <div className="flex items-center">
667
- <input
668
- ref={paymentRequestInputRef}
669
- type="text"
670
- value={prepareSendQuote?.originAmountFormatted || ""}
671
- readOnly={true}
672
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary ${
673
- isLoadingQuote ? "animate-pulse" : ""
674
- }`}
675
- style={{
676
- fontSize: inputStyles.fontSize,
677
- width: prepareSendQuote?.originAmountFormatted
678
- ? `${Math.max(prepareSendQuote.originAmountFormatted.length - 1 + 0.5, 1)}ch`
679
- : "1ch",
680
- minWidth: "1ch",
681
- maxWidth: "200px",
682
- padding: "0",
683
- margin: "0",
684
- transition: "all 0.1s ease-in-out",
685
- }}
686
- />
687
- <span
688
- className="font-bold text-gray-400 dark:text-gray-500"
689
- style={{
690
- fontSize: inputStyles.fontSize,
691
- marginLeft: "0.1em",
692
- padding: "0",
693
- transition: "all 0.2s ease-in-out",
694
- }}
695
- >
696
- {originToken?.symbol || "TOKEN"}
697
- </span>
698
- {isLoadingQuote && (
699
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
700
- )}
701
- </div>
707
+ <div className="flex items-center space-x-2">
708
+ <input
709
+ ref={paymentRequestInputRef}
710
+ type="text"
711
+ value={prepareSendQuote?.originAmountFormatted || ""}
712
+ placeholder={"0"}
713
+ readOnly={true}
714
+ className={`w-full bg-transparent font-bold trails-text-primary border-none outline-none ${
715
+ isLoadingQuote ? "animate-pulse" : ""
716
+ }`}
717
+ style={originInputStyles}
718
+ />
719
+ {isLoadingQuote && (
720
+ <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
721
+ )}
702
722
  </div>
703
723
  </div>
704
724
 
705
- {/* Origin Token Selection Button */}
706
- <button
707
- type="button"
708
- onClick={() => setShowOriginTokenSelector(true)}
709
- 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"
710
- >
711
- {originToken ? (
712
- <>
713
- <TokenImage
714
- symbol={originToken.symbol}
715
- imageUrl={originToken.imageUrl}
716
- chainId={originToken.chainId}
717
- size={20}
718
- />
719
- <span className="font-medium trails-text-primary text-sm">
720
- {originToken.symbol}
721
- </span>
722
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
723
- </>
724
- ) : (
725
- <>
726
- <span className="font-medium trails-text-muted text-sm">
727
- Select Token
728
- </span>
729
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
730
- </>
731
- )}
732
- </button>
725
+ {/* Origin Token Selection */}
726
+ <div className="relative">
727
+ <TokenSelectorButton
728
+ token={originToken}
729
+ chainId={originToken?.chainId}
730
+ onSelect={() => setShowOriginTokenSelector(true)}
731
+ />
732
+ </div>
733
733
  </div>
734
734
 
735
735
  {/* Bottom Info Row */}
736
- <div className="mt-2 flex justify-between items-center">
736
+ <div className="mt-4 flex justify-between items-center">
737
737
  {/* USD Amount */}
738
- <div className="text-xs trails-text-muted">
738
+ <div className="text-xs text-gray-500 dark:text-gray-400">
739
739
  {originToken?.symbol &&
740
740
  prepareSendQuote?.originAmountFormatted ? (
741
741
  <>≈ {prepareSendQuote?.originAmountUsdDisplay || "$0.00"}</>
@@ -743,17 +743,59 @@ export const Pay: React.FC<PayProps> = ({
743
743
  <span>&nbsp;</span>
744
744
  )}
745
745
  </div>
746
- {/* Token Balance */}
747
- <div className="text-xs trails-text-muted">
748
- {originTokenBalance?.balanceFormatted ? (
749
- <>
750
- Balance: {originTokenBalance.balanceFormatted}{" "}
751
- {originToken?.symbol}
752
- </>
753
- ) : (
754
- <span>&nbsp;</span>
755
- )}
756
- </div>
746
+
747
+ {/* Origin Token Balance and Percentage Buttons */}
748
+ {originToken && originTokenBalance?.balanceFormatted && (
749
+ <div className="flex items-center space-x-2">
750
+ <button
751
+ type="button"
752
+ className="text-xs text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 transition-colors bg-transparent border-none p-0"
753
+ onClick={() => {
754
+ if (originTokenBalance.balanceFormatted) {
755
+ const balance = parseFloat(
756
+ originTokenBalance.balanceFormatted,
757
+ )
758
+ if (!Number.isNaN(balance)) {
759
+ setTokenAmountForBackend(balance.toFixed(6))
760
+ }
761
+ }
762
+ }}
763
+ onKeyDown={(e) => {
764
+ if (e.key === "Enter" || e.key === " ") {
765
+ e.preventDefault()
766
+ if (originTokenBalance.balanceFormatted) {
767
+ const balance = parseFloat(
768
+ originTokenBalance.balanceFormatted,
769
+ )
770
+ if (!Number.isNaN(balance)) {
771
+ setTokenAmountForBackend(balance.toFixed(6))
772
+ }
773
+ }
774
+ }
775
+ }}
776
+ title="Click to use full balance"
777
+ >
778
+ Balance: {originTokenBalance.balanceFormatted}
779
+ </button>
780
+
781
+ {/* Percentage Buttons - Only show if toAmount is not set */}
782
+ {!toAmount && (
783
+ <PercentageMaxButtons
784
+ userBalance={originTokenBalance.balanceFormatted}
785
+ isNativeToken={
786
+ originToken.contractAddress === zeroAddress ||
787
+ originToken.contractAddress === undefined
788
+ }
789
+ gasCostFormatted={prepareSendQuote?.gasCostFormatted}
790
+ chainId={originToken.chainId}
791
+ onAmountSelect={(amount) => {
792
+ setTokenAmountForBackend(amount)
793
+ }}
794
+ className="opacity-100"
795
+ />
796
+ )}
797
+ </div>
798
+ )}
757
799
  </div>
758
800
  </div>
759
801
  </div>
@@ -839,12 +881,7 @@ export const Pay: React.FC<PayProps> = ({
839
881
  >
840
882
  <div className="text-left">
841
883
  <div className="font-medium trails-text-primary text-sm">
842
- Pay with
843
- {fundMethod === "qr-code"
844
- ? " QR Code"
845
- : fundMethod === "exchange"
846
- ? " Exchange"
847
- : ""}
884
+ Payment method
848
885
  </div>
849
886
  </div>
850
887
 
@@ -888,128 +925,64 @@ export const Pay: React.FC<PayProps> = ({
888
925
 
889
926
  <div className="space-y-1">
890
927
  {/* Destination Amount Input Section - Like Fund.tsx but for destination token */}
891
- <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">
928
+ <div
929
+ className={`pt-4 pb-4 trails-bg-secondary trails-border-radius-container p-3 group transition-all duration-200 border border-transparent min-h-[120px] flex flex-col ${
930
+ !toAmount
931
+ ? "trails-bg-secondary-hover focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary"
932
+ : ""
933
+ }`}
934
+ >
892
935
  {/* Amount to Pay Label */}
893
- <div className="flex justify-between items-center mb-2">
894
- <div className="text-sm font-medium trails-text-secondary text-left">
936
+ <div className="mb-4 flex justify-between items-center">
937
+ <div className="text-sm font-medium trails-text-secondary text-left m-0">
895
938
  Recipient receives
896
939
  </div>
940
+ <FundingMethodSelectorButton />
897
941
  </div>
898
942
 
899
- <div className="flex items-center space-x-2">
943
+ <div className="flex items-center space-x-2 flex-1">
900
944
  {/* Amount Input */}
901
945
  <div className="flex-1">
902
- <div
903
- className="flex items-center justify-start cursor-text"
904
- onClick={() => inputRef.current?.focus()}
905
- >
906
- <div className="flex items-center">
907
- <input
908
- ref={inputRef}
909
- type="text"
910
- value={displayAmount}
911
- onChange={(e) => handleAmountChange(e.target.value)}
912
- placeholder={`0 ${selectedDestToken?.symbol || ""}`}
913
- readOnly={!!toAmount}
914
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary ${
915
- isLoadingQuote ? "animate-pulse" : ""
916
- }`}
917
- style={{
918
- fontSize: inputStyles.fontSize,
919
- width: `${Math.max((displayAmount || "0").length - 1 + 0.5, 1)}ch`,
920
- minWidth: "1ch",
921
- maxWidth: "270px",
922
- padding: "0",
923
- margin: "0",
924
- transition: "all 0.1s ease-in-out",
925
- }}
926
- inputMode="decimal"
927
- />
928
- <span
929
- className="font-bold text-gray-400 dark:text-gray-500"
930
- style={{
931
- fontSize: inputStyles.fontSize,
932
- marginLeft: "0.1em",
933
- padding: "0",
934
- transition: "all 0.2s ease-in-out",
935
- }}
936
- >
937
- {selectedDestToken?.symbol?.slice(0, 4) || "TOKEN"}
938
- </span>
939
- {isLoadingQuote && (
940
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
941
- )}
942
- </div>
946
+ <div className="flex items-center space-x-2">
947
+ <input
948
+ ref={inputRef}
949
+ type="text"
950
+ value={displayAmount}
951
+ onChange={(e) => handleAmountChange(e.target.value)}
952
+ placeholder={`0 ${selectedDestToken?.symbol || ""}`}
953
+ readOnly={!!toAmount}
954
+ className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
955
+ isLoadingQuote ? "animate-pulse" : ""
956
+ }`}
957
+ style={inputStyles}
958
+ />
959
+ {isLoadingQuote && (
960
+ <div className="animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
961
+ )}
943
962
  </div>
944
963
  </div>
945
964
 
946
- {/* Destination Token Selection Button */}
947
- {toToken ? (
948
- /* Non-clickable display when toToken is provided */
949
- <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">
950
- {selectedDestToken ? (
951
- <>
952
- <TokenImage
953
- symbol={selectedDestToken.symbol}
954
- imageUrl={selectedDestToken.imageUrl}
955
- chainId={globalDestinationToken?.chainId || toChainId}
956
- size={20}
957
- />
958
- <span className="font-medium trails-text-primary text-sm">
959
- {selectedDestToken.symbol}
960
- </span>
961
- </>
962
- ) : (
963
- <span className="font-medium trails-text-muted text-sm">
964
- Select Token
965
- </span>
966
- )}
967
- </div>
968
- ) : (
969
- /* Clickable button when toToken is not provided */
970
- <button
971
- type="button"
972
- onClick={() => setShowDestinationTokenSelector(true)}
973
- 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"
974
- >
975
- {selectedDestToken ? (
976
- <>
977
- <TokenImage
978
- symbol={selectedDestToken.symbol}
979
- imageUrl={selectedDestToken.imageUrl}
980
- chainId={globalDestinationToken?.chainId || toChainId}
981
- size={20}
982
- />
983
- <span className="font-medium trails-text-primary text-sm">
984
- {selectedDestToken.symbol}
985
- </span>
986
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
987
- </>
988
- ) : (
989
- <>
990
- <span className="font-medium trails-text-muted text-sm">
991
- Select Token
992
- </span>
993
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
994
- </>
995
- )}
996
- </button>
997
- )}
965
+ {/* Destination Token Selection */}
966
+ <div className="relative">
967
+ <TokenSelectorButton
968
+ token={selectedDestToken}
969
+ chainId={globalDestinationToken?.chainId || toChainId}
970
+ onSelect={() => setShowDestinationTokenSelector(true)}
971
+ unselectable={!!toToken}
972
+ />
973
+ </div>
998
974
  </div>
999
975
 
1000
976
  {/* Bottom Info Row */}
1001
- <div className="mt-2 flex justify-between items-center">
977
+ <div className="mt-4 flex justify-between items-center">
1002
978
  {/* USD Amount */}
1003
- <div className="text-xs trails-text-muted">
979
+ <div className="text-xs text-gray-500 dark:text-gray-400">
1004
980
  {selectedDestToken?.symbol && displayAmount ? (
1005
981
  <>≈ {amountUsdDisplay || "$0.00"}</>
1006
982
  ) : (
1007
983
  <span>&nbsp;</span>
1008
984
  )}
1009
985
  </div>
1010
- <div className="text-xs trails-text-muted">
1011
- <span>&nbsp;</span>
1012
- </div>
1013
986
  </div>
1014
987
  </div>
1015
988
  </div>
@@ -1063,6 +1036,13 @@ export const Pay: React.FC<PayProps> = ({
1063
1036
  chainId={originToken?.chainId}
1064
1037
  />
1065
1038
 
1039
+ {/* Quote Details */}
1040
+ {prepareSendQuote && (
1041
+ <div className="mb-4">
1042
+ <QuoteDetails quote={prepareSendQuote} showContent={true} />
1043
+ </div>
1044
+ )}
1045
+
1066
1046
  {/* Pay Button */}
1067
1047
  <form onSubmit={handleSubmit}>
1068
1048
  <button
@@ -1085,10 +1065,14 @@ export const Pay: React.FC<PayProps> = ({
1085
1065
  <Loader2 className="w-5 h-5 animate-spin mr-2" />
1086
1066
  <span>{buttonText}</span>
1087
1067
  </div>
1068
+ ) : !account?.address ? (
1069
+ "Connect your wallet"
1088
1070
  ) : prepareSendQuote?.noSufficientBalance ? (
1089
1071
  "Insufficient Balance"
1090
1072
  ) : !selectedRecipient ? (
1091
1073
  "Select recipient address"
1074
+ ) : !originToken ? (
1075
+ "Select payment token"
1092
1076
  ) : !selectedDestToken ? (
1093
1077
  "Select destination token"
1094
1078
  ) : !tokenAmountForBackend ||
@@ -1101,13 +1085,6 @@ export const Pay: React.FC<PayProps> = ({
1101
1085
  )}
1102
1086
  </button>
1103
1087
  </form>
1104
-
1105
- {/* Quote Details */}
1106
- {prepareSendQuote && (
1107
- <div className="space-y-2">
1108
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
1109
- </div>
1110
- )}
1111
1088
  </div>
1112
1089
  )
1113
1090
  }