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,869 +0,0 @@
1
- import { ChevronDown, Loader2, RefreshCcw, TrendingUp } from "lucide-react"
2
- import type React from "react"
3
- import { useCallback, useEffect, useRef, useState } from "react"
4
- import type { Account, WalletClient } from "viem"
5
- import { isAddress } from "viem"
6
- import type { TransactionState } from "../../transactions.js"
7
- import type { OnCompleteProps, Token, TokenInfo } from "../hooks/useSendForm.js"
8
- import { useSendForm } from "../hooks/useSendForm.js"
9
- import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
10
- import { ChainImage } from "./ChainImage.js"
11
- import { TokenImage } from "./TokenImage.js"
12
- import { QuoteDetails } from "./QuoteDetails.js"
13
- import { TruncatedAddress } from "./TruncatedAddress.js"
14
- // import { RefundAddressInput } from "./RefundAddressInput.js"
15
- import { type PrepareSendQuote, TradeType } from "../../prepareSend.js"
16
- import { getChainInfo, getChainColor } from "../../chains.js"
17
- import { formatTvl } from "../../prices.js"
18
- import aaveLogo from "../assets/aave.svg"
19
- import morphoLogo from "../assets/morpho.svg"
20
- import { ScreenHeader } from "./ScreenHeader.js"
21
- import { ErrorDisplay } from "./ErrorDisplay.js"
22
- import { useMode } from "../hooks/useMode.js"
23
- import { getExplorerUrlForAddress } from "../../explorer.js"
24
- import { logger } from "../../logger.js"
25
- import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
26
-
27
- interface PaySendFormProps {
28
- selectedToken: Token
29
- onSend: (amount: string, recipient: string) => void
30
- onBack?: () => void
31
- onConfirm: () => void
32
- onComplete: (result: OnCompleteProps) => void
33
- account: Account
34
- toRecipient?: string
35
- toAmount?: string
36
- toChainId?: number
37
- toToken?: string
38
- toCalldata?: string
39
- walletClient: WalletClient
40
- onTransactionStateChange: (transactionStates: TransactionState[]) => void
41
- onError: (error: Error | string | null) => void
42
- onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
43
- paymasterUrls?: Array<{ chainId: number; url: string }>
44
- gasless?: boolean
45
- setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
46
- quoteProvider?: string
47
- fundMethod?: string
48
- onNavigateToMeshConnect?: (
49
- props: {
50
- toTokenSymbol: string
51
- toTokenAmount: string
52
- toChainId: number
53
- toRecipientAddress: string
54
- },
55
- quote?: PrepareSendQuote | null,
56
- ) => void
57
- onAmountUpdate?: (amount: string) => void
58
- selectedPool?: {
59
- id: string
60
- name: string
61
- protocol: string
62
- chainId: number
63
- apy: number
64
- tvl: number
65
- token: {
66
- symbol: string
67
- name: string
68
- address: string
69
- decimals: number
70
- logoUrl?: string
71
- }
72
- depositAddress: string
73
- isActive: boolean
74
- poolUrl?: string
75
- protocolUrl?: string
76
- } | null
77
- checkoutOnHandlers?: CheckoutOnHandlers
78
- }
79
-
80
- export const PaySendForm: React.FC<PaySendFormProps> = ({
81
- selectedToken,
82
- onSend,
83
- onBack,
84
- onConfirm,
85
- onComplete,
86
- account,
87
- toAmount,
88
- toRecipient,
89
- toChainId,
90
- toToken,
91
- toCalldata,
92
- walletClient,
93
- onTransactionStateChange,
94
- onError,
95
- onWaitingForWalletConfirm,
96
- paymasterUrls,
97
- gasless,
98
- setWalletConfirmRetryHandler,
99
- quoteProvider,
100
- fundMethod,
101
- onNavigateToMeshConnect,
102
- onAmountUpdate,
103
- selectedPool,
104
- checkoutOnHandlers,
105
- }) => {
106
- const { mode } = useMode()
107
- const { isBalanceVisible } = useBalanceVisible()
108
- // const [isRefundAddressOpen, setIsRefundAddressOpen] = useState(false)
109
- // const [refundAddress, setRefundAddress] = useState<string>(account.address)
110
- const [refetchTrigger, setRefetchTrigger] = useState(0)
111
- const {
112
- amount,
113
- amountRaw,
114
- amountUsdDisplay,
115
- balanceUsdDisplay,
116
- chainInfo,
117
- balanceFormatted,
118
- handleRecipientInputChange,
119
- handleSubmit,
120
- isChainDropdownOpen,
121
- isSubmitting,
122
- isLoadingQuote,
123
- isTokenDropdownOpen,
124
- recipient,
125
- recipientInput,
126
- selectedDestinationChain,
127
- selectedDestToken,
128
- setAmount,
129
- setRecipient,
130
- setRecipientInput,
131
- setSelectedDestinationChain,
132
- setSelectedDestToken,
133
- buttonText,
134
- isValidRecipient,
135
- ensAddress,
136
- supportedTokens,
137
- setIsChainDropdownOpen,
138
- setIsTokenDropdownOpen,
139
- toAmountDisplay,
140
- destinationTokenAddress,
141
- supportedChains,
142
- isValidCustomToken,
143
- prepareSendQuote,
144
- quoteError,
145
- quoteErrorPrettified,
146
- isSameTokenWithoutCustomCalldata,
147
- } = useSendForm({
148
- account,
149
- toAmount,
150
- toRecipient,
151
- toChainId,
152
- toToken,
153
- toCalldata,
154
- // refundAddress,
155
- walletClient,
156
- onTransactionStateChange,
157
- onError,
158
- onWaitingForWalletConfirm,
159
- paymasterUrls,
160
- gasless,
161
- onConfirm,
162
- onComplete,
163
- onSend,
164
- selectedToken,
165
- setWalletConfirmRetryHandler,
166
- tradeType: TradeType.EXACT_OUTPUT,
167
- quoteProvider,
168
- fundMethod,
169
- mode,
170
- onNavigateToMeshConnect,
171
- checkoutOnHandlers,
172
- refetchTrigger,
173
- })
174
-
175
- // Handle amount input changes with decimal validation
176
- const handleAmountChange = useCallback(
177
- (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
- },
185
- [setAmount],
186
- )
187
-
188
- // Handle manual quote refetch
189
- const handleRefetchQuote = useCallback(() => {
190
- setRefetchTrigger((prev) => prev + 1)
191
- }, [])
192
-
193
- // Call onAmountUpdate when amountRaw changes
194
- useEffect(() => {
195
- if (onAmountUpdate) {
196
- onAmountUpdate(amountRaw)
197
- }
198
- }, [amountRaw, onAmountUpdate])
199
-
200
- const chainDropdownRef = useRef<HTMLDivElement>(null)
201
- const tokenDropdownRef = useRef<HTMLDivElement>(null)
202
-
203
- useEffect(() => {
204
- const handleClickOutside = (event: MouseEvent) => {
205
- logger.console.log(
206
- "[trails-sdk] click outside handler called, isChainDropdownOpen:",
207
- isChainDropdownOpen,
208
- )
209
- if (
210
- chainDropdownRef.current &&
211
- !chainDropdownRef.current.contains(event.target as Node)
212
- ) {
213
- logger.console.log(
214
- "[trails-sdk] closing chain dropdown from outside click",
215
- )
216
- setIsChainDropdownOpen(false)
217
- }
218
- if (
219
- tokenDropdownRef.current &&
220
- !tokenDropdownRef.current.contains(event.target as Node)
221
- ) {
222
- setIsTokenDropdownOpen(false)
223
- }
224
- }
225
-
226
- if (isChainDropdownOpen || isTokenDropdownOpen) {
227
- document.addEventListener("click", handleClickOutside)
228
- return () => document.removeEventListener("click", handleClickOutside)
229
- }
230
- }, [
231
- setIsChainDropdownOpen,
232
- setIsTokenDropdownOpen,
233
- isChainDropdownOpen,
234
- isTokenDropdownOpen,
235
- ])
236
-
237
- if (!selectedDestinationChain) {
238
- return null
239
- }
240
-
241
- if (!selectedToken) {
242
- return null
243
- }
244
-
245
- return (
246
- <div className="space-y-2">
247
- <ScreenHeader
248
- onBack={onBack}
249
- headerContent={mode === "earn" ? "Earn" : "Send Payment"}
250
- headerContentAlign="left"
251
- showAccountActions={true}
252
- />
253
-
254
- <div className="flex items-center space-x-2 p-4 trails-border-radius-container trails-bg-secondary">
255
- <div className="flex items-start justify-between w-full">
256
- {/* Left side - Chain and Token images with token name */}
257
- <div className="flex items-start space-x-2">
258
- {/* Token Image and Name */}
259
- <div className="flex items-center space-x-2">
260
- <div style={{ width: "32px", height: "32px" }} className="mr-2">
261
- <a
262
- href={getExplorerUrlForAddress({
263
- address: selectedToken.contractAddress,
264
- chainId: selectedToken.chainId,
265
- })}
266
- target="_blank"
267
- rel="noopener noreferrer"
268
- className="cursor-pointer"
269
- >
270
- <TokenImage
271
- symbol={selectedToken.symbol}
272
- imageUrl={selectedToken.imageUrl}
273
- chainId={selectedToken.chainId}
274
- size={32}
275
- />
276
- </a>
277
- </div>
278
- <div className="flex flex-col">
279
- <span className="text-sm font-medium max-w-[135px] truncate text-left text-gray-900 dark:text-white">
280
- {selectedToken.name}
281
- </span>
282
- <span className="text-sm text-gray-500 dark:text-gray-400">
283
- on {chainInfo?.name || "Unknown Chain"}
284
- </span>
285
- </div>
286
- </div>
287
- </div>
288
-
289
- {/* Right side - USD value and amount */}
290
- {fundMethod !== "qr-code" && fundMethod !== "exchange" && (
291
- <div className="text-right">
292
- <div className="text-sm font-medium text-gray-900 dark:text-white">
293
- <span className="text-gray-600 dark:text-gray-400">
294
- Balance:{" "}
295
- </span>
296
- {isBalanceVisible ? balanceUsdDisplay : "••••••"}
297
- </div>
298
- <div className="text-sm text-gray-600 dark:text-gray-400">
299
- {isBalanceVisible
300
- ? `${balanceFormatted} ${selectedToken.symbol}`
301
- : "••••••"}
302
- </div>
303
- </div>
304
- )}
305
- </div>
306
- </div>
307
-
308
- {/* Pool Information - Only show in earn mode when selectedPool is available */}
309
- {mode === "earn" && selectedPool && (
310
- <div className="p-4 trails-border-radius-container trails-bg-secondary">
311
- <div className="flex items-center justify-between mb-2">
312
- <div className="flex items-center space-x-3">
313
- <div style={{ width: "32px", height: "32px" }}>
314
- <a
315
- href={getExplorerUrlForAddress({
316
- address: selectedPool.token.address,
317
- chainId: selectedPool.chainId,
318
- })}
319
- target="_blank"
320
- rel="noopener noreferrer"
321
- className="cursor-pointer"
322
- >
323
- <TokenImage
324
- symbol={selectedPool.token.symbol}
325
- imageUrl={selectedPool.token.logoUrl}
326
- chainId={selectedPool.chainId}
327
- size={32}
328
- />
329
- </a>
330
- </div>
331
- <div>
332
- <h3 className="font-medium text-gray-900 dark:text-white text-sm">
333
- {selectedPool.poolUrl ? (
334
- <a
335
- href={selectedPool.poolUrl}
336
- target="_blank"
337
- rel="noopener noreferrer"
338
- className="hover:underline cursor-pointer"
339
- >
340
- {selectedPool.name}
341
- </a>
342
- ) : (
343
- selectedPool.name
344
- )}
345
- </h3>
346
- <div className="flex items-center space-x-2">
347
- <span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
348
- {selectedPool.protocol === "Aave" && (
349
- <img src={aaveLogo} alt="Aave" className="w-3 h-3 mr-1" />
350
- )}
351
- {selectedPool.protocol === "Morpho" && (
352
- <img
353
- src={morphoLogo}
354
- alt="Morpho"
355
- className="w-3 h-3 mr-1"
356
- />
357
- )}
358
- {selectedPool.protocolUrl ? (
359
- <a
360
- href={selectedPool.protocolUrl}
361
- target="_blank"
362
- rel="noopener noreferrer"
363
- className="hover:underline cursor-pointer"
364
- >
365
- {selectedPool.protocol}
366
- </a>
367
- ) : (
368
- selectedPool.protocol
369
- )}
370
- </span>
371
- <span
372
- className={`px-2 py-0.5 rounded-full text-xs font-medium ${getChainColor(selectedPool.chainId)}`}
373
- >
374
- {getChainInfo(selectedPool.chainId)?.name ||
375
- `Chain ${selectedPool.chainId}`}
376
- </span>
377
- </div>
378
- </div>
379
- </div>
380
- <div className="text-right">
381
- <div className="flex items-center space-x-1 text-green-600 dark:text-green-400">
382
- <TrendingUp className="w-3 h-3" />
383
- <span className="font-semibold text-sm">
384
- {selectedPool.apy.toFixed(1)}%
385
- </span>
386
- </div>
387
- <p className="text-xs text-gray-500 dark:text-gray-400">APY</p>
388
- </div>
389
- </div>
390
- <div className="flex items-center justify-between text-sm">
391
- <div className="flex items-center space-x-1 text-gray-600 dark:text-gray-400">
392
- <span className="text-xs">
393
- TVL: {formatTvl(selectedPool.tvl)}
394
- </span>
395
- </div>
396
- <div className="flex items-center space-x-1">
397
- <span
398
- className={`w-2 h-2 rounded-full ${selectedPool.isActive ? "bg-green-500" : "bg-red-500"}`}
399
- />
400
- <span className="text-xs text-gray-600 dark:text-gray-400">
401
- {selectedPool.isActive ? "Active" : "Inactive"}
402
- </span>
403
- </div>
404
- </div>
405
- </div>
406
- )}
407
-
408
- <form onSubmit={handleSubmit} className="space-y-2">
409
- {/* Chain Selection - More Compact */}
410
- {!toChainId && (
411
- <div className="mb-4">
412
- <label
413
- htmlFor="destination-chain"
414
- className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
415
- >
416
- Destination Chain
417
- </label>
418
- <div className="relative" ref={chainDropdownRef}>
419
- <button
420
- type="button"
421
- onClick={() => setIsChainDropdownOpen(!isChainDropdownOpen)}
422
- className="w-full flex items-center px-4 py-3 border border-solid trails-border-radius-dropdown hover:border-gray-400 cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 trails-dropdown"
423
- >
424
- <ChainImage chainId={selectedDestinationChain.id} size={24} />
425
- <span className="ml-2 flex-1 text-left">
426
- {selectedDestinationChain.name}
427
- </span>
428
- <ChevronDown
429
- className={`h-5 w-5 ${"text-gray-400"} transition-transform ${
430
- isChainDropdownOpen ? "transform rotate-180" : ""
431
- }`}
432
- />
433
- </button>
434
-
435
- {isChainDropdownOpen && (
436
- <div className="absolute z-10 w-full mt-1 border border-solid trails-border-radius-dropdown shadow-lg max-h-60 overflow-y-auto custom-scrollbar trails-dropdown">
437
- {supportedChains.map((chain) => (
438
- <button
439
- key={chain.id}
440
- type="button"
441
- onClick={(e) => {
442
- e.preventDefault()
443
- e.stopPropagation()
444
- setSelectedDestinationChain(chain)
445
- setIsChainDropdownOpen(false)
446
- }}
447
- onMouseDown={(e) => {
448
- e.preventDefault()
449
- e.stopPropagation()
450
- }}
451
- className={`w-full flex items-center px-4 py-3 trails-dropdown-item cursor-pointer ${
452
- selectedDestinationChain.id === chain.id
453
- ? "trails-dropdown-item-selected"
454
- : "hover:trails-dropdown-item"
455
- }`}
456
- >
457
- <ChainImage chainId={chain.id} size={24} />
458
- <span className="ml-2">{chain.name}</span>
459
- {selectedDestinationChain.id === chain.id && (
460
- <span className="ml-auto text-gray-900 dark:text-white">
461
-
462
- </span>
463
- )}
464
- </button>
465
- ))}
466
- </div>
467
- )}
468
- </div>
469
- </div>
470
- )}
471
-
472
- {/* Token Selection - More Compact */}
473
- {!toToken && (
474
- <div className="mb-4">
475
- <label
476
- htmlFor="token"
477
- className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
478
- >
479
- Receive Token
480
- </label>
481
- <div className="relative" ref={tokenDropdownRef}>
482
- <button
483
- type="button"
484
- onClick={() => setIsTokenDropdownOpen(!isTokenDropdownOpen)}
485
- className="w-full flex items-center px-4 py-3 border border-solid trails-border-radius-dropdown hover:border-gray-400 cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 trails-dropdown"
486
- >
487
- <div className="w-5 h-5 rounded-full flex items-center justify-center text-sm bg-gray-100 dark:bg-gray-700">
488
- <TokenImage
489
- symbol={selectedDestToken?.symbol}
490
- imageUrl={selectedDestToken?.imageUrl}
491
- size={24}
492
- />
493
- </div>
494
- <span className="ml-2 flex-1 text-left">
495
- {selectedDestToken?.name} ({selectedDestToken?.symbol})
496
- </span>
497
- <ChevronDown
498
- className={`h-5 w-5 text-gray-400 transition-transform ${
499
- isTokenDropdownOpen ? "transform rotate-180" : ""
500
- }`}
501
- />
502
- </button>
503
-
504
- {isTokenDropdownOpen && (
505
- <div className="absolute z-10 w-full mt-1 border border-solid trails-border-radius-dropdown shadow-lg max-h-60 overflow-y-auto custom-scrollbar trails-dropdown">
506
- {supportedTokens.map((token) => (
507
- <button
508
- key={`${token.contractAddress}-${token.chainId}`}
509
- type="button"
510
- onClick={() => {
511
- setSelectedDestToken(token as TokenInfo)
512
- setIsTokenDropdownOpen(false)
513
- }}
514
- className={`w-full flex items-center px-4 py-3 cursor-pointer trails-dropdown-item ${
515
- selectedDestToken?.symbol === token.symbol
516
- ? "trails-dropdown-item-selected"
517
- : "hover:trails-dropdown-item"
518
- }`}
519
- >
520
- <TokenImage
521
- symbol={token.symbol}
522
- imageUrl={token.imageUrl}
523
- size={24}
524
- />
525
- <span className="ml-2">
526
- {token.name} ({token.symbol})
527
- </span>
528
- {selectedDestToken?.symbol === token.symbol && (
529
- <span className="ml-auto text-gray-900 dark:text-white">
530
-
531
- </span>
532
- )}
533
- </button>
534
- ))}
535
- </div>
536
- )}
537
- </div>
538
- </div>
539
- )}
540
-
541
- {/* Amount Input - More Compact */}
542
- {!toAmount && (
543
- <div className="mb-2">
544
- <label
545
- htmlFor="amount"
546
- className="block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300 text-left"
547
- >
548
- Amount to {mode === "earn" ? "Deposit" : "Receive"}
549
- </label>
550
- <div className="relative trails-border-radius-container">
551
- <input
552
- id="amount"
553
- type="text"
554
- value={amount}
555
- onChange={(e) => handleAmountChange(e.target.value)}
556
- placeholder="0.00"
557
- className="block w-full pl-4 pr-12 py-3 border border-solid trails-border-radius-input focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-lg trails-input"
558
- />
559
- <div className="absolute inset-y-0 right-0 flex items-center pr-4">
560
- <span className="text-gray-400">
561
- {selectedDestToken?.symbol}
562
- </span>
563
- </div>
564
- </div>
565
- {amountUsdDisplay && selectedDestToken?.symbol && (
566
- <div className="h-6 mt-1">
567
- <div className="text-sm text-gray-400 text-left">
568
- ≈ {amountUsdDisplay}
569
- </div>
570
- </div>
571
- )}
572
- </div>
573
- )}
574
-
575
- {/* Receive Section - Similar to FundSendForm */}
576
- {(toAmount || toChainId || toToken) && (
577
- <div className="space-y-1">
578
- <div className="flex items-center justify-between">
579
- <div
580
- className={`text-lg font-semibold text-left ${"text-gray-900 dark:text-white"}`}
581
- >
582
- {mode === "earn" ? "Deposit" : "Receive"}
583
- </div>
584
- <button
585
- type="button"
586
- onClick={handleRefetchQuote}
587
- disabled={
588
- isLoadingQuote ||
589
- !amount ||
590
- !selectedDestToken ||
591
- !selectedDestinationChain ||
592
- !isValidRecipient
593
- }
594
- className={`p-2 rounded-md transition-colors cursor-pointer ${
595
- isLoadingQuote ||
596
- !amount ||
597
- !selectedDestToken ||
598
- !selectedDestinationChain ||
599
- !isValidRecipient
600
- ? "opacity-50 cursor-not-allowed text-gray-400 dark:text-gray-500"
601
- : "text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-700 dark:hover:text-gray-200"
602
- }`}
603
- title="Refetch quote"
604
- >
605
- <RefreshCcw
606
- className={`h-4 w-4 ${isLoadingQuote ? "animate-spin" : ""}`}
607
- />
608
- </button>
609
- </div>
610
-
611
- <div className="p-2">
612
- <div className="flex items-center space-x-3">
613
- <a
614
- href={getExplorerUrlForAddress({
615
- address: destinationTokenAddress || "",
616
- chainId: selectedDestinationChain.id,
617
- })}
618
- target="_blank"
619
- rel="noopener noreferrer"
620
- className="cursor-pointer"
621
- >
622
- <TokenImage
623
- symbol={selectedDestToken?.symbol}
624
- imageUrl={selectedDestToken?.imageUrl}
625
- chainId={selectedDestinationChain.id}
626
- size={32}
627
- />
628
- </a>
629
- <div>
630
- <div className="flex items-center space-x-2">
631
- <div
632
- className={`text-lg font-semibold ${"text-gray-900 dark:text-white"} ${isLoadingQuote ? "animate-pulse" : ""}`}
633
- >
634
- {toAmountDisplay} {selectedDestToken?.symbol}
635
- </div>
636
- {isLoadingQuote && (
637
- <div
638
- className={`animate-spin rounded-full h-4 w-4 border-solid border-b-2 ${"border-blue-400"}`}
639
- />
640
- )}
641
- </div>
642
- <div
643
- className={`text-xs ${"text-gray-500 dark:text-gray-400"} ${isLoadingQuote ? "animate-pulse" : ""}`}
644
- >
645
- ≈ {amountUsdDisplay}{" "}
646
- {selectedDestinationChain
647
- ? `on ${selectedDestinationChain.name}`
648
- : ""}
649
- </div>
650
- </div>
651
- </div>
652
- </div>
653
-
654
- {/* Show recipient address if different from sender */}
655
- {recipient &&
656
- recipient.toLowerCase() !== account.address.toLowerCase() && (
657
- <div className="px-2 pb-1">
658
- <div className={`text-xs text-left ${"text-gray-400"}`}>
659
- {mode === "earn" ? "Pool" : "Recipient"}:{" "}
660
- <TruncatedAddress
661
- address={recipient}
662
- chainId={selectedDestinationChain.id}
663
- />
664
- </div>
665
- </div>
666
- )}
667
- </div>
668
- )}
669
-
670
- {/* Recipient Input - More Compact */}
671
- {!toRecipient && (
672
- <div className="mb-4">
673
- <div className="flex justify-between items-center mb-1">
674
- <div>
675
- <label
676
- htmlFor="recipient"
677
- className="text-sm font-medium text-gray-700 dark:text-gray-300"
678
- >
679
- {toCalldata
680
- ? "Destination Address"
681
- : mode === "earn"
682
- ? "Pool Address"
683
- : "Recipient Address"}
684
- </label>
685
- {recipient &&
686
- isAddress(recipient) &&
687
- recipient.toLowerCase() === account.address.toLowerCase() && (
688
- <div className="text-xs mt-0.5 text-left text-gray-400">
689
- Same as sender
690
- </div>
691
- )}
692
- </div>
693
- <div className="h-7 flex items-center">
694
- {recipient !== account.address ? (
695
- <button
696
- type="button"
697
- onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
698
- event.preventDefault()
699
- setRecipientInput(account.address)
700
- setRecipient(account.address)
701
- }}
702
- className={`px-2 py-1 text-xs cursor-pointer trails-border-radius-button transition-colors bg-blue-500 hover:bg-blue-600 text-white`}
703
- >
704
- Use Account
705
- </button>
706
- ) : null}
707
- </div>
708
- </div>
709
- <input
710
- id="recipient"
711
- type="text"
712
- value={recipientInput}
713
- onChange={handleRecipientInputChange}
714
- placeholder="0x... or name.eth"
715
- className="block w-full px-4 py-3 border border-solid trails-border-radius-input focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm trails-input"
716
- />
717
- {ensAddress && <p className="text-sm text-gray-400">{recipient}</p>}
718
- </div>
719
- )}
720
-
721
- {/* Custom Calldata - More Compact */}
722
- {toCalldata && (
723
- <div className="px-2 py-1">
724
- <p className={`text-[10px] text-left ${"text-gray-400"}`}>
725
- This transaction includes custom calldata for contract interaction
726
- at the destination address
727
- </p>
728
- </div>
729
- )}
730
-
731
- {/* Refund Address Input */}
732
- {/* <RefundAddressInput
733
- account={account}
734
- isOpen={isRefundAddressOpen}
735
- onToggle={() => setIsRefundAddressOpen(!isRefundAddressOpen)}
736
- refundAddress={refundAddress}
737
- onRefundAddressChange={setRefundAddress}
738
- chainId={selectedDestinationChain.id}
739
- /> */}
740
-
741
- {/* Warning Messages - Show only one at a time */}
742
- {isSameTokenWithoutCustomCalldata ? (
743
- <ErrorDisplay
744
- errorPrettified="Cannot swap to the same token on the same chain without custom calldata. Please select a different origin token."
745
- severity="error"
746
- />
747
- ) : (
748
- <ErrorDisplay
749
- errorPrettified={quoteErrorPrettified}
750
- error={quoteError}
751
- severity="warning"
752
- />
753
- )}
754
- {prepareSendQuote?.noSufficientBalance ? (
755
- <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
756
- <div className="flex items-center space-x-2">
757
- <svg
758
- className="w-4 h-4 text-amber-500 flex-shrink-0"
759
- fill="none"
760
- stroke="currentColor"
761
- viewBox="0 0 24 24"
762
- aria-hidden="true"
763
- >
764
- <path
765
- strokeLinecap="round"
766
- strokeLinejoin="round"
767
- strokeWidth={2}
768
- 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"
769
- />
770
- </svg>
771
- <p className="text-sm text-amber-600 dark:text-amber-400">
772
- Insufficient balance to complete this transaction
773
- </p>
774
- </div>
775
- </div>
776
- ) : null}
777
-
778
- <button
779
- type="submit"
780
- disabled={
781
- !amount ||
782
- !isValidRecipient ||
783
- isSubmitting ||
784
- !destinationTokenAddress ||
785
- !isValidCustomToken ||
786
- isLoadingQuote ||
787
- !prepareSendQuote ||
788
- prepareSendQuote?.noSufficientBalance ||
789
- isSameTokenWithoutCustomCalldata
790
- }
791
- 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`}
792
- >
793
- {isSubmitting ? (
794
- <div className="flex items-center justify-center">
795
- <Loader2
796
- className={`w-5 h-5 animate-spin mr-2 ${"text-gray-400"}`}
797
- />
798
- <span>{buttonText}</span>
799
- </div>
800
- ) : isSameTokenWithoutCustomCalldata ? (
801
- "Select Different Tokens"
802
- ) : prepareSendQuote?.noSufficientBalance ? (
803
- "Insufficient Balance"
804
- ) : (
805
- buttonText
806
- )}
807
- </button>
808
-
809
- {/* Quote Details */}
810
- {prepareSendQuote && (
811
- <div className="space-y-2">
812
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
813
- </div>
814
- )}
815
- </form>
816
- </div>
817
- )
818
- }
819
-
820
- const styles = `
821
- select {
822
- appearance: none;
823
- border: 1px solid #e5e7eb;
824
- outline: none;
825
- font-size: 1rem;
826
- width: 100%;
827
- background-color: #fff;
828
- border-radius: 0.5rem;
829
- padding: 0.75rem 1rem;
830
- padding-right: 2rem;
831
-
832
- cursor: pointer;
833
- transition: all 0.2s;
834
- }
835
-
836
- select:hover {
837
- border-color: #d1d5db;
838
- }
839
-
840
- select:focus {
841
- border-color: #3b82f6;
842
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
843
- }
844
-
845
- select option {
846
- padding: 0.75rem 1rem;
847
- min-height: 3rem;
848
- display: flex;
849
- align-items: center;
850
- padding-left: 2.75rem;
851
- position: relative;
852
- cursor: pointer;
853
- }
854
-
855
- select option:hover {
856
- background-color: #f3f4f6;
857
- }
858
-
859
- select option:checked {
860
- background-color: #eff6ff;
861
- color: #1d4ed8;
862
- }
863
- `
864
-
865
- if (typeof document !== "undefined") {
866
- const styleTag = document.createElement("style")
867
- styleTag.textContent = styles
868
- document.head.appendChild(styleTag)
869
- }