0xtrails 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/aave.d.ts +8 -0
  2. package/dist/aave.d.ts.map +1 -1
  3. package/dist/{ccip-BlV1Mry3.js → ccip-CXlshvBY.js} +1 -1
  4. package/dist/config.d.ts +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/constants.d.ts +1 -0
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/error.d.ts +1 -0
  9. package/dist/error.d.ts.map +1 -1
  10. package/dist/estimate.d.ts +52 -0
  11. package/dist/estimate.d.ts.map +1 -1
  12. package/dist/{index-BNWCIGfQ.js → index-_QuyGrjU.js} +72332 -72246
  13. package/dist/index.js +2 -2
  14. package/dist/intents.d.ts +40 -0
  15. package/dist/intents.d.ts.map +1 -1
  16. package/dist/metaTxnMonitor.d.ts +3 -3
  17. package/dist/metaTxnMonitor.d.ts.map +1 -1
  18. package/dist/metaTxns.d.ts +3 -3
  19. package/dist/metaTxns.d.ts.map +1 -1
  20. package/dist/morpho.d.ts +8 -0
  21. package/dist/morpho.d.ts.map +1 -1
  22. package/dist/prepareSend.d.ts +16 -6
  23. package/dist/prepareSend.d.ts.map +1 -1
  24. package/dist/queryParams.d.ts.map +1 -1
  25. package/dist/relayer.d.ts +6 -6
  26. package/dist/relayer.d.ts.map +1 -1
  27. package/dist/sequenceWallet.d.ts +2 -2
  28. package/dist/sequenceWallet.d.ts.map +1 -1
  29. package/dist/tokens.d.ts.map +1 -1
  30. package/dist/wallets.d.ts.map +1 -1
  31. package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
  32. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  33. package/dist/widget/components/ClassicSwap.d.ts +2 -0
  34. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  35. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  36. package/dist/widget/components/ConnectedWallets.d.ts +4 -0
  37. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  38. package/dist/widget/components/Earn.d.ts.map +1 -1
  39. package/dist/widget/components/Fund.d.ts.map +1 -1
  40. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  41. package/dist/widget/components/{FundSendForm.d.ts → FundSwap.d.ts} +11 -5
  42. package/dist/widget/components/FundSwap.d.ts.map +1 -0
  43. package/dist/widget/components/FundingMethodSelectorButton.d.ts +4 -0
  44. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -0
  45. package/dist/widget/components/Modal.d.ts.map +1 -1
  46. package/dist/widget/components/Pay.d.ts.map +1 -1
  47. package/dist/widget/components/PercentageMaxButtons.d.ts +12 -0
  48. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -0
  49. package/dist/widget/components/{PaySendForm.d.ts → PoolDeposit.d.ts} +11 -34
  50. package/dist/widget/components/PoolDeposit.d.ts.map +1 -0
  51. package/dist/widget/components/{SimpleSwap.d.ts → PoolWithdraw.d.ts} +16 -8
  52. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -0
  53. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  54. package/dist/widget/components/Receive.d.ts.map +1 -1
  55. package/dist/widget/components/RecipientSelectorButton.d.ts +4 -0
  56. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -0
  57. package/dist/widget/components/Recipients.d.ts.map +1 -1
  58. package/dist/widget/components/RequiredPropsError.d.ts +8 -0
  59. package/dist/widget/components/RequiredPropsError.d.ts.map +1 -0
  60. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  61. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  62. package/dist/widget/components/Swap.d.ts +1 -0
  63. package/dist/widget/components/Swap.d.ts.map +1 -1
  64. package/dist/widget/components/SwapSettings.d.ts.map +1 -1
  65. package/dist/widget/components/TokenImage.d.ts +1 -0
  66. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  67. package/dist/widget/components/TokenList.d.ts.map +1 -1
  68. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  69. package/dist/widget/components/TokenSelectorButton.d.ts +16 -0
  70. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -0
  71. package/dist/widget/components/UserPreferences.d.ts.map +1 -1
  72. package/dist/widget/components/WaasFeeOptions.d.ts +8 -0
  73. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -0
  74. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  75. package/dist/widget/components/WalletList.d.ts.map +1 -1
  76. package/dist/widget/css/compiled.css +2 -0
  77. package/dist/widget/css/index.css +554 -0
  78. package/dist/widget/hooks/useBack.d.ts +1 -0
  79. package/dist/widget/hooks/useBack.d.ts.map +1 -1
  80. package/dist/widget/hooks/useCheckout.d.ts +1 -1
  81. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  82. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  83. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  84. package/dist/widget/hooks/useDefaultTokenSelection.d.ts +3 -3
  85. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  86. package/dist/widget/hooks/usePayMessage.d.ts.map +1 -1
  87. package/dist/widget/hooks/useSelectedFundMethod.d.ts +12 -0
  88. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -0
  89. package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -1
  90. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  91. package/dist/widget/index.js +1 -1
  92. package/dist/widget/widget.d.ts +4 -4
  93. package/dist/widget/widget.d.ts.map +1 -1
  94. package/package.json +18 -12
  95. package/src/aave.ts +32 -0
  96. package/src/config.ts +12 -4
  97. package/src/constants.ts +2 -0
  98. package/src/error.ts +19 -1
  99. package/src/estimate.ts +416 -5
  100. package/src/intents.ts +161 -11
  101. package/src/metaTxnMonitor.ts +3 -3
  102. package/src/metaTxns.ts +3 -5
  103. package/src/morpho.ts +32 -0
  104. package/src/prepareSend.ts +503 -166
  105. package/src/queryParams.ts +2 -1
  106. package/src/relayer.ts +11 -11
  107. package/src/sequenceWallet.ts +2 -2
  108. package/src/tokens.ts +7 -1
  109. package/src/wallets.ts +8 -0
  110. package/src/widget/compiled.css +2 -2
  111. package/src/widget/components/AccountActionsDropdown.tsx +3 -13
  112. package/src/widget/components/AccountSettings.tsx +6 -24
  113. package/src/widget/components/ClassicSwap.tsx +111 -155
  114. package/src/widget/components/ConnectWallet.tsx +4 -37
  115. package/src/widget/components/ConnectedWallets.tsx +113 -58
  116. package/src/widget/components/Earn.tsx +73 -589
  117. package/src/widget/components/Fund.tsx +31 -82
  118. package/src/widget/components/FundMethods.tsx +82 -159
  119. package/src/widget/components/FundSwap.tsx +52 -0
  120. package/src/widget/components/FundingMethodSelectorButton.tsx +60 -0
  121. package/src/widget/components/Modal.tsx +6 -2
  122. package/src/widget/components/Pay.tsx +183 -208
  123. package/src/widget/components/PercentageMaxButtons.tsx +77 -0
  124. package/src/widget/components/PoolDeposit.tsx +593 -0
  125. package/src/widget/components/PoolWithdraw.tsx +903 -0
  126. package/src/widget/components/QuoteDetails.tsx +22 -8
  127. package/src/widget/components/Receive.tsx +0 -2
  128. package/src/widget/components/RecipientSelectorButton.tsx +42 -0
  129. package/src/widget/components/Recipients.tsx +62 -156
  130. package/src/widget/components/RequiredPropsError.tsx +33 -0
  131. package/src/widget/components/ScreenHeader.tsx +5 -1
  132. package/src/widget/components/SlippageToleranceSettings.tsx +2 -1
  133. package/src/widget/components/Swap.tsx +2 -43
  134. package/src/widget/components/SwapSettings.tsx +2 -14
  135. package/src/widget/components/TokenImage.tsx +21 -4
  136. package/src/widget/components/TokenList.tsx +0 -1
  137. package/src/widget/components/TokenSelector.tsx +1 -0
  138. package/src/widget/components/TokenSelectorButton.tsx +75 -0
  139. package/src/widget/components/UserPreferences.tsx +6 -24
  140. package/src/widget/components/WaasFeeOptions.tsx +331 -0
  141. package/src/widget/components/WalletConfirmation.tsx +55 -3
  142. package/src/widget/components/WalletList.tsx +4 -2
  143. package/src/widget/hooks/useBack.tsx +2 -0
  144. package/src/widget/hooks/useCheckout.ts +36 -20
  145. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  146. package/src/widget/hooks/useDefaultTokenSelection.tsx +104 -28
  147. package/src/widget/hooks/usePayMessage.tsx +86 -11
  148. package/src/widget/hooks/useSelectedFundMethod.tsx +41 -0
  149. package/src/widget/hooks/useSelectedRecipient.tsx +10 -0
  150. package/src/widget/hooks/useSendForm.ts +24 -2
  151. package/src/widget/index.css +27 -0
  152. package/src/widget/widget.tsx +169 -111
  153. package/dist/widget/components/FundSendForm.d.ts.map +0 -1
  154. package/dist/widget/components/PaySendForm.d.ts.map +0 -1
  155. package/dist/widget/components/SimpleSwap.d.ts.map +0 -1
  156. package/dist/widget/hooks/useSwapSettings.d.ts +0 -16
  157. package/dist/widget/hooks/useSwapSettings.d.ts.map +0 -1
  158. package/src/widget/components/FundSendForm.tsx +0 -903
  159. package/src/widget/components/PaySendForm.tsx +0 -869
  160. package/src/widget/components/SimpleSwap.tsx +0 -983
  161. package/src/widget/hooks/useSwapSettings.tsx +0 -100
