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,43 +1,21 @@
1
- import {
2
- TrendingUp,
3
- ArrowDown,
4
- ChevronRight,
5
- Search,
6
- ChevronDown,
7
- Loader2,
8
- } from "lucide-react"
9
- import { useEffect, useState, useRef } from "react"
1
+ import { useState } from "react"
10
2
  import type React from "react"
11
3
  import type { Account, WalletClient } from "viem"
12
4
  import type { TransactionState } from "../../transactions.js"
13
5
  import type { OnCompleteProps } from "../hooks/useSendForm.js"
14
6
  import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
15
- import { useSendForm } from "../hooks/useSendForm.js"
16
- import { TradeType } from "../../prepareSend.js"
17
- import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
18
- import { useEarnPool } from "../hooks/useEarnPool.js"
19
- import { useTokenList } from "../hooks/useTokenList.js"
20
- import { useMode } from "../hooks/useMode.js"
21
- import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
22
- import { TokenImage } from "./TokenImage.js"
23
- import { TokenSelector } from "./TokenSelector.js"
24
- import { EarnPools } from "./EarnPools.js"
7
+ import { ArrowUpCircle, ArrowDownCircle } from "lucide-react"
25
8
  import { ScreenHeader } from "./ScreenHeader.js"
26
- import { ChainList } from "./ChainList.js"
27
- import { QuoteDetails } from "./QuoteDetails.js"
28
- import { formatTvl } from "../../prices.js"
29
- import { getExplorerUrlForAddress } from "../../explorer.js"
30
- import aaveLogo from "../assets/aave.svg"
31
- import morphoLogo from "../assets/morpho.svg"
9
+ import { PoolDeposit } from "./PoolDeposit.js"
10
+ import { PoolWithdraw } from "./PoolWithdraw.js"
32
11
  import type { PrepareSendQuote } from "../../prepareSend.js"
33
12
  import type { SupportedToken } from "../../tokens.js"
34
- import { logger } from "../../logger.js"
35
13
 
