0xtrails 0.2.5 → 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 (116) hide show
  1. package/dist/abortController.d.ts +8 -0
  2. package/dist/abortController.d.ts.map +1 -0
  3. package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/fees.d.ts +19 -0
  7. package/dist/fees.d.ts.map +1 -0
  8. package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
  9. package/dist/index.js +726 -520
  10. package/dist/prepareSend.d.ts +11 -77
  11. package/dist/prepareSend.d.ts.map +1 -1
  12. package/dist/transactions.d.ts +4 -2
  13. package/dist/transactions.d.ts.map +1 -1
  14. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  15. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  16. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  17. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  18. package/dist/widget/components/ClassicSwap.d.ts +2 -2
  19. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  20. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  21. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  22. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  23. package/dist/widget/components/Earn.d.ts +2 -2
  24. package/dist/widget/components/Earn.d.ts.map +1 -1
  25. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  26. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  27. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  28. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  29. package/dist/widget/components/Fund.d.ts +2 -2
  30. package/dist/widget/components/Fund.d.ts.map +1 -1
  31. package/dist/widget/components/FundSwap.d.ts +2 -2
  32. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  33. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  34. package/dist/widget/components/Identicon.d.ts.map +1 -1
  35. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  36. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  37. package/dist/widget/components/Modal.d.ts.map +1 -1
  38. package/dist/widget/components/Pay.d.ts +2 -2
  39. package/dist/widget/components/Pay.d.ts.map +1 -1
  40. package/dist/widget/components/PoolDeposit.d.ts +3 -2
  41. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  42. package/dist/widget/components/PoolWithdraw.d.ts +3 -2
  43. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  44. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  45. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  46. package/dist/widget/components/Receipt.d.ts.map +1 -1
  47. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  48. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  49. package/dist/widget/components/Swap.d.ts +2 -2
  50. package/dist/widget/components/Swap.d.ts.map +1 -1
  51. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  52. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  53. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  54. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  55. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  56. package/dist/widget/components/Tooltip.d.ts +9 -0
  57. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  58. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  59. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  60. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  61. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  62. package/dist/widget/css/compiled.css +1 -1
  63. package/dist/widget/hooks/useQuote.d.ts +83 -0
  64. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  65. package/dist/widget/hooks/useSendForm.d.ts +2 -2
  66. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  67. package/dist/widget/index.js +2 -2
  68. package/dist/widget/widget.d.ts +5 -0
  69. package/dist/widget/widget.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/abortController.ts +35 -0
  72. package/src/constants.ts +3 -0
  73. package/src/fees.ts +199 -0
  74. package/src/prepareSend.ts +225 -398
  75. package/src/trails.ts +3 -3
  76. package/src/transactions.ts +62 -18
  77. package/src/widget/compiled.css +1 -1
  78. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  79. package/src/widget/components/AccountSettings.tsx +48 -36
  80. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  81. package/src/widget/components/ClassicSwap.tsx +24 -62
  82. package/src/widget/components/ConnectWallet.tsx +4 -1
  83. package/src/widget/components/ConnectedWallets.tsx +21 -21
  84. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  85. package/src/widget/components/Earn.tsx +34 -29
  86. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  87. package/src/widget/components/FeeBreakdown.tsx +155 -0
  88. package/src/widget/components/Fund.tsx +10 -26
  89. package/src/widget/components/FundSwap.tsx +2 -2
  90. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  91. package/src/widget/components/Identicon.tsx +164 -95
  92. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  93. package/src/widget/components/Modal.tsx +0 -12
  94. package/src/widget/components/Pay.tsx +65 -63
  95. package/src/widget/components/PoolDeposit.tsx +206 -230
  96. package/src/widget/components/PoolWithdraw.tsx +219 -238
  97. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  98. package/src/widget/components/QuoteDetails.tsx +25 -8
  99. package/src/widget/components/Receipt.tsx +16 -2
  100. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  101. package/src/widget/components/Recipients.tsx +1 -1
  102. package/src/widget/components/ScreenHeader.tsx +60 -36
  103. package/src/widget/components/Swap.tsx +2 -2
  104. package/src/widget/components/ThemeProvider.tsx +2 -1
  105. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  106. package/src/widget/components/TokenImage.tsx +1 -1
  107. package/src/widget/components/TokenSelector.tsx +62 -53
  108. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  109. package/src/widget/components/Tooltip.tsx +51 -0
  110. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  111. package/src/widget/components/WaasFeeOptions.tsx +124 -5
  112. package/src/widget/components/WalletConfirmation.tsx +23 -13
  113. package/src/widget/components/WalletConnect.tsx +93 -29
  114. package/src/widget/hooks/useQuote.ts +413 -0
  115. package/src/widget/hooks/useSendForm.ts +8 -4
  116. package/src/widget/widget.tsx +175 -190