@@ -0,0 +1,593 @@
1
+ import {
2
+ TrendingUp,
3
+ ArrowDown,
4
+ ChevronRight,
5
+ Search,
6
+ Loader2,
7
+ } from "lucide-react"
8
+ import { useEffect, useState, useRef } from "react"
9
+ import type React from "react"
10
+ import type { Account, WalletClient } from "viem"
11
+ import { zeroAddress } from "viem"
12
+ import type { TransactionState } from "../../transactions.js"
13
+ import type { OnCompleteProps } from "../hooks/useSendForm.js"
14
+ 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"
25
+ import { ChainList } from "./ChainList.js"
26
+ import { QuoteDetails } from "./QuoteDetails.js"
27
+ import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
28
+ import { TokenSelectorButton } from "./TokenSelectorButton.js"
29
+ import { FundingMethodSelectorButton } from "./FundingMethodSelectorButton.js"
30
+ import { formatTvl } from "../../prices.js"
31
+ import { useAmountUsd } from "../hooks/useAmountUsd.js"
32
+ import { getExplorerUrlForAddress } from "../../explorer.js"
33
+ import aaveLogo from "../assets/aave.svg"
34
+ import morphoLogo from "../assets/morpho.svg"
35
+ import type { PrepareSendQuote } from "../../prepareSend.js"
36
+ import type { SupportedToken } from "../../tokens.js"
37
+ import { logger } from "../../logger.js"
38
+
39
+ interface PoolDepositProps {
40
+ account: Account
41
+ walletClient: WalletClient
42
+ onTransactionStateChange: (transactionStates: TransactionState[]) => void
43
+ onError: (error: Error | string | null) => void
44
+ onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
45
+ onConfirm: () => void
46
+ onComplete: (result: OnCompleteProps) => void
47
+ onSend: (amount: string, recipient: string) => void
48
+ paymasterUrls?: Array<{ chainId: number; url: string }>
49
+ gasless?: boolean
50
+ setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
51
+ quoteProvider?: string
52
+ fundMethod?: string
53
+ onNavigateToMeshConnect?: (
54
+ props: {
55
+ toTokenSymbol: string
56
+ toTokenAmount: string
57
+ toChainId: number
58
+ toRecipientAddress: string
59
+ },
60
+ quote?: PrepareSendQuote | null,
61
+ ) => void
62
+ checkoutOnHandlers?: CheckoutOnHandlers
63
+ recentTokens?: SupportedToken[]
64
+ onRecentTokenSelect?: (token: SupportedToken) => void
65
+ onTrackToken?: (token: any) => void
66
+ }
67
+
68
+ export const PoolDeposit: React.FC<PoolDepositProps> = ({
69
+ account,
70
+ walletClient,
71
+ onTransactionStateChange,
72
+ onError,
73
+ onWaitingForWalletConfirm,
74
+ onConfirm,
75
+ onComplete,
76
+ onSend,
77
+ paymasterUrls,
78
+ gasless,
79
+ setWalletConfirmRetryHandler,
80
+ quoteProvider,
81
+ fundMethod,
82
+ onNavigateToMeshConnect,
83
+ checkoutOnHandlers,
84
+ recentTokens,
85
+ onRecentTokenSelect,
86
+ onTrackToken,
87
+ }) => {
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
+ balanceFormatted,
111
+ isLoadingQuote,
112
+ prepareSendQuote,
113
+ setAmount: setSendFormAmount,
114
+ handleSubmit,
115
+ isSubmitting,
116
+ buttonText,
117
+ isValidRecipient,
118
+ } = useSendForm({
119
+ account,
120
+ toAmount: undefined,
121
+ toRecipient: selectedPool?.depositAddress,
122
+ toChainId: selectedPool?.chainId,
123
+ toToken: selectedPool?.token.address,
124
+ toCalldata: undefined,
125
+ walletClient,
126
+ onTransactionStateChange,
127
+ onError,
128
+ onWaitingForWalletConfirm,
129
+ paymasterUrls,
130
+ gasless,
131
+ onConfirm,
132
+ onComplete,
133
+ onSend,
134
+ selectedToken: originToken as any,
135
+ setWalletConfirmRetryHandler,
136
+ tradeType: TradeType.EXACT_INPUT,
137
+ quoteProvider,
138
+ fundMethod,
139
+ mode,
140
+ onNavigateToMeshConnect,
141
+ checkoutOnHandlers,
142
+ })
143
+
144
+ // Calculate USD value using the underlying token (for pool tokens like aBasUSDC)
145
+ const { amountUsdFormatted: underlyingTokenUsdDisplay } = useAmountUsd({
146
+ amount: amount,
147
+ token: originToken?.contractAddress, // Use the origin token's address for USD calculation
148
+ chainId: originToken?.chainId,
149
+ })
150
+
151
+ // Auto-select the highest USD value token as origin token
152
+ useEffect(() => {
153
+ if (
154
+ !originToken &&
155
+ !isLoadingTokens &&
156
+ filteredTokensFormatted?.length > 0
157
+ ) {
158
+ const highestValueToken = filteredTokensFormatted[0] // First token is highest USD value
159
+ if (highestValueToken && Number(highestValueToken.balanceUsd) > 0) {
160
+ const decimals =
161
+ highestValueToken.contractInfo?.decimals ??
162
+ (highestValueToken as any)?.decimals
163
+ setOriginToken({
164
+ ...highestValueToken,
165
+ contractInfo: {
166
+ decimals,
167
+ contractAddress: highestValueToken.contractAddress,
168
+ symbol: highestValueToken.symbol,
169
+ name: highestValueToken.name,
170
+ },
171
+ } as any)
172
+ }
173
+ }
174
+ }, [originToken, isLoadingTokens, filteredTokensFormatted, setOriginToken])
175
+
176
+ // Auto-focus input field on component mount
177
+ useEffect(() => {
178
+ if (inputRef.current) {
179
+ inputRef.current.focus()
180
+ }
181
+ }, [])
182
+
183
+ const handleAmountChange = (value: string) => {
184
+ // Validate decimal places (max 8 decimals)
185
+ const decimalMatch = value.match(/^\d*\.?\d{0,8}$/)
186
+ if (!decimalMatch && value !== "") {
187
+ return // Don't update if more than 8 decimals
188
+ }
189
+ setAmount(value)
190
+ setSendFormAmount(value) // Sync with useSendForm for quote functionality
191
+ }
192
+
193
+ const handlePoolSelect = (pool: any) => {
194
+ logger.console.log("Selected pool:", pool)
195
+ setSelectedPool(pool)
196
+ setShowEarnPools(false)
197
+ }
198
+
199
+ const handleOriginTokenSelect = (token: any) => {
200
+ const formattedToken = {
201
+ ...token,
202
+ decimals: token.contractInfo?.decimals || token.decimals,
203
+ contractInfo: {
204
+ decimals: token.contractInfo?.decimals || token.decimals,
205
+ contractAddress: token.contractAddress,
206
+ symbol: token.symbol,
207
+ name: token.name,
208
+ },
209
+ } as any
210
+ setOriginToken(formattedToken)
211
+ logger.console.log("[trails-sdk] selected origin token", token)
212
+ setShowOriginTokenSelector(false)
213
+ // Track the token selection
214
+ onTrackToken?.(token)
215
+ }
216
+
217
+ // Handle amount selection from percentage buttons
218
+ const handleAmountSelect = (selectedAmount: string) => {
219
+ setAmount(selectedAmount)
220
+ setSendFormAmount(selectedAmount)
221
+ }
222
+
223
+ if (showOriginChainList) {
224
+ return (
225
+ <ChainList
226
+ onBack={() => {
227
+ setShowOriginChainList(false)
228
+ setShowOriginTokenSelector(true)
229
+ }}
230
+ />
231
+ )
232
+ }
233
+
234
+ if (showEarnPools) {
235
+ return (
236
+ <EarnPools
237
+ onBack={() => setShowEarnPools(false)}
238
+ onPoolSelect={handlePoolSelect}
239
+ />
240
+ )
241
+ }
242
+
243
+ if (showOriginTokenSelector) {
244
+ return (
245
+ <div className="space-y-2">
246
+ <TokenSelector
247
+ onTokenSelect={handleOriginTokenSelect}
248
+ onError={onError}
249
+ fundMethod={fundMethod}
250
+ showContinueButton={false}
251
+ compactMode={false}
252
+ recentTokens={recentTokens}
253
+ onRecentTokenSelect={onRecentTokenSelect}
254
+ allSupportedTokens={false}
255
+ chainListScreen={true}
256
+ onNavigateToChainList={() => {
257
+ setShowOriginTokenSelector(false)
258
+ setShowOriginChainList(true)
259
+ }}
260
+ />
261
+ </div>
262
+ )
263
+ }
264
+
265
+ return (
266
+ <div className="space-y-2">
267
+ <div className="space-y-1">
268
+ {/* Origin Token Selection */}
269
+ <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">
270
+ {/* Deposit Label */}
271
+ <div className="flex justify-between items-center mb-2">
272
+ <div className="text-sm font-semibold trails-text-secondary text-left">
273
+ From
274
+ </div>
275
+ <FundingMethodSelectorButton />
276
+ </div>
277
+
278
+ <div className="flex items-center space-x-2">
279
+ {/* Amount Input */}
280
+ <div className="flex-1">
281
+ <button
282
+ type="button"
283
+ className="flex items-center justify-start cursor-text bg-transparent border-none p-0 w-full"
284
+ onClick={() => inputRef.current?.focus()}
285
+ >
286
+ <div className="flex items-center">
287
+ <input
288
+ ref={inputRef}
289
+ type="text"
290
+ value={amount}
291
+ onChange={(e) => handleAmountChange(e.target.value)}
292
+ placeholder="0"
293
+ className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary ${
294
+ isLoadingQuote ? "animate-pulse" : ""
295
+ }`}
296
+ style={{
297
+ fontSize:
298
+ amount.length > 12
299
+ ? "0.875rem"
300
+ : amount.length > 9
301
+ ? "1rem"
302
+ : amount.length > 6
303
+ ? "1.125rem"
304
+ : amount.length > 3
305
+ ? "1.25rem"
306
+ : "1.5rem",
307
+ width: `${Math.max((amount || "0").length, 1)}ch`,
308
+ minWidth: "1ch",
309
+ maxWidth: "270px",
310
+ padding: "0",
311
+ margin: "0",
312
+ transition: "all 0.1s ease-in-out",
313
+ }}
314
+ inputMode="decimal"
315
+ />
316
+ <span
317
+ className="font-bold text-gray-400 dark:text-gray-500"
318
+ style={{
319
+ fontSize:
320
+ amount.length > 12
321
+ ? "0.875rem"
322
+ : amount.length > 9
323
+ ? "1rem"
324
+ : amount.length > 6
325
+ ? "1.125rem"
326
+ : amount.length > 3
327
+ ? "1.25rem"
328
+ : "1.5rem",
329
+ marginLeft: "0.1em",
330
+ padding: "0",
331
+ transition: "all 0.2s ease-in-out",
332
+ }}
333
+ >
334
+ {originToken?.symbol.slice(0, 4) || ""}
335
+ </span>
336
+ {isLoadingQuote && (
337
+ <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
338
+ )}
339
+ </div>
340
+ </button>
341
+ </div>
342
+
343
+ {/* Token Selection Button */}
344
+ <TokenSelectorButton
345
+ token={originToken}
346
+ chainId={originToken?.chainId}
347
+ onSelect={() => setShowOriginTokenSelector(true)}
348
+ />
349
+ </div>
350
+
351
+ {/* Bottom Info Row */}
352
+ <div className="mt-2 flex justify-between items-center">
353
+ {/* USD Amount */}
354
+ <div className="text-xs trails-text-muted">
355
+ {originToken?.symbol && amount ? (
356
+ <>≈ {underlyingTokenUsdDisplay || "$0.00"}</>
357
+ ) : (
358
+ <span>&nbsp;</span>
359
+ )}
360
+ </div>
361
+
362
+ {/* Origin Token Balance and Percentage Buttons */}
363
+ {originToken && balanceFormatted && (
364
+ <div className="flex items-center space-x-2">
365
+ <button
366
+ type="button"
367
+ className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
368
+ onClick={() => handleAmountSelect(balanceFormatted || "0")}
369
+ onKeyDown={(e) => {
370
+ if (e.key === "Enter" || e.key === " ") {
371
+ e.preventDefault()
372
+ handleAmountSelect(balanceFormatted || "0")
373
+ }
374
+ }}
375
+ title="Click to use full balance"
376
+ >
377
+ Balance:{" "}
378
+ {isBalanceVisible ? balanceFormatted || "0.00" : "••••••"}
379
+ </button>
380
+
381
+ {/* Percentage Buttons */}
382
+ <PercentageMaxButtons
383
+ userBalance={balanceFormatted}
384
+ isNativeToken={originToken.contractAddress === zeroAddress}
385
+ gasCostFormatted={prepareSendQuote?.gasCostFormatted}
386
+ chainId={originToken.chainId}
387
+ onAmountSelect={handleAmountSelect}
388
+ className="opacity-100"
389
+ />
390
+ </div>
391
+ )}
392
+ </div>
393
+ </div>
394
+
395
+ {/* Arrow Down Between Sections */}
396
+ <div className="relative">
397
+ <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">
398
+ <ArrowDown
399
+ className="w-5 h-5 text-gray-900 dark:text-white"
400
+ strokeWidth={2.5}
401
+ />
402
+ </div>
403
+ </div>
404
+
405
+ {/* Destination Vault Selection */}
406
+ <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">
407
+ {selectedPool ? (
408
+ <div className="p-3 trails-border-radius-container trails-bg-secondary transition-all overflow-hidden">
409
+ {/* Vault Label */}
410
+ <div className="flex justify-between items-center mb-2">
411
+ <div className="text-sm font-semibold trails-text-secondary text-left">
412
+ Vault
413
+ </div>
414
+ </div>
415
+
416
+ <div className="px-1">
417
+ <div className="flex items-center justify-between">
418
+ <div className="flex items-center space-x-3">
419
+ <div style={{ width: "32px", height: "32px" }}>
420
+ <a
421
+ href={getExplorerUrlForAddress({
422
+ address: selectedPool.token.address,
423
+ chainId: selectedPool.chainId,
424
+ })}
425
+ target="_blank"
426
+ rel="noopener noreferrer"
427
+ className="cursor-pointer"
428
+ >
429
+ <TokenImage
430
+ symbol={selectedPool.token.symbol}
431
+ imageUrl={selectedPool.token.logoUrl}
432
+ chainId={selectedPool.chainId}
433
+ contractAddress={selectedPool.token.address}
434
+ size={32}
435
+ />
436
+ </a>
437
+ </div>
438
+ <div>
439
+ <h3 className="font-medium text-gray-900 dark:text-white text-sm">
440
+ {selectedPool.poolUrl ? (
441
+ <a
442
+ href={selectedPool.poolUrl}
443
+ target="_blank"
444
+ rel="noopener noreferrer"
445
+ className="hover:underline cursor-pointer"
446
+ >
447
+ {selectedPool.name}
448
+ </a>
449
+ ) : (
450
+ selectedPool.name
451
+ )}
452
+ </h3>
453
+ <div className="flex items-center space-x-2">
454
+ <span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
455
+ {selectedPool.protocol === "Aave" && (
456
+ <img
457
+ src={aaveLogo}
458
+ alt="Aave"
459
+ className="w-3 h-3 mr-1"
460
+ />
461
+ )}
462
+ {selectedPool.protocol === "Morpho" && (
463
+ <img
464
+ src={morphoLogo}
465
+ alt="Morpho"
466
+ className="w-3 h-3 mr-1"
467
+ />
468
+ )}
469
+ {selectedPool.protocolUrl ? (
470
+ <a
471
+ href={selectedPool.protocolUrl}
472
+ target="_blank"
473
+ rel="noopener noreferrer"
474
+ className="hover:underline cursor-pointer"
475
+ >
476
+ {selectedPool.protocol}
477
+ </a>
478
+ ) : (
479
+ selectedPool.protocol
480
+ )}
481
+ </span>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ <button
486
+ type="button"
487
+ title="Select Vault"
488
+ onClick={() => setShowEarnPools(true)}
489
+ 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"
490
+ >
491
+ <div>
492
+ <div className="flex items-center justify-end space-x-1 text-green-600 dark:text-green-400 mb-1 whitespace-nowrap">
493
+ <TrendingUp className="w-3 h-3" />
494
+ <span className="font-semibold text-sm">
495
+ {selectedPool.apy.toFixed(1)}% APY
496
+ </span>
497
+ </div>
498
+ <p className="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
499
+ TVL: {formatTvl(selectedPool.tvl)}
500
+ </p>
501
+ </div>
502
+ <ChevronRight className="w-4 h-4 text-gray-400" />
503
+ </button>
504
+ </div>
505
+ </div>
506
+ </div>
507
+ ) : (
508
+ <button
509
+ type="button"
510
+ onClick={() => setShowEarnPools(true)}
511
+ className="w-full py-6 px-4 trails-list-item trails-border-radius-container transition-all duration-200 cursor-pointer"
512
+ >
513
+ <div className="flex items-center justify-between">
514
+ <div className="flex items-center space-x-3 flex-1">
515
+ <div className="w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
516
+ <Search className="w-4 h-4 text-gray-400" />
517
+ </div>
518
+ <div className="text-left flex-1">
519
+ <div className="font-semibold text-gray-900 dark:text-white text-sm">
520
+ Select vault to earn yield with
521
+ </div>
522
+ </div>
523
+ </div>
524
+ <ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
525
+ </div>
526
+ </button>
527
+ )}
528
+ </div>
529
+ </div>
530
+
531
+ {prepareSendQuote?.noSufficientBalance ? (
532
+ <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
533
+ <div className="flex items-center space-x-2">
534
+ <svg
535
+ className="w-4 h-4 text-amber-500 flex-shrink-0"
536
+ fill="none"
537
+ stroke="currentColor"
538
+ viewBox="0 0 24 24"
539
+ aria-hidden="true"
540
+ >
541
+ <path
542
+ strokeLinecap="round"
543
+ strokeLinejoin="round"
544
+ strokeWidth={2}
545
+ 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"
546
+ />
547
+ </svg>
548
+ <p className="text-sm text-amber-600 dark:text-amber-400">
549
+ Insufficient balance to complete this transaction
550
+ </p>
551
+ </div>
552
+ </div>
553
+ ) : null}
554
+
555
+ {/* Quote Details */}
556
+ {prepareSendQuote && (
557
+ <div className="space-y-2">
558
+ <QuoteDetails quote={prepareSendQuote} showContent={true} />
559
+ </div>
560
+ )}
561
+
562
+ <form onSubmit={handleSubmit}>
563
+ <button
564
+ type="submit"
565
+ disabled={
566
+ !amount ||
567
+ !isValidRecipient ||
568
+ isSubmitting ||
569
+ !originToken ||
570
+ !selectedPool ||
571
+ isLoadingQuote ||
572
+ !prepareSendQuote ||
573
+ prepareSendQuote?.noSufficientBalance
574
+ }
575
+ 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`}
576
+ >
577
+ {isSubmitting ? (
578
+ <div className="flex items-center justify-center">
579
+ <Loader2
580
+ className={`w-5 h-5 animate-spin mr-2 ${"text-gray-400"}`}
581
+ />
582
+ <span>{buttonText}</span>
583
+ </div>
584
+ ) : prepareSendQuote?.noSufficientBalance ? (
585
+ "Insufficient Balance"
586
+ ) : (
587
+ buttonText || "Deposit"
588
+ )}
589
+ </button>
590
+ </form>
591
+ </div>
592
+ )
593
+ }