36
14
  interface EarnProps {
37
15
  onBack?: () => void
38
16
  onContinue: () => void
39
- account: Account
40
- walletClient: WalletClient
17
+ account?: Account
18
+ walletClient?: WalletClient
41
19
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
42
20
  onError: (error: Error | string | null) => void
43
21
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -85,205 +63,11 @@ export const Earn: React.FC<EarnProps> = ({
85
63
  onRecentTokenSelect,
86
64
  onTrackToken,
87
65
  }) => {
88
- const { mode } = useMode()
89
- const { isBalanceVisible } = useBalanceVisible()
90
- const { selectedToken: originToken, setSelectedToken: setOriginToken } =
91
- useOriginSelectedToken()
92
- const { selectedPool, setSelectedPool } = useEarnPool()
93
-
94
- const [showEarnPools, setShowEarnPools] = useState(false)
95
- const [showOriginTokenSelector, setShowOriginTokenSelector] = useState(false)
96
- const [showOriginChainList, setShowOriginChainList] = useState(false)
97
- const [amount, setAmount] = useState("")
98
- const inputRef = useRef<HTMLInputElement>(null)
99
-
100
- // Get sorted tokens to auto-select the highest USD value token
101
- const { filteredTokensFormatted, isLoadingTokens } = useTokenList({
102
- onContinue: () => {}, // Not used for auto-selection
103
- onError: () => {}, // Not used for auto-selection
104
- fundMethod: undefined,
105
- allSupportedTokens: false,
106
- })
107
-
108
- // Use useSendForm for quote functionality when both token and pool are selected
109
- const {
110
- amountUsdDisplay,
111
- balanceFormatted,
112
- isLoadingQuote,
113
- prepareSendQuote,
114
- setAmount: setSendFormAmount,
115
- handleSubmit,
116
- isSubmitting,
117
- buttonText,
118
- isValidRecipient,
119
- } = useSendForm({
120
- account,
121
- toAmount: undefined,
122
- toRecipient: selectedPool?.depositAddress,
123
- toChainId: selectedPool?.chainId,
124
- toToken: selectedPool?.token.address,
125
- toCalldata: undefined,
126
- walletClient,
127
- onTransactionStateChange,
128
- onError,
129
- onWaitingForWalletConfirm,
130
- paymasterUrls,
131
- gasless,
132
- onConfirm,
133
- onComplete,
134
- onSend,
135
- selectedToken: originToken as any,
136
- setWalletConfirmRetryHandler,
137
- tradeType: TradeType.EXACT_INPUT,
138
- quoteProvider,
139
- fundMethod,
140
- mode,
141
- onNavigateToMeshConnect,
142
- checkoutOnHandlers,
143
- })
144
-
145
- // Auto-select the highest USD value token as origin token
146
- useEffect(() => {
147
- if (
148
- !originToken &&
149
- !isLoadingTokens &&
150
- filteredTokensFormatted?.length > 0
151
- ) {
152
- const highestValueToken = filteredTokensFormatted[0] // First token is highest USD value
153
- if (highestValueToken && Number(highestValueToken.balanceUsd) > 0) {
154
- const decimals =
155
- highestValueToken.contractInfo?.decimals ??
156
- (highestValueToken as any)?.decimals
157
- setOriginToken({
158
- ...highestValueToken,
159
- contractInfo: {
160
- decimals,
161
- contractAddress: highestValueToken.contractAddress,
162
- symbol: highestValueToken.symbol,
163
- name: highestValueToken.name,
164
- },
165
- } as any)
166
- }
167
- }
168
- }, [originToken, isLoadingTokens, filteredTokensFormatted, setOriginToken])
169
-
170
- // Auto-focus input field on component mount
171
- useEffect(() => {
172
- if (inputRef.current) {
173
- inputRef.current.focus()
174
- }
175
- }, [])
176
-
177
- const handleAmountChange = (value: string) => {
178
- // Validate decimal places (max 8 decimals)
179
- const decimalMatch = value.match(/^\d*\.?\d{0,8}$/)
180
- if (!decimalMatch && value !== "") {
181
- return // Don't update if more than 8 decimals
182
- }
183
- setAmount(value)
184
- setSendFormAmount(value) // Sync with useSendForm for quote functionality
185
- }
186
-
187
- const handlePoolSelect = (pool: any) => {
188
- logger.console.log("Selected pool:", pool)
189
- setSelectedPool(pool)
190
- setShowEarnPools(false)
191
- }
192
-
193
- const handleOriginTokenSelect = (token: any) => {
194
- const formattedToken = {
195
- ...token,
196
- decimals: token.contractInfo?.decimals || token.decimals,
197
- contractInfo: {
198
- decimals: token.contractInfo?.decimals || token.decimals,
199
- contractAddress: token.contractAddress,
200
- symbol: token.symbol,
201
- name: token.name,
202
- },
203
- } as any
204
- setOriginToken(formattedToken)
205
- logger.console.log("[trails-sdk] selected origin token", token)
206
- setShowOriginTokenSelector(false)
207
- // Track the token selection
208
- onTrackToken?.(token)
209
- }
210
-
211
- // Handle percentage button clicks
212
- const handlePercentageClick = (percentage: number) => {
213
- if (!originToken || !balanceFormatted) return
214
-
215
- // Parse the balance and calculate percentage
216
- const balance = parseFloat(balanceFormatted)
217
- if (Number.isNaN(balance)) return
218
-
219
- const calculatedAmount = (balance * percentage) / 100
220
- const amountStr = calculatedAmount.toFixed(6)
221
- setAmount(amountStr)
222
- setSendFormAmount(amountStr)
223
- }
224
-
225
- // Handle max button click
226
- const handleMaxClick = () => {
227
- if (!balanceFormatted) return
228
-
229
- const balance = parseFloat(balanceFormatted)
230
- if (Number.isNaN(balance)) return
231
-
232
- const amountStr = balance.toFixed(6)
233
- setAmount(amountStr)
234
- setSendFormAmount(amountStr)
235
- }
236
-
237
- if (showOriginChainList) {
238
- return (
239
- <ChainList
240
- onBack={() => {
241
- setShowOriginChainList(false)
242
- setShowOriginTokenSelector(true)
243
- }}
244
- />
245
- )
246
- }
247
-
248
- if (showEarnPools) {
249
- return (
250
- <EarnPools
251
- onBack={() => setShowEarnPools(false)}
252
- onPoolSelect={handlePoolSelect}
253
- />
254
- )
255
- }
256
-
257
- if (showOriginTokenSelector) {
258
- return (
259
- <div className="space-y-2">
260
- <ScreenHeader
261
- onBack={() => setShowOriginTokenSelector(false)}
262
- headerContent="Select Token"
263
- headerContentAlign="left"
264
- showAccountActions={true}
265
- />
266
- <TokenSelector
267
- onTokenSelect={handleOriginTokenSelect}
268
- onError={onError}
269
- fundMethod={fundMethod}
270
- showContinueButton={false}
271
- compactMode={false}
272
- recentTokens={recentTokens}
273
- onRecentTokenSelect={onRecentTokenSelect}
274
- allSupportedTokens={false}
275
- chainListScreen={true}
276
- onNavigateToChainList={() => {
277
- setShowOriginTokenSelector(false)
278
- setShowOriginChainList(true)
279
- }}
280
- />
281
- </div>
282
- )
283
- }
66
+ const [activeTab, setActiveTab] = useState<"deposit" | "withdraw">("deposit")
67
+ const [isShowingPoolSelector, setIsShowingPoolSelector] = useState(false) // Track if pool selector is shown
284
68
 
285
69
  return (
286
- <div className="space-y-4">
70
+ <div className="space-y-2">
287
71
  <ScreenHeader
288
72
  onBack={onBack}
289
73
  headerContent="Earn"
@@ -291,376 +75,81 @@ export const Earn: React.FC<EarnProps> = ({
291
75
  showAccountActions={true}
292
76
  />
293
77
 
294
- <div className="space-y-1">
295
- {/* Origin Token Selection */}
296
- <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">
297
- {/* Deposit Label and Percentage Buttons */}
298
- <div className="flex justify-between items-center mb-2">
299
- <div className="text-sm font-semibold trails-text-secondary text-left">
300
- Deposit
301
- </div>
302
-
303
- {/* Percentage Buttons */}
304
- {originToken && (
305
- <div className="flex space-x-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
306
- <button
307
- type="button"
308
- onClick={() => handlePercentageClick(25)}
309
- className="py-1 px-2 text-xs font-medium trails-border-radius-container border border-solid transition-colors cursor-pointer border-gray-300 text-gray-600 dark:border-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg hover:border-gray-400 dark:hover:border-gray-500"
310
- >
311
- 25%
312
- </button>
313
- <button
314
- type="button"
315
- onClick={() => handlePercentageClick(50)}
316
- className="py-1 px-2 text-xs font-medium trails-border-radius-container border border-solid transition-colors cursor-pointer border-gray-300 text-gray-600 dark:border-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg hover:border-gray-400 dark:hover:border-gray-500"
317
- >
318
- 50%
319
- </button>
320
- <button
321
- type="button"
322
- onClick={() => handlePercentageClick(75)}
323
- className="py-1 px-2 text-xs font-medium trails-border-radius-container border border-solid transition-colors cursor-pointer border-gray-300 text-gray-600 dark:border-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg hover:border-gray-400 dark:hover:border-gray-500"
324
- >
325
- 75%
326
- </button>
327
- <button
328
- type="button"
329
- onClick={handleMaxClick}
330
- className="py-1 px-2 text-xs font-medium trails-border-radius-container border border-solid transition-colors cursor-pointer border-gray-300 text-gray-600 dark:border-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:trails-hover-bg hover:border-gray-400 dark:hover:border-gray-500"
331
- >
332
- Max
333
- </button>
334
- </div>
335
- )}
336
- </div>
337
-
338
- <div className="flex items-center space-x-2">
339
- {/* Amount Input */}
340
- <div className="flex-1">
341
- <div
342
- className="flex items-center justify-start cursor-text"
343
- onClick={() => inputRef.current?.focus()}
344
- >
345
- <div className="flex items-center">
346
- <input
347
- ref={inputRef}
348
- type="text"
349
- value={amount}
350
- onChange={(e) => handleAmountChange(e.target.value)}
351
- placeholder="0"
352
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary ${
353
- isLoadingQuote ? "animate-pulse" : ""
354
- }`}
355
- style={{
356
- fontSize:
357
- amount.length > 12
358
- ? "0.875rem"
359
- : amount.length > 9
360
- ? "1rem"
361
- : amount.length > 6
362
- ? "1.125rem"
363
- : amount.length > 3
364
- ? "1.25rem"
365
- : "1.5rem",
366
- width: `${Math.max((amount || "0").length, 1)}ch`,
367
- minWidth: "1ch",
368
- maxWidth: "270px",
369
- padding: "0",
370
- margin: "0",
371
- transition: "all 0.1s ease-in-out",
372
- }}
373
- inputMode="decimal"
374
- />
375
- <span
376
- className="font-bold text-gray-400 dark:text-gray-500"
377
- style={{
378
- fontSize:
379
- amount.length > 12
380
- ? "0.875rem"
381
- : amount.length > 9
382
- ? "1rem"
383
- : amount.length > 6
384
- ? "1.125rem"
385
- : amount.length > 3
386
- ? "1.25rem"
387
- : "1.5rem",
388
- marginLeft: "0.1em",
389
- padding: "0",
390
- transition: "all 0.2s ease-in-out",
391
- }}
392
- >
393
- {originToken?.symbol.slice(0, 4) || ""}
394
- </span>
395
- {isLoadingQuote && (
396
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
397
- )}
398
- </div>
399
- </div>
400
- </div>
401
-
402
- {/* Token Selection Button */}
403
- <button
404
- type="button"
405
- onClick={() => setShowOriginTokenSelector(true)}
406
- 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"
407
- >
408
- {originToken ? (
409
- <>
410
- <TokenImage
411
- symbol={originToken.symbol}
412
- imageUrl={originToken.imageUrl}
413
- chainId={originToken.chainId}
414
- size={20}
415
- />
416
- <span className="font-medium trails-text-primary text-sm">
417
- {originToken.symbol}
418
- </span>
419
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
420
- </>
421
- ) : (
422
- <>
423
- <span className="font-medium trails-text-muted text-sm">
424
- Select Token
425
- </span>
426
- <ChevronDown className="w-3.5 h-3.5 trails-text-muted" />
427
- </>
428
- )}
429
- </button>
430
- </div>
431
-
432
- {/* Bottom Info Row */}
433
- <div className="mt-2 flex justify-between items-center">
434
- {/* USD Amount */}
435
- <div className="text-xs trails-text-muted">
436
- {originToken?.symbol && amount ? (
437
- <>≈ {amountUsdDisplay || "$0.00"}</>
438
- ) : (
439
- <span>&nbsp;</span>
440
- )}
441
- </div>
442
-
443
- {/* Origin Token Balance */}
444
- <div className="text-xs trails-text-muted text-right">
445
- {originToken ? (
446
- <button
447
- type="button"
448
- className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
449
- onClick={handleMaxClick}
450
- onKeyDown={(e) => {
451
- if (e.key === "Enter" || e.key === " ") {
452
- e.preventDefault()
453
- handleMaxClick()
454
- }
455
- }}
456
- title="Click to use full balance"
457
- >
458
- Balance:{" "}
459
- {isBalanceVisible
460
- ? `${balanceFormatted || "0.00"} ${originToken.symbol}`
461
- : "••••••"}
462
- </button>
463
- ) : (
464
- <span>&nbsp;</span>
465
- )}
466
- </div>
467
- </div>
468
- </div>
469
-
470
- {/* Arrow Down Between Sections */}
471
- <div className="relative">
472
- <div className="absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 p-1.5 trails-border-radius-button trails-bg-tertiary transition-colors border-2 border-white dark:border-gray-800 z-1">
473
- <ArrowDown
474
- className="w-5 h-5 text-gray-900 dark:text-white"
475
- strokeWidth={2.5}
476
- />
477
- </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>
478
105
  </div>
106
+ )}
479
107
 
480
- {/* Destination Vault Selection */}
481
- <div className="trails-bg-secondary trails-border-radius-container transition-all duration-200 border border-transparent hover:!bg-white dark:hover:!bg-white hover:border-gray-400 dark:hover:border-gray-500">
482
- {selectedPool ? (
483
- <div className="p-3 trails-border-radius-container trails-bg-secondary transition-all overflow-hidden">
484
- {/* Vault Label */}
485
- <div className="flex justify-between items-center mb-2">
486
- <div className="text-sm font-semibold trails-text-secondary text-left">
487
- Vault
488
- </div>
489
- </div>
490
-
491
- <div className="px-1">
492
- <div className="flex items-center justify-between">
493
- <div className="flex items-center space-x-3">
494
- <div style={{ width: "32px", height: "32px" }}>
495
- <a
496
- href={getExplorerUrlForAddress({
497
- address: selectedPool.token.address,
498
- chainId: selectedPool.chainId,
499
- })}
500
- target="_blank"
501
- rel="noopener noreferrer"
502
- className="cursor-pointer"
503
- >
504
- <TokenImage
505
- symbol={selectedPool.token.symbol}
506
- imageUrl={selectedPool.token.logoUrl}
507
- chainId={selectedPool.chainId}
508
- size={32}
509
- />
510
- </a>
511
- </div>
512
- <div>
513
- <h3 className="font-medium text-gray-900 dark:text-white text-sm">
514
- {selectedPool.poolUrl ? (
515
- <a
516
- href={selectedPool.poolUrl}
517
- target="_blank"
518
- rel="noopener noreferrer"
519
- className="hover:underline cursor-pointer"
520
- >
521
- {selectedPool.name}
522
- </a>
523
- ) : (
524
- selectedPool.name
525
- )}
526
- </h3>
527
- <div className="flex items-center space-x-2">
528
- <span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
529
- {selectedPool.protocol === "Aave" && (
530
- <img
531
- src={aaveLogo}
532
- alt="Aave"
533
- className="w-3 h-3 mr-1"
534
- />
535
- )}
536
- {selectedPool.protocol === "Morpho" && (
537
- <img
538
- src={morphoLogo}
539
- alt="Morpho"
540
- className="w-3 h-3 mr-1"
541
- />
542
- )}
543
- {selectedPool.protocolUrl ? (
544
- <a
545
- href={selectedPool.protocolUrl}
546
- target="_blank"
547
- rel="noopener noreferrer"
548
- className="hover:underline cursor-pointer"
549
- >
550
- {selectedPool.protocol}
551
- </a>
552
- ) : (
553
- selectedPool.protocol
554
- )}
555
- </span>
556
- </div>
557
- </div>
558
- </div>
559
- <button
560
- type="button"
561
- title="Select Vault"
562
- onClick={() => setShowEarnPools(true)}
563
- className="text-right flex items-center space-x-3 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 rounded p-2 transition-colors"
564
- >
565
- <div>
566
- <div className="flex items-center justify-end space-x-1 text-green-600 dark:text-green-400 mb-1 whitespace-nowrap">
567
- <TrendingUp className="w-3 h-3" />
568
- <span className="font-semibold text-sm">
569
- {selectedPool.apy.toFixed(1)}% APY
570
- </span>
571
- </div>
572
- <p className="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
573
- TVL: {formatTvl(selectedPool.tvl)}
574
- </p>
575
- </div>
576
- <ChevronRight className="w-4 h-4 text-gray-400" />
577
- </button>
578
- </div>
579
- </div>
580
- </div>
581
- ) : (
582
- <button
583
- type="button"
584
- onClick={() => setShowEarnPools(true)}
585
- className="w-full py-6 px-4 trails-list-item trails-border-radius-container transition-all duration-200 cursor-pointer"
586
- >
587
- <div className="flex items-center justify-between">
588
- <div className="flex items-center space-x-3 flex-1">
589
- <div className="w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
590
- <Search className="w-4 h-4 text-gray-400" />
591
- </div>
592
- <div className="text-left flex-1">
593
- <div className="font-semibold text-gray-900 dark:text-white text-sm">
594
- Select vault to earn yield with
595
- </div>
596
- </div>
597
- </div>
598
- <ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
599
- </div>
600
- </button>
601
- )}
602
- </div>
603
- </div>
604
-
605
- {prepareSendQuote?.noSufficientBalance ? (
606
- <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
607
- <div className="flex items-center space-x-2">
608
- <svg
609
- className="w-4 h-4 text-amber-500 flex-shrink-0"
610
- fill="none"
611
- stroke="currentColor"
612
- viewBox="0 0 24 24"
613
- aria-hidden="true"
614
- >
615
- <path
616
- strokeLinecap="round"
617
- strokeLinejoin="round"
618
- strokeWidth={2}
619
- d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
620
- />
621
- </svg>
622
- <p className="text-sm text-amber-600 dark:text-amber-400">
623
- Insufficient balance to complete this transaction
624
- </p>
625
- </div>
626
- </div>
627
- ) : null}
628
-
629
- <form onSubmit={handleSubmit}>
630
- <button
631
- type="submit"
632
- disabled={
633
- !amount ||
634
- !isValidRecipient ||
635
- isSubmitting ||
636
- !originToken ||
637
- !selectedPool ||
638
- isLoadingQuote ||
639
- !prepareSendQuote ||
640
- prepareSendQuote?.noSufficientBalance
641
- }
642
- className={`w-full font-semibold py-4 px-4 trails-border-radius-button transition-colors bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 text-white disabled:text-gray-500 disabled:cursor-not-allowed cursor-pointer relative`}
643
- >
644
- {isSubmitting ? (
645
- <div className="flex items-center justify-center">
646
- <Loader2
647
- className={`w-5 h-5 animate-spin mr-2 ${"text-gray-400"}`}
648
- />
649
- <span>{buttonText}</span>
650
- </div>
651
- ) : prepareSendQuote?.noSufficientBalance ? (
652
- "Insufficient Balance"
653
- ) : (
654
- buttonText || "Deposit"
655
- )}
656
- </button>
657
- </form>
658
-
659
- {/* Quote Details */}
660
- {prepareSendQuote && (
661
- <div className="space-y-2">
662
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
663
- </div>
108
+ {/* Content */}
109
+ {activeTab === "deposit" ? (
110
+ <PoolDeposit
111
+ account={account}
112
+ walletClient={walletClient}
113
+ onTransactionStateChange={onTransactionStateChange}
114
+ onError={onError}
115
+ onWaitingForWalletConfirm={onWaitingForWalletConfirm}
116
+ onConfirm={onConfirm}
117
+ onComplete={onComplete}
118
+ onSend={onSend}
119
+ paymasterUrls={paymasterUrls}
120
+ gasless={gasless}
121
+ setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
122
+ quoteProvider={quoteProvider}
123
+ fundMethod={fundMethod}
124
+ onNavigateToMeshConnect={onNavigateToMeshConnect}
125
+ checkoutOnHandlers={checkoutOnHandlers}
126
+ recentTokens={recentTokens}
127
+ onRecentTokenSelect={onRecentTokenSelect}
128
+ onTrackToken={onTrackToken}
129
+ onPoolSelectorStateChange={setIsShowingPoolSelector}
130
+ />
131
+ ) : (
132
+ <PoolWithdraw
133
+ account={account}
134
+ walletClient={walletClient}
135
+ onTransactionStateChange={onTransactionStateChange}
136
+ onError={onError}
137
+ onWaitingForWalletConfirm={onWaitingForWalletConfirm}
138
+ onConfirm={onConfirm}
139
+ onComplete={onComplete}
140
+ onSend={onSend}
141
+ paymasterUrls={paymasterUrls}
142
+ gasless={gasless}
143
+ setWalletConfirmRetryHandler={setWalletConfirmRetryHandler}
144
+ quoteProvider={quoteProvider}
145
+ fundMethod={fundMethod}
146
+ onNavigateToMeshConnect={onNavigateToMeshConnect}
147
+ checkoutOnHandlers={checkoutOnHandlers}
148
+ recentTokens={recentTokens}
149
+ onRecentTokenSelect={onRecentTokenSelect}
150
+ onTrackToken={onTrackToken}
151
+ onPoolSelectorStateChange={setIsShowingPoolSelector}
152
+ />
664
153
  )}
665
154
  </div>
666
155
  )