@@ -3,6 +3,7 @@ import type React from "react"
3
3
  import { useEffect, useMemo, useRef, useState } from "react"
4
4
  import { ChainImage } from "./ChainImage.js"
5
5
  import { AllChainsIcon } from "./AllChainsIcon.js"
6
+ import { getChainInfo } from "../../chains.js"
6
7
 
7
8
  export interface Chain {
8
9
  chainId: number
@@ -94,9 +95,29 @@ export const ChainFilterDropdown: React.FC<ChainFilterDropdownProps> = ({
94
95
  }
95
96
  }
96
97
 
97
- const selectedChain = chains.find(
98
- (chain) => chain.chainId === selectedChainId,
99
- )
98
+ // Find selected chain in the chains array, or get it from getChainInfo if not found
99
+ // This handles the case where user selects a chain with no tokens
100
+ const selectedChain = useMemo(() => {
101
+ if (selectedChainId === null) return null
102
+
103
+ const chainInList = chains.find(
104
+ (chain) => chain.chainId === selectedChainId,
105
+ )
106
+
107
+ if (chainInList) return chainInList
108
+
109
+ // If chain not in list (e.g., no tokens on that chain), get info from getChainInfo
110
+ const chainInfo = getChainInfo(selectedChainId)
111
+ if (chainInfo) {
112
+ return {
113
+ chainId: chainInfo.id,
114
+ name: chainInfo.name,
115
+ imageUrl: chainInfo.imageUrl,
116
+ }
117
+ }
118
+
119
+ return null
120
+ }, [chains, selectedChainId])
100
121
 
101
122
  return (
102
123
  <div className={`relative ${className}`} ref={dropdownRef}>
@@ -1,6 +1,6 @@
1
1
  import { Loader2, ArrowDown } from "lucide-react"
2
2
  import type React from "react"
3
- import { useCallback, useEffect, useRef, useState, useMemo } from "react"
3
+ import { useCallback, useEffect, useRef, useState } from "react"
4
4
  import type { Account, WalletClient } from "viem"
5
5
  import { zeroAddress } from "viem"
6
6
  import type { TransactionState } from "../../transactions.js"
@@ -28,6 +28,7 @@ import { RecipientSelectorButton } from "./RecipientSelectorButton.js"
28
28
  import { RefundWarning } from "./RefundWarning.js"
29
29
  import { Identicon } from "./Identicon.js"
30
30
  import { truncateAddress } from "../../utils.js"
31
+ import { useDynamicInputStyles } from "./DynamicInputStyles.js"
31
32
 
32
33
  interface ClassicSwapProps {
33
34
  selectedToken: Token | null
@@ -35,13 +36,13 @@ interface ClassicSwapProps {
35
36
  onBack?: () => void
36
37
  onConfirm: () => void
37
38
  onComplete: (result: OnCompleteProps) => void
38
- account: Account
39
+ account?: Account
39
40
  toRecipient?: string
40
41
  toAmount?: string
41
42
  toChainId?: number
42
43
  toToken?: string
43
44
  toCalldata?: string
44
- walletClient: WalletClient
45
+ walletClient?: WalletClient
45
46
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
46
47
  onError: (error: Error | string | null) => void
47
48
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -146,7 +147,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
146
147
  } = useSendForm({
147
148
  account,
148
149
  toAmount: tradeType === TradeType.EXACT_OUTPUT ? buyAmount : toAmount,
149
- toRecipient: toRecipient || account.address,
150
+ toRecipient: toRecipient || account?.address,
150
151
  toChainId,
151
152
  toToken,
152
153
  toCalldata,
@@ -363,51 +364,12 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
363
364
  }
364
365
 
365
366
  // Dynamic font size based on input length
366
- const sellInputStyles = useMemo(() => {
367
- const inputLength = sellAmount.length
368
- let fontSize: string
369
-
370
- if (inputLength > 12) {
371
- fontSize = "1rem"
372
- } else if (inputLength > 9) {
373
- fontSize = "1.25rem"
374
- } else if (inputLength > 6) {
375
- fontSize = "1.5rem"
376
- } else if (inputLength > 3) {
377
- fontSize = "1.75rem"
378
- } else {
379
- fontSize = "2rem"
380
- }
381
-
382
- return {
383
- fontSize,
384
- transition: "all 0.1s ease-in-out",
385
- }
386
- }, [sellAmount.length])
387
-
388
- const buyInputStyles = useMemo(() => {
389
- const inputValue =
390
- tradeType === TradeType.EXACT_OUTPUT ? buyAmount : toAmountDisplay || ""
391
- const inputLength = inputValue.length
392
- let fontSize: string
393
-
394
- if (inputLength > 12) {
395
- fontSize = "1rem"
396
- } else if (inputLength > 9) {
397
- fontSize = "1.25rem"
398
- } else if (inputLength > 6) {
399
- fontSize = "1.5rem"
400
- } else if (inputLength > 3) {
401
- fontSize = "1.75rem"
402
- } else {
403
- fontSize = "2rem"
404
- }
367
+ const sellInputStyles = useDynamicInputStyles({ inputValue: sellAmount })
405
368
 
406
- return {
407
- fontSize,
408
- transition: "all 0.1s ease-in-out",
409
- }
410
- }, [tradeType, buyAmount, toAmountDisplay])
369
+ const buyInputStyles = useDynamicInputStyles({
370
+ inputValue:
371
+ tradeType === TradeType.EXACT_OUTPUT ? buyAmount : toAmountDisplay || "",
372
+ })
411
373
 
412
374
  // Handle source token selection from full-screen selector
413
375
  const handleSourceTokenSelectorSelect = useCallback(
@@ -585,10 +547,10 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
585
547
 
586
548
  <form onSubmit={handleSubmit} className="space-y-1">
587
549
  {/* Input Section - Amount + Token Selection */}
588
- <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">
550
+ <div className="pt-4 pb-4 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">
589
551
  {/* Sell Label */}
590
- <div className="flex justify-between items-center mb-2">
591
- <div className="text-sm font-medium trails-text-secondary text-left">
552
+ <div className="mb-4 flex justify-between items-center">
553
+ <div className="text-sm font-medium trails-text-secondary text-left m-0">
592
554
  {mode === "fund" ? "Payment method" : "Sell"}
593
555
  </div>
594
556
  <FundingMethodSelectorButton />
@@ -604,7 +566,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
604
566
  type="text"
605
567
  value={sellAmount}
606
568
  onChange={(e) => handleSellAmountChange(e.target.value)}
607
- placeholder={`0 ${originToken?.symbol || ""}`}
569
+ placeholder={"0"}
608
570
  className={`w-full bg-transparent font-bold trails-text-primary placeholder:trails-text-muted border-none outline-none ${
609
571
  isLoadingQuote && tradeType === TradeType.EXACT_OUTPUT
610
572
  ? "animate-pulse"
@@ -632,10 +594,10 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
632
594
  </div>
633
595
 
634
596
  {/* Bottom Info Row for sell */}
635
- <div className="mt-2 flex justify-between items-center">
597
+ <div className="mt-4 flex justify-between items-center">
636
598
  {/* USD Amount */}
637
599
  {originToken?.symbol && (
638
- <div className="text-xs trails-text-muted">
600
+ <div className="text-xs text-gray-500 dark:text-gray-400">
639
601
  ≈{" "}
640
602
  {tradeType === TradeType.EXACT_INPUT
641
603
  ? amountUsdDisplay || "$0.00"
@@ -650,7 +612,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
650
612
  <div className="flex items-center space-x-2">
651
613
  <button
652
614
  type="button"
653
- className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
615
+ 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"
654
616
  onClick={() => {
655
617
  if (balanceFormatted) {
656
618
  const balance = parseFloat(balanceFormatted)
@@ -716,10 +678,10 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
716
678
  </div>
717
679
 
718
680
  {/* Output Section - Amount + Token Selection */}
719
- <div className="trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 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">
681
+ <div className="pt-4 pb-4 trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 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 mb-4">
720
682
  {/* Buy Label */}
721
- <div className="flex justify-between items-center mb-2">
722
- <div className="text-sm font-medium trails-text-secondary text-left">
683
+ <div className="mb-4 flex justify-between items-center mb-2">
684
+ <div className="text-sm font-medium trails-text-secondary text-left m-0">
723
685
  {mode === "fund" ? "Recipient" : "Buy"}
724
686
  </div>
725
687
  {toRecipient ? (
@@ -747,7 +709,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
747
709
  }
748
710
  onChange={(e) => handleBuyAmountChange(e.target.value)}
749
711
  onFocus={handleBuyInputFocus}
750
- placeholder={`0 ${selectedDestToken?.symbol || ""}`}
712
+ placeholder={"0"}
751
713
  className={`w-full bg-transparent font-bold placeholder:trails-text-muted border-none outline-none ${
752
714
  !amount
753
715
  ? "text-gray-400 dark:text-gray-500"
@@ -777,11 +739,11 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
777
739
  </div>
778
740
 
779
741
  {/* Bottom Info Row */}
780
- <div className="mt-2 flex justify-between items-center">
742
+ <div className="mt-4 flex justify-between items-center">
781
743
  {/* Destination Amount USD from Quote */}
782
744
  {(prepareSendQuote?.destinationAmountUsdDisplay ||
783
745
  (isLoadingQuote && tradeType === TradeType.EXACT_INPUT)) && (
784
- <div className="text-xs trails-text-muted">
746
+ <div className="text-xs text-gray-500 dark:text-gray-400">
785
747
  ≈{" "}
786
748
  {isLoadingQuote && tradeType === TradeType.EXACT_INPUT
787
749
  ? "$0.00"
@@ -833,7 +795,7 @@ export const ClassicSwap: React.FC<ClassicSwapProps> = ({
833
795
 
834
796
  {/* Quote Details */}
835
797
  {prepareSendQuote && (
836
- <div className="space-y-2">
798
+ <div className="mb-4">
837
799
  <QuoteDetails
838
800
  quote={prepareSendQuote}
839
801
  showContent={true}
@@ -36,7 +36,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
36
36
  lastClickedWallet,
37
37
  showDisconnect = true,
38
38
  }) => {
39
- const { isConnected, address, connector } = useAccount()
39
+ const { address, connector } = useAccount()
40
40
  const connections = useConnections()
41
41
  const { switchAccount } = useSwitchAccount()
42
42
  const { setCurrentScreen } = useCurrentScreen()
@@ -45,6 +45,9 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
45
45
  const connectors = useConnectors()
46
46
  const [error, setError] = useState<string | null>(null)
47
47
 
48
+ // Check if there are any connected accounts across all connectors
49
+ const isConnected = connections.length > 0
50
+
48
51
  useEffect(() => {
49
52
  if (error) {
50
53
  if (onError) {
@@ -86,25 +86,8 @@ const WalletItem: React.FC<WalletItemProps> = ({
86
86
  {/* Identicon */}
87
87
  <Identicon value={wallet.address} size={32} />
88
88
  <div className="flex flex-col items-start space-y-1">
89
- <div className="flex items-center space-x-2">
90
- {/* Wallet Icon */}
91
- {typeof wallet.walletConfig?.icon === "string" ? (
92
- <img
93
- src={wallet.walletConfig.icon}
94
- alt={wallet.walletConfig.name}
95
- className="h-4 w-4"
96
- />
97
- ) : (
98
- <Wallet className="h-4 w-4 text-gray-600 dark:text-gray-400" />
99
- )}
100
- <span className="text-sm font-bold text-gray-900 dark:text-gray-100">
101
- {wallet.walletConfig?.name ||
102
- wallet.connector?.name ||
103
- "Wallet"}
104
- </span>
105
- </div>
106
- <div className="flex items-center space-x-1">
107
- <span className="text-xs text-gray-500 dark:text-gray-400 font-mono">
89
+ <div className="flex items-center space-x-1 m-0">
90
+ <span className="text-sm font-bold text-gray-900 dark:text-gray-100 whitespace-nowrap">
108
91
  {truncateAddress(wallet.address)}
109
92
  </span>
110
93
  <div
@@ -147,6 +130,23 @@ const WalletItem: React.FC<WalletItemProps> = ({
147
130
  )}
148
131
  </div>
149
132
  </div>
133
+ <div className="flex items-center space-x-2">
134
+ {/* Wallet Icon */}
135
+ {typeof wallet.walletConfig?.icon === "string" ? (
136
+ <img
137
+ src={wallet.walletConfig.icon}
138
+ alt={wallet.walletConfig.name}
139
+ className="h-3.5 w-3.5 mr-1"
140
+ />
141
+ ) : (
142
+ <Wallet className="h-3.5 w-3.5 text-gray-600 dark:text-gray-400 mr-1" />
143
+ )}
144
+ <span className="text-xs text-gray-500 dark:text-gray-400">
145
+ {wallet.walletConfig?.name ||
146
+ wallet.connector?.name ||
147
+ "Wallet"}
148
+ </span>
149
+ </div>
150
150
  </div>
151
151
  </div>
152
152
 
@@ -156,7 +156,7 @@ const WalletItem: React.FC<WalletItemProps> = ({
156
156
  Active
157
157
  </span>
158
158
  ) : !onWalletSelect ? (
159
- <span className="text-xs px-2 py-1 rounded font-medium bg-gray-200 text-gray-700 dark:bg-gray-600 dark:text-gray-300 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
159
+ <span className="whitespace-nowrap text-xs px-2 py-1 rounded font-medium bg-gray-200 text-gray-700 dark:bg-gray-600 dark:text-gray-300 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
160
160
  Set as active
161
161
  </span>
162
162
  ) : null}
@@ -324,7 +324,7 @@ export const ConnectedWallets: React.FC<ConnectedWalletsProps> = ({
324
324
  return (
325
325
  <div className={`space-y-2 ${className}`}>
326
326
  {/* Header */}
327
- <div className="text-sm font-medium text-gray-700 dark:text-gray-300 text-left mb-3">
327
+ <div className="text-sm font-medium text-gray-900 dark:text-gray-100 text-left mb-2">
328
328
  Connected Wallets
329
329
  </div>
330
330
 
@@ -0,0 +1,76 @@
1
+ import { useMemo } from "react"
2
+
3
+ interface DynamicInputStylesProps {
4
+ inputValue: string
5
+ variant?: "default" | "smaller" | "inline"
6
+ }
7
+
8
+ export const useDynamicInputStyles = ({
9
+ inputValue,
10
+ variant = "default",
11
+ }: DynamicInputStylesProps) => {
12
+ return useMemo(() => {
13
+ const inputLength = inputValue.length
14
+ let fontSize: string
15
+
16
+ if (variant === "smaller") {
17
+ // Used in Fund.tsx - smaller font sizes
18
+ if (inputLength > 15) {
19
+ fontSize = "1rem"
20
+ } else if (inputLength > 12) {
21
+ fontSize = "1.125rem"
22
+ } else if (inputLength > 9) {
23
+ fontSize = "1.25rem"
24
+ } else if (inputLength > 6) {
25
+ fontSize = "1.375rem"
26
+ } else {
27
+ fontSize = "1.5rem"
28
+ }
29
+ } else if (variant === "inline") {
30
+ // Used in PoolWithdraw.tsx and PoolDeposit.tsx - inline styles with width
31
+ if (inputLength > 15) {
32
+ fontSize = "1rem"
33
+ } else if (inputLength > 12) {
34
+ fontSize = "1.125rem"
35
+ } else if (inputLength > 9) {
36
+ fontSize = "1.25rem"
37
+ } else if (inputLength > 6) {
38
+ fontSize = "1.375rem"
39
+ } else {
40
+ fontSize = "1.5rem"
41
+ }
42
+ } else {
43
+ // Default variant - used in Pay.tsx and ClassicSwap.tsx
44
+ if (inputLength > 15) {
45
+ fontSize = "1.25rem"
46
+ } else if (inputLength > 12) {
47
+ fontSize = "1.5rem"
48
+ } else if (inputLength > 9) {
49
+ fontSize = "1.75rem"
50
+ } else if (inputLength > 6) {
51
+ fontSize = "2rem"
52
+ } else {
53
+ fontSize = "2.25rem"
54
+ }
55
+ }
56
+
57
+ const baseStyles = {
58
+ fontSize,
59
+ transition: "all 0.1s ease-in-out",
60
+ }
61
+
62
+ // Add width and other properties for inline variant
63
+ if (variant === "inline") {
64
+ return {
65
+ ...baseStyles,
66
+ width: `${Math.max((inputValue || "0").length, 1)}ch`,
67
+ minWidth: "1ch",
68
+ maxWidth: "270px",
69
+ padding: "0",
70
+ margin: "0",
71
+ }
72
+ }
73
+
74
+ return baseStyles
75
+ }, [inputValue, variant])
76
+ }
@@ -14,8 +14,8 @@ import type { SupportedToken } from "../../tokens.js"
14
14
  interface EarnProps {
15
15
  onBack?: () => void
16
16
  onContinue: () => void
17
- account: Account
18
- walletClient: WalletClient
17
+ account?: Account
18
+ walletClient?: WalletClient
19
19
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
20
20
  onError: (error: Error | string | null) => void
21
21
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -64,6 +64,7 @@ export const Earn: React.FC<EarnProps> = ({
64
64
  onTrackToken,
65
65
  }) => {
66
66
  const [activeTab, setActiveTab] = useState<"deposit" | "withdraw">("deposit")
67
+ const [isShowingPoolSelector, setIsShowingPoolSelector] = useState(false) // Track if pool selector is shown
67
68
 
68
69
  return (
69
70
  <div className="space-y-2">
@@ -74,33 +75,35 @@ export const Earn: React.FC<EarnProps> = ({
74
75
  showAccountActions={true}
75
76
  />
76
77
 
77
- {/* Tabs */}
78
- <div className="flex justify-start space-x-2">
79
- <button
80
- type="button"
81
- onClick={() => setActiveTab("deposit")}
82
- className={`flex items-center space-x-2 py-2 px-4 text-xs font-bold cursor-pointer trails-border-radius-input ${
83
- activeTab === "deposit"
84
- ? "trails-bg-secondary text-blue-500"
85
- : "trails-text-secondary"
86
- }`}
87
- >
88
- <ArrowDownCircle className="w-4 h-4" />
89
- <span>Deposit</span>
90
- </button>
91
- <button
92
- type="button"
93
- onClick={() => setActiveTab("withdraw")}
94
- className={`flex items-center space-x-2 py-2 px-4 text-xs font-bold cursor-pointer trails-border-radius-input ${
95
- activeTab === "withdraw"
96
- ? "trails-bg-secondary text-blue-500"
97
- : "trails-text-secondary"
98
- }`}
99
- >
100
- <ArrowUpCircle className="w-4 h-4" />
101
- <span>Withdraw</span>
102
- </button>
103
- </div>
78
+ {/* Tabs - only show when not in pool selector mode */}
79
+ {!isShowingPoolSelector && (
80
+ <div className="flex justify-start space-x-2">
81
+ <button
82
+ type="button"
83
+ onClick={() => setActiveTab("deposit")}
84
+ className={`flex items-center space-x-2 py-2 px-4 text-xs font-bold cursor-pointer trails-border-radius-input ${
85
+ activeTab === "deposit"
86
+ ? "trails-bg-secondary text-blue-500"
87
+ : "trails-text-secondary"
88
+ }`}
89
+ >
90
+ <ArrowDownCircle className="w-4 h-4" />
91
+ <span>Deposit</span>
92
+ </button>
93
+ <button
94
+ type="button"
95
+ onClick={() => setActiveTab("withdraw")}
96
+ className={`flex items-center space-x-2 py-2 px-4 text-xs font-bold cursor-pointer trails-border-radius-input ${
97
+ activeTab === "withdraw"
98
+ ? "trails-bg-secondary text-blue-500"
99
+ : "trails-text-secondary"
100
+ }`}
101
+ >
102
+ <ArrowUpCircle className="w-4 h-4" />
103
+ <span>Withdraw</span>
104
+ </button>
105
+ </div>
106
+ )}
104
107
 
105
108
  {/* Content */}
106
109
  {activeTab === "deposit" ? (
@@ -123,6 +126,7 @@ export const Earn: React.FC<EarnProps> = ({
123
126
  recentTokens={recentTokens}
124
127
  onRecentTokenSelect={onRecentTokenSelect}
125
128
  onTrackToken={onTrackToken}
129
+ onPoolSelectorStateChange={setIsShowingPoolSelector}
126
130
  />
127
131
  ) : (
128
132
  <PoolWithdraw
@@ -144,6 +148,7 @@ export const Earn: React.FC<EarnProps> = ({
144
148
  recentTokens={recentTokens}
145
149
  onRecentTokenSelect={onRecentTokenSelect}
146
150
  onTrackToken={onTrackToken}
151
+ onPoolSelectorStateChange={setIsShowingPoolSelector}
147
152
  />
148
153
  )}
149
154
  </div>
@@ -0,0 +1,130 @@
1
+ import { type CSSProperties, useEffect, useState } from "react"
2
+
3
+ export function ErrorAnimationIcon() {
4
+ const [animate, setAnimate] = useState(false)
5
+
6
+ useEffect(() => {
7
+ const timer = setTimeout(() => {
8
+ setAnimate(true)
9
+ }, 100)
10
+
11
+ return () => clearTimeout(timer)
12
+ }, [])
13
+
14
+ const styles: Record<string, CSSProperties> = {
15
+ container: {
16
+ width: "80px",
17
+ height: "80px",
18
+ position: "relative" as const,
19
+ },
20
+ circleContainer: {
21
+ position: "relative" as const,
22
+ width: "100%",
23
+ height: "100%",
24
+ },
25
+ circleFill: {
26
+ position: "absolute" as const,
27
+ top: 0,
28
+ left: 0,
29
+ width: "100%",
30
+ height: "100%",
31
+ borderRadius: "50%",
32
+ backgroundColor: "#ef4444",
33
+ transform: animate ? "scale(1)" : "scale(0)",
34
+ opacity: animate ? 1 : 0,
35
+ transition: "transform 0.15s ease-out 0.3s, opacity 0.15s ease-out 0.3s",
36
+ },
37
+ progressRing: {
38
+ position: "absolute" as const,
39
+ top: 0,
40
+ left: 0,
41
+ width: "100%",
42
+ height: "100%",
43
+ transform: "rotate(-90deg)",
44
+ },
45
+ circle: {
46
+ fill: "none",
47
+ stroke: "url(#redGradient)",
48
+ strokeWidth: 4,
49
+ strokeLinecap: "round" as const,
50
+ strokeDasharray: 226,
51
+ strokeDashoffset: animate ? 0 : 226,
52
+ transition: "stroke-dashoffset 0.3s ease-out",
53
+ },
54
+ xContainer: {
55
+ position: "absolute" as const,
56
+ top: "50%",
57
+ left: "50%",
58
+ transform: animate
59
+ ? "translate(-50%, -50%) scale(1)"
60
+ : "translate(-50%, -50%) scale(0)",
61
+ opacity: animate ? 1 : 0,
62
+ zIndex: 10,
63
+ transition: "transform 0.1s ease-out 0.4s, opacity 0.1s ease-out 0.4s",
64
+ },
65
+ xPath: {
66
+ fill: "none",
67
+ stroke: "white",
68
+ strokeWidth: 3,
69
+ strokeLinecap: "round" as const,
70
+ strokeLinejoin: "round" as const,
71
+ strokeDasharray: 15,
72
+ strokeDashoffset: animate ? 0 : 15,
73
+ transition: "stroke-dashoffset 0.15s ease-out 0.45s",
74
+ },
75
+ xPathSecond: {
76
+ fill: "none",
77
+ stroke: "white",
78
+ strokeWidth: 3,
79
+ strokeLinecap: "round" as const,
80
+ strokeLinejoin: "round" as const,
81
+ strokeDasharray: 15,
82
+ strokeDashoffset: animate ? 0 : 15,
83
+ transition: "stroke-dashoffset 0.15s ease-out 0.5s",
84
+ },
85
+ }
86
+
87
+ return (
88
+ <div style={styles.container}>
89
+ <div style={styles.circleContainer}>
90
+ {/* Circle fill */}
91
+ <div style={styles.circleFill} />
92
+
93
+ {/* Progress ring */}
94
+ <svg style={styles.progressRing}>
95
+ <defs>
96
+ <radialGradient
97
+ id="redGradient"
98
+ cx="50%"
99
+ cy="50%"
100
+ r="50%"
101
+ gradientUnits="objectBoundingBox"
102
+ >
103
+ <stop
104
+ offset="0%"
105
+ style={{ stopColor: "#ef4444", stopOpacity: 0 }}
106
+ />
107
+ <stop
108
+ offset="70%"
109
+ style={{ stopColor: "#ef4444", stopOpacity: 0.3 }}
110
+ />
111
+ <stop
112
+ offset="100%"
113
+ style={{ stopColor: "#ef4444", stopOpacity: 1 }}
114
+ />
115
+ </radialGradient>
116
+ </defs>
117
+ <circle cx="40" cy="40" r="36" style={styles.circle} />
118
+ </svg>
119
+
120
+ {/* X icon */}
121
+ <div style={styles.xContainer}>
122
+ <svg width="48" height="48" viewBox="0 0 24 24">
123
+ <path d="M6 6L18 18" style={styles.xPath} />
124
+ <path d="M18 6L6 18" style={styles.xPathSecond} />
125
+ </svg>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ )
130
+ }