0xtrails 0.2.5 → 0.3.0

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 (267) hide show
  1. package/dist/aave.d.ts +2 -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-CXlshvBY.js → ccip-BMB3uDZt.js} +1 -1
  6. package/dist/config.d.ts +0 -5
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/constants.d.ts +4 -4
  9. package/dist/constants.d.ts.map +1 -1
  10. package/dist/error.d.ts +4 -1
  11. package/dist/error.d.ts.map +1 -1
  12. package/dist/fees.d.ts +19 -0
  13. package/dist/fees.d.ts.map +1 -0
  14. package/dist/{index-_QuyGrjU.js → index-QXPUrZVv.js} +48719 -50852
  15. package/dist/index.d.ts +9 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +811 -784
  18. package/dist/intentReceiptMonitor.d.ts +24 -0
  19. package/dist/intentReceiptMonitor.d.ts.map +1 -0
  20. package/dist/intentReceiptPoller.d.ts +69 -0
  21. package/dist/intentReceiptPoller.d.ts.map +1 -0
  22. package/dist/intents.d.ts +15 -11
  23. package/dist/intents.d.ts.map +1 -1
  24. package/dist/morpho.d.ts +6 -5
  25. package/dist/morpho.d.ts.map +1 -1
  26. package/dist/mutations.d.ts +16 -0
  27. package/dist/mutations.d.ts.map +1 -0
  28. package/dist/preconditions.d.ts +5 -4
  29. package/dist/preconditions.d.ts.map +1 -1
  30. package/dist/prepareSend.d.ts +7 -258
  31. package/dist/prepareSend.d.ts.map +1 -1
  32. package/dist/prices.d.ts +9 -6
  33. package/dist/prices.d.ts.map +1 -1
  34. package/dist/sequenceWallet.d.ts +3 -16
  35. package/dist/sequenceWallet.d.ts.map +1 -1
  36. package/dist/tokenBalances.d.ts +17 -13
  37. package/dist/tokenBalances.d.ts.map +1 -1
  38. package/dist/trails.d.ts +24 -40
  39. package/dist/trails.d.ts.map +1 -1
  40. package/dist/transactionIntent/constants.d.ts +7 -0
  41. package/dist/transactionIntent/constants.d.ts.map +1 -0
  42. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +44 -0
  43. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -0
  44. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +30 -0
  45. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -0
  46. package/dist/transactionIntent/deposits/index.d.ts +4 -0
  47. package/dist/transactionIntent/deposits/index.d.ts.map +1 -0
  48. package/dist/transactionIntent/deposits/standardDeposit.d.ts +30 -0
  49. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -0
  50. package/dist/transactionIntent/execution/index.d.ts +2 -0
  51. package/dist/transactionIntent/execution/index.d.ts.map +1 -0
  52. package/dist/transactionIntent/execution/transactionState.d.ts +5 -0
  53. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -0
  54. package/dist/transactionIntent/handlers/crossChain.d.ts +82 -0
  55. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -0
  56. package/dist/transactionIntent/handlers/index.d.ts +4 -0
  57. package/dist/transactionIntent/handlers/index.d.ts.map +1 -0
  58. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +62 -0
  59. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +1 -0
  60. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +72 -0
  61. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -0
  62. package/dist/transactionIntent/index.d.ts +9 -0
  63. package/dist/transactionIntent/index.d.ts.map +1 -0
  64. package/dist/transactionIntent/quote/feeExtractors.d.ts +17 -0
  65. package/dist/transactionIntent/quote/feeExtractors.d.ts.map +1 -0
  66. package/dist/transactionIntent/quote/index.d.ts +4 -0
  67. package/dist/transactionIntent/quote/index.d.ts.map +1 -0
  68. package/dist/transactionIntent/quote/normalizeQuote.d.ts +34 -0
  69. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -0
  70. package/dist/transactionIntent/quote/quoteHelpers.d.ts +5 -0
  71. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -0
  72. package/dist/transactionIntent/types.d.ts +131 -0
  73. package/dist/transactionIntent/types.d.ts.map +1 -0
  74. package/dist/transactionIntent/utils/balanceChecker.d.ts +18 -0
  75. package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -0
  76. package/dist/transactionIntent/utils/index.d.ts +4 -0
  77. package/dist/transactionIntent/utils/index.d.ts.map +1 -0
  78. package/dist/transactionIntent/utils/lifiHelpers.d.ts +10 -0
  79. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +1 -0
  80. package/dist/transactionIntent/utils/testnetHelpers.d.ts +3 -0
  81. package/dist/transactionIntent/utils/testnetHelpers.d.ts.map +1 -0
  82. package/dist/transactionIntent/validators.d.ts +6 -0
  83. package/dist/transactionIntent/validators.d.ts.map +1 -0
  84. package/dist/transactions.d.ts +6 -3
  85. package/dist/transactions.d.ts.map +1 -1
  86. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  87. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  88. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  89. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  90. package/dist/widget/components/ClassicSwap.d.ts +2 -3
  91. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  92. package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
  93. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  94. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  95. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  96. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  97. package/dist/widget/components/DynamicSizeInputField.d.ts +13 -0
  98. package/dist/widget/components/DynamicSizeInputField.d.ts.map +1 -0
  99. package/dist/widget/components/Earn.d.ts +2 -3
  100. package/dist/widget/components/Earn.d.ts.map +1 -1
  101. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  102. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  103. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  104. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  105. package/dist/widget/components/FeeOptions.d.ts +5 -13
  106. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  107. package/dist/widget/components/Fund.d.ts +2 -3
  108. package/dist/widget/components/Fund.d.ts.map +1 -1
  109. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  110. package/dist/widget/components/FundSwap.d.ts +2 -3
  111. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  112. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  113. package/dist/widget/components/Identicon.d.ts.map +1 -1
  114. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  115. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  116. package/dist/widget/components/Modal.d.ts.map +1 -1
  117. package/dist/widget/components/Pay.d.ts +2 -3
  118. package/dist/widget/components/Pay.d.ts.map +1 -1
  119. package/dist/widget/components/PoolDeposit.d.ts +3 -3
  120. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  121. package/dist/widget/components/PoolWithdraw.d.ts +3 -20
  122. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  123. package/dist/widget/components/QuoteDetails.d.ts +2 -0
  124. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  125. package/dist/widget/components/Receipt.d.ts.map +1 -1
  126. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  127. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  128. package/dist/widget/components/Swap.d.ts +2 -3
  129. package/dist/widget/components/Swap.d.ts.map +1 -1
  130. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  131. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  132. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  133. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  134. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  135. package/dist/widget/components/Tooltip.d.ts +9 -0
  136. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  137. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  138. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  139. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  140. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  141. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  142. package/dist/widget/css/compiled.css +2 -2
  143. package/dist/widget/hooks/useCheckout.d.ts +17 -4
  144. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  145. package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -1
  146. package/dist/widget/hooks/useQuote.d.ts +82 -0
  147. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  148. package/dist/widget/hooks/useSelectedFeeToken.d.ts +1 -0
  149. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +1 -1
  150. package/dist/widget/hooks/useSendForm.d.ts +5 -6
  151. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  152. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  153. package/dist/widget/hooks/useWalletConnectionContext.d.ts +25 -0
  154. package/dist/widget/hooks/useWalletConnectionContext.d.ts.map +1 -0
  155. package/dist/widget/index.js +2 -2
  156. package/dist/widget/widget.d.ts +17 -7
  157. package/dist/widget/widget.d.ts.map +1 -1
  158. package/package.json +19 -21
  159. package/src/aave.ts +54 -1
  160. package/src/abortController.ts +35 -0
  161. package/src/config.ts +57 -58
  162. package/src/constants.ts +11 -9
  163. package/src/error.ts +21 -3
  164. package/src/fees.ts +210 -0
  165. package/src/index.ts +35 -13
  166. package/src/intentReceiptMonitor.ts +102 -0
  167. package/src/intentReceiptPoller.ts +299 -0
  168. package/src/intents.ts +205 -171
  169. package/src/morpho.ts +58 -9
  170. package/src/mutations.ts +129 -0
  171. package/src/preconditions.ts +16 -21
  172. package/src/prepareSend.ts +92 -4699
  173. package/src/prices.ts +26 -22
  174. package/src/relaySdk.ts +2 -2
  175. package/src/sequenceWallet.ts +6 -73
  176. package/src/tokenBalances.ts +175 -69
  177. package/src/trails.ts +230 -722
  178. package/src/transactionIntent/constants.ts +11 -0
  179. package/src/transactionIntent/deposits/depositOrchestrator.ts +210 -0
  180. package/src/transactionIntent/deposits/gaslessDeposit.ts +588 -0
  181. package/src/transactionIntent/deposits/index.ts +3 -0
  182. package/src/transactionIntent/deposits/standardDeposit.ts +379 -0
  183. package/src/transactionIntent/execution/index.ts +1 -0
  184. package/src/transactionIntent/execution/transactionState.ts +35 -0
  185. package/src/transactionIntent/handlers/crossChain.ts +1707 -0
  186. package/src/transactionIntent/handlers/index.ts +3 -0
  187. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +323 -0
  188. package/src/transactionIntent/handlers/sameChainSameToken.ts +712 -0
  189. package/src/transactionIntent/index.ts +9 -0
  190. package/src/transactionIntent/quote/feeExtractors.ts +81 -0
  191. package/src/transactionIntent/quote/index.ts +3 -0
  192. package/src/transactionIntent/quote/normalizeQuote.ts +367 -0
  193. package/src/transactionIntent/quote/quoteHelpers.ts +53 -0
  194. package/src/transactionIntent/types.ts +157 -0
  195. package/src/transactionIntent/utils/balanceChecker.ts +96 -0
  196. package/src/transactionIntent/utils/index.ts +3 -0
  197. package/src/transactionIntent/utils/lifiHelpers.ts +68 -0
  198. package/src/transactionIntent/utils/testnetHelpers.ts +10 -0
  199. package/src/transactionIntent/validators.ts +57 -0
  200. package/src/transactions.ts +98 -71
  201. package/src/widget/compiled.css +2 -2
  202. package/src/widget/components/AccountIntentTransactionHistory.tsx +36 -36
  203. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  204. package/src/widget/components/AccountSettings.tsx +70 -41
  205. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  206. package/src/widget/components/ClassicSwap.tsx +44 -107
  207. package/src/widget/components/ConfigDisplay.tsx +0 -11
  208. package/src/widget/components/ConnectWallet.tsx +4 -1
  209. package/src/widget/components/ConnectedWallets.tsx +51 -25
  210. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  211. package/src/widget/components/DynamicSizeInputField.tsx +109 -0
  212. package/src/widget/components/Earn.tsx +34 -45
  213. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  214. package/src/widget/components/FeeBreakdown.tsx +155 -0
  215. package/src/widget/components/FeeOption.tsx +2 -2
  216. package/src/widget/components/FeeOptions.tsx +151 -112
  217. package/src/widget/components/Fund.tsx +10 -29
  218. package/src/widget/components/FundMethods.tsx +4 -3
  219. package/src/widget/components/FundSwap.tsx +2 -3
  220. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  221. package/src/widget/components/Identicon.tsx +164 -95
  222. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  223. package/src/widget/components/Modal.tsx +0 -12
  224. package/src/widget/components/Pay.tsx +72 -75
  225. package/src/widget/components/PoolDeposit.tsx +221 -242
  226. package/src/widget/components/PoolWithdraw.tsx +347 -469
  227. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  228. package/src/widget/components/QuoteDetails.tsx +906 -484
  229. package/src/widget/components/Receipt.tsx +16 -2
  230. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  231. package/src/widget/components/Recipients.tsx +1 -1
  232. package/src/widget/components/ScreenHeader.tsx +60 -36
  233. package/src/widget/components/Swap.tsx +2 -3
  234. package/src/widget/components/ThemeProvider.tsx +2 -1
  235. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  236. package/src/widget/components/TokenImage.tsx +1 -1
  237. package/src/widget/components/TokenSelector.tsx +62 -53
  238. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  239. package/src/widget/components/Tooltip.tsx +51 -0
  240. package/src/widget/components/TransferPendingVertical.tsx +12 -8
  241. package/src/widget/components/WaasFeeOptions.tsx +139 -4
  242. package/src/widget/components/WalletConfirmation.tsx +23 -13
  243. package/src/widget/components/WalletConnect.tsx +93 -29
  244. package/src/widget/hooks/useAmountUsd.ts +9 -9
  245. package/src/widget/hooks/useCheckout.ts +97 -9
  246. package/src/widget/hooks/useDefaultTokenSelection.tsx +27 -21
  247. package/src/widget/hooks/useQuote.ts +466 -0
  248. package/src/widget/hooks/useSelectedFeeToken.tsx +32 -37
  249. package/src/widget/hooks/useSendForm.ts +45 -51
  250. package/src/widget/hooks/useTokenList.ts +34 -26
  251. package/src/widget/hooks/useWalletConnectionContext.tsx +128 -0
  252. package/src/widget/widget.tsx +365 -390
  253. package/dist/apiClient.d.ts +0 -9
  254. package/dist/apiClient.d.ts.map +0 -1
  255. package/dist/intentEntrypoint.d.ts +0 -114
  256. package/dist/intentEntrypoint.d.ts.map +0 -1
  257. package/dist/metaTxnMonitor.d.ts +0 -15
  258. package/dist/metaTxnMonitor.d.ts.map +0 -1
  259. package/dist/metaTxns.d.ts +0 -11
  260. package/dist/metaTxns.d.ts.map +0 -1
  261. package/dist/relayer.d.ts +0 -43
  262. package/dist/relayer.d.ts.map +0 -1
  263. package/src/apiClient.ts +0 -35
  264. package/src/intentEntrypoint.ts +0 -203
  265. package/src/metaTxnMonitor.ts +0 -171
  266. package/src/metaTxns.ts +0 -45
  267. package/src/relayer.ts +0 -289
@@ -1,19 +1,15 @@
1
1
  import { TrendingUp, ChevronRight, Search, Loader2 } from "lucide-react"
2
2
  import { useEffect, useState, useRef, useMemo } from "react"
3
3
  import type React from "react"
4
- import type { Account, WalletClient } from "viem"
5
- import { createPublicClient, http, parseUnits, formatUnits } from "viem"
4
+ import type { Account, Chain, WalletClient } from "viem"
5
+ import { parseUnits, formatUnits, parseAbi } from "viem"
6
6
  import type { TransactionState } from "../../transactions.js"
7
7
  import type { OnCompleteProps } from "../hooks/useSendForm.js"
8
8
  import type { CheckoutOnHandlers } from "../hooks/useCheckout.js"
9
- import { useSendForm } from "../hooks/useSendForm.js"
10
- import { TradeType } from "../../prepareSend.js"
11
9
  import { useEarnPool } from "../hooks/useEarnPool.js"
12
- import { useMode } from "../hooks/useMode.js"
13
10
  import { useBalanceVisible } from "../hooks/useBalanceVisible.js"
14
11
  import { TokenImage } from "./TokenImage.js"
15
12
  import { EarnPools } from "./EarnPools.js"
16
- import { QuoteDetails } from "./QuoteDetails.js"
17
13
  import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
18
14
  import { formatTvl } from "../../prices.js"
19
15
  import { getExplorerUrlForAddress } from "../../explorer.js"
@@ -21,40 +17,29 @@ import { getChainInfo } from "../../chains.js"
21
17
  import { useAmountUsd } from "../hooks/useAmountUsd.js"
22
18
  import aaveLogo from "../assets/aave.svg"
23
19
  import morphoLogo from "../assets/morpho.svg"
24
- import type { PrepareSendQuote } from "../../prepareSend.js"
25
20
  import type { SupportedToken } from "../../tokens.js"
26
21
  import { logger } from "../../logger.js"
27
22
  import { generateAaveWithdrawCalldata } from "../../aave.js"
28
23
  import { generateMorphoWithdrawCalldata } from "../../morpho.js"
29
24
  import { formatAmount } from "../../tokenBalances.js"
25
+ import { DynamicSizeInputField } from "./DynamicSizeInputField.js"
26
+ import { TokenDisplayNonSelectable } from "./TokenDisplayNonSelectable.js"
27
+ import { estimateGasCostFormatted, estimateGasLimit } from "../../estimate.js"
28
+ import { sendOriginTransaction } from "../../intents.js"
29
+ import { usePublicClient } from "wagmi"
30
+ import { getTransactionStateFromReceipt } from "../../transactionIntent/index.js"
30
31
 
31
32
  interface PoolWithdrawProps {
32
- account: Account
33
- walletClient: WalletClient
33
+ account?: Account
34
+ walletClient?: WalletClient
34
35
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
35
36
  onError: (error: Error | string | null) => void
36
- onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
37
- onConfirm: () => void
38
37
  onComplete: (result: OnCompleteProps) => void
39
- onSend: (amount: string, recipient: string) => void
40
- paymasterUrls?: Array<{ chainId: number; url: string }>
41
- gasless?: boolean
42
- setWalletConfirmRetryHandler: (handler: () => Promise<void>) => void
43
- quoteProvider?: string
44
- fundMethod?: string
45
- onNavigateToMeshConnect?: (
46
- props: {
47
- toTokenSymbol: string
48
- toTokenAmount: string
49
- toChainId: number
50
- toRecipientAddress: string
51
- },
52
- quote?: PrepareSendQuote | null,
53
- ) => void
54
38
  checkoutOnHandlers?: CheckoutOnHandlers
55
39
  recentTokens?: SupportedToken[]
56
40
  onRecentTokenSelect?: (token: SupportedToken) => void
57
41
  onTrackToken?: (token: any) => void
42
+ onPoolSelectorStateChange?: (isShowing: boolean) => void
58
43
  }
59
44
 
60
45
  export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
@@ -62,22 +47,14 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
62
47
  walletClient,
63
48
  onTransactionStateChange,
64
49
  onError,
65
- onWaitingForWalletConfirm,
66
- onConfirm,
67
50
  onComplete,
68
- onSend,
69
- paymasterUrls,
70
- gasless,
71
- setWalletConfirmRetryHandler,
72
- quoteProvider,
73
- fundMethod,
74
- onNavigateToMeshConnect,
75
- checkoutOnHandlers,
51
+ onPoolSelectorStateChange,
76
52
  }) => {
77
- const { mode } = useMode()
78
53
  const { isBalanceVisible } = useBalanceVisible()
79
54
  const { selectedPool, setSelectedPool } = useEarnPool()
55
+ const publicClient = usePublicClient()
80
56
 
57
+ const [amountToWithdraw, setAmountToWithdraw] = useState("")
81
58
  const [showEarnPools, setShowEarnPools] = useState(false)
82
59
  const [amount, setAmount] = useState("")
83
60
  const [aTokenAddress, setATokenAddress] = useState<string | null>(null)
@@ -134,14 +111,8 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
134
111
  )
135
112
  return
136
113
  }
137
-
138
- const publicClient = createPublicClient({
139
- chain,
140
- transport: http(),
141
- })
142
-
143
114
  // Call getReserveAToken on the pool contract to get aToken address
144
- const aTokenAddr = (await publicClient.readContract({
115
+ const aTokenAddr = (await publicClient?.readContract({
145
116
  address: selectedPool.depositAddress as `0x${string}`,
146
117
  abi: [
147
118
  {
@@ -178,7 +149,7 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
178
149
  }
179
150
 
180
151
  fetchATokenAddress()
181
- }, [selectedPool])
152
+ }, [publicClient, selectedPool])
182
153
 
183
154
  // Fetch pool balance (aToken balance for Aave, pool contract balance for Morpho, pool token balance for others)
184
155
  useEffect(() => {
@@ -214,11 +185,6 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
214
185
  return
215
186
  }
216
187
 
217
- const publicClient = createPublicClient({
218
- chain,
219
- transport: http(),
220
- })
221
-
222
188
  // Determine which token address to query
223
189
  let tokenToQuery: string
224
190
  if (selectedPool.protocol === "Aave" && aTokenAddress) {
@@ -238,7 +204,7 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
238
204
  })
239
205
 
240
206
  // Fetch balance using balanceOf
241
- const balance = (await publicClient.readContract({
207
+ const balance = (await publicClient?.readContract({
242
208
  address: tokenToQuery as `0x${string}`,
243
209
  abi: [
244
210
  {
@@ -254,7 +220,7 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
254
220
  })) as bigint
255
221
 
256
222
  // Fetch decimals
257
- const decimals = (await publicClient.readContract({
223
+ const decimals = (await publicClient?.readContract({
258
224
  address: tokenToQuery as `0x${string}`,
259
225
  abi: [
260
226
  {
@@ -297,10 +263,17 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
297
263
  }
298
264
 
299
265
  fetchPoolBalance()
300
- }, [selectedPool, aTokenAddress, account])
266
+ }, [publicClient, selectedPool, aTokenAddress, account])
301
267
 
302
268
  // Generate withdraw calldata dynamically based on user's entered amount
303
269
  const withdrawCalldata = useMemo(() => {
270
+ if (!account?.address) {
271
+ logger.console.log(
272
+ "[pool-withdraw] No account found, skipping calldata generation",
273
+ )
274
+ return undefined
275
+ }
276
+
304
277
  const isAave = selectedPool?.protocol === "Aave"
305
278
  const isMorpho = selectedPool?.protocol === "Morpho"
306
279
 
@@ -330,9 +303,9 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
330
303
  )
331
304
  } else if (isMorpho) {
332
305
  calldata = generateMorphoWithdrawCalldata(
333
- selectedPool.token.address, // underlying asset (e.g., USDC)
334
- amountWei, // actual amount to withdraw in wei
335
- account.address, // recipient (user's wallet)
306
+ amountWei, // amount of underlying assets to withdraw in wei
307
+ account.address, // receiver (user's wallet)
308
+ account.address, // owner (user's wallet - owns the shares)
336
309
  )
337
310
  } else {
338
311
  return undefined
@@ -356,128 +329,7 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
356
329
  )
357
330
  return undefined
358
331
  }
359
- }, [selectedPool, amount, account.address])
360
-
361
- // Convert pool to token format for useSendForm
362
- // For Aave, use the aToken address
363
- // For Morpho, use the pool contract address
364
- // For others, use the underlying token address
365
- const poolToken = useMemo(() => {
366
- if (!selectedPool) {
367
- logger.console.log("[pool-withdraw] No pool selected, poolToken is null")
368
- return null
369
- }
370
-
371
- const isAave = selectedPool.protocol === "Aave"
372
- const isMorpho = selectedPool.protocol === "Morpho"
373
-
374
- let contractAddressToUse: string
375
- if (isAave && aTokenAddress) {
376
- contractAddressToUse = aTokenAddress
377
- } else if (isMorpho) {
378
- contractAddressToUse = selectedPool.depositAddress
379
- } else {
380
- contractAddressToUse = selectedPool.token.address
381
- }
382
-
383
- logger.console.log("[pool-withdraw] Constructing poolToken object:", {
384
- poolName: selectedPool.name,
385
- protocol: selectedPool.protocol,
386
- isAave,
387
- isMorpho,
388
- aTokenAddress,
389
- depositAddress: selectedPool.depositAddress,
390
- underlyingTokenAddress: selectedPool.token.address,
391
- willUseAddress: contractAddressToUse,
392
- })
393
-
394
- const token = {
395
- symbol: selectedPool.token.symbol,
396
- imageUrl: selectedPool.token.logoUrl,
397
- chainId: selectedPool.chainId,
398
- contractAddress: contractAddressToUse,
399
- decimals: selectedPool.token.decimals || 18,
400
- contractInfo: {
401
- decimals: selectedPool.token.decimals || 18,
402
- contractAddress: contractAddressToUse,
403
- symbol: selectedPool.token.symbol,
404
- name: selectedPool.token.name || selectedPool.token.symbol,
405
- },
406
- } as any
407
-
408
- logger.console.log("[pool-withdraw] ✅ Constructed poolToken:", token)
409
-
410
- return token
411
- }, [selectedPool, aTokenAddress])
412
-
413
- // Use useSendForm for quote functionality
414
- // Transaction is always sent TO the pool contract (depositAddress)
415
- // For Aave, withdraw() calldata specifies the recipient (user's address)
416
- // Set up source token (aToken for Aave, pool shares for Morpho)
417
- const finalSelectedToken = poolToken
418
- ? {
419
- chainId: selectedPool?.chainId,
420
- ...poolToken,
421
- }
422
- : null
423
-
424
- // Log what we're passing to useSendForm
425
- useEffect(() => {
426
- logger.console.log("[pool-withdraw] useSendForm configuration:", {
427
- hasAccount: !!account,
428
- accountAddress: account?.address,
429
- toRecipient: selectedPool?.depositAddress,
430
- toChainId: selectedPool?.chainId,
431
- toToken: finalSelectedToken?.contractAddress, // Use aToken/pool address as destination token
432
- hasCalldata: !!withdrawCalldata,
433
- calldataLength: withdrawCalldata?.length,
434
- selectedToken: finalSelectedToken,
435
- selectedTokenChainId: finalSelectedToken?.chainId,
436
- selectedTokenAddress: finalSelectedToken?.contractAddress,
437
- tradeType: "EXACT_INPUT",
438
- amount: amount,
439
- isSameChain: selectedPool?.chainId === finalSelectedToken?.chainId,
440
- willBeSameToken: true, // toToken is same as selectedToken's contractAddress
441
- })
442
- }, [account, selectedPool, withdrawCalldata, finalSelectedToken, amount])
443
-
444
- const {
445
- balanceFormatted: _unusedBalanceFormatted, // Not used, we fetch balance separately
446
- isLoadingQuote,
447
- prepareSendQuote,
448
- setAmount: setSendFormAmount,
449
- handleSubmit,
450
- isSubmitting,
451
- buttonText,
452
- isValidRecipient,
453
- selectedDestToken: returnedDestToken, // Get the destination token set by useSendForm
454
- setSelectedDestinationChain, // Function to set destination chain
455
- supportedChains, // Available chains
456
- } = useSendForm({
457
- account,
458
- toAmount: undefined,
459
- toRecipient: selectedPool?.depositAddress, // Always the pool contract address
460
- toChainId: selectedPool?.chainId,
461
- toToken: finalSelectedToken?.contractAddress, // aToken address (Aave) or pool address (Morpho)
462
- toCalldata: withdrawCalldata, // Aave/Morpho withdraw calldata
463
- walletClient,
464
- onTransactionStateChange,
465
- onError,
466
- onWaitingForWalletConfirm,
467
- paymasterUrls,
468
- gasless,
469
- onConfirm,
470
- onComplete,
471
- onSend,
472
- selectedToken: finalSelectedToken, // Source token (what we're withdrawing from)
473
- setWalletConfirmRetryHandler,
474
- tradeType: TradeType.EXACT_INPUT, // User specifies exact input amount to withdraw
475
- quoteProvider,
476
- fundMethod,
477
- mode,
478
- onNavigateToMeshConnect,
479
- checkoutOnHandlers,
480
- })
332
+ }, [selectedPool, amount, account?.address])
481
333
 
482
334
  // Calculate USD value using the underlying token (for pool tokens like aBasUSDC)
483
335
  const { amountUsdFormatted: underlyingTokenUsdDisplay } = useAmountUsd({
@@ -495,58 +347,92 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
495
347
  })
496
348
  }, [poolBalance, isLoadingBalance])
497
349
 
498
- // Log the destination token returned from useSendForm
350
+ // Auto-focus input field on component mount
499
351
  useEffect(() => {
500
- logger.console.log("[pool-withdraw] Destination token from useSendForm:", {
501
- symbol: returnedDestToken?.symbol,
502
- name: returnedDestToken?.name,
503
- decimals: returnedDestToken?.decimals,
504
- sourceTokenAddress: finalSelectedToken?.contractAddress,
505
- })
506
- }, [returnedDestToken, finalSelectedToken])
352
+ if (inputRef.current) {
353
+ inputRef.current.focus()
354
+ }
355
+ }, [])
507
356
 
508
- // Set destination chain to match the pool's chain (same-chain transaction)
357
+ // Notify parent component about pool selector visibility
509
358
  useEffect(() => {
510
- if (selectedPool && supportedChains && setSelectedDestinationChain) {
511
- const destinationChain = supportedChains.find(
512
- (chain) => chain.id === selectedPool.chainId,
513
- )
359
+ if (onPoolSelectorStateChange) {
360
+ // Hide tabs when showing EarnPools selector (no token selector in withdraw)
361
+ onPoolSelectorStateChange(showEarnPools)
362
+ }
363
+ }, [showEarnPools, onPoolSelectorStateChange])
514
364
 
515
- if (destinationChain) {
516
- logger.console.log("[pool-withdraw] Setting destination chain:", {
517
- chainId: destinationChain.id,
518
- chainName: destinationChain.name,
519
- poolChainId: selectedPool.chainId,
520
- isSameChain: true,
521
- })
522
- setSelectedDestinationChain(destinationChain)
523
- } else {
524
- logger.console.warn(
525
- "[pool-withdraw] Destination chain not found in supported chains:",
526
- {
527
- poolChainId: selectedPool.chainId,
528
- supportedChainIds: supportedChains.map((c) => c.id),
529
- },
365
+ const [gasCostFormatted, setGasCostFormatted] = useState("")
366
+
367
+ // For Aave, check if wallet has enough aTokens in order to withdraw
368
+ const [hasEnoughATokens, setHasEnoughATokens] = useState<boolean | null>(null)
369
+ useEffect(() => {
370
+ if (
371
+ publicClient &&
372
+ selectedPool &&
373
+ selectedPool.protocol === "Aave" &&
374
+ amountToWithdraw
375
+ ) {
376
+ try {
377
+ const checkHasEnoughATokens = async () => {
378
+ const aTokenAddress = selectedPool.token.address
379
+ const decimals = selectedPool.token.decimals || 18
380
+ const aTokenBalance = await publicClient?.readContract({
381
+ address: aTokenAddress as `0x${string}`,
382
+ abi: parseAbi([
383
+ "function balanceOf(address) view returns (uint256)",
384
+ ]),
385
+ functionName: "balanceOf",
386
+ args: [account?.address as `0x${string}`],
387
+ })
388
+ const aTokenBalanceFormatted = formatUnits(aTokenBalance, decimals)
389
+ setHasEnoughATokens(aTokenBalanceFormatted >= amountToWithdraw)
390
+ }
391
+ checkHasEnoughATokens()
392
+ } catch (error) {
393
+ logger.console.error(
394
+ "[pool-withdraw] ❌ Error checking has enough aTokens:",
395
+ error,
530
396
  )
531
397
  }
532
398
  }
533
- }, [selectedPool, supportedChains, setSelectedDestinationChain])
399
+ }, [publicClient, selectedPool, account, amountToWithdraw])
534
400
 
535
- // Auto-focus input field on component mount
401
+ // Estimate gas when pool selected and amount is set
536
402
  useEffect(() => {
537
- if (inputRef.current) {
538
- inputRef.current.focus()
403
+ const estimateGas = async () => {
404
+ if (publicClient && selectedPool && amountToWithdraw) {
405
+ try {
406
+ const gasLimit = await estimateGasLimit(publicClient, {
407
+ account: account?.address as `0x${string}`,
408
+ to: selectedPool.depositAddress as `0x${string}`,
409
+ data: withdrawCalldata as `0x${string}`,
410
+ value: 0n,
411
+ })
412
+ const gasCostFormatted = await estimateGasCostFormatted(
413
+ publicClient,
414
+ gasLimit || 100_000n,
415
+ 18,
416
+ )
417
+ setGasCostFormatted(gasCostFormatted)
418
+ } catch (error) {
419
+ logger.console.error(
420
+ "[pool-withdraw] ❌ Error estimating gas:",
421
+ error,
422
+ )
423
+ }
424
+ } else {
425
+ logger.console.error(
426
+ "[pool-withdraw] ❌ Error estimating gas: no public client or selected pool or amount to withdraw",
427
+ )
428
+ }
539
429
  }
540
- }, [])
430
+ estimateGas()
431
+ }, [publicClient, selectedPool, withdrawCalldata, account, amountToWithdraw])
541
432
 
542
433
  const handleAmountChange = (value: string) => {
543
- // Validate decimal places (max 8 decimals)
544
- const decimalMatch = value.match(/^\d*\.?\d{0,8}$/)
545
- if (!decimalMatch && value !== "") {
546
- return // Don't update if more than 8 decimals
547
- }
548
434
  setAmount(value)
549
- setSendFormAmount(value) // Sync with useSendForm for quote functionality
435
+ setAmountToWithdraw(value) // Sync with useSendForm for quote functionality
550
436
  }
551
437
 
552
438
  const handlePoolSelect = (pool: any) => {
@@ -558,9 +444,71 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
558
444
  // Handle amount selection from percentage buttons
559
445
  const handleAmountSelect = (selectedAmount: string) => {
560
446
  setAmount(selectedAmount)
561
- setSendFormAmount(selectedAmount)
447
+ setAmountToWithdraw(selectedAmount)
448
+ }
449
+
450
+ const [isLoadingWithdraw, setIsLoadingWithdraw] = useState(false)
451
+
452
+ const handleSubmit = async (e: React.FormEvent) => {
453
+ e.preventDefault()
454
+ if (!account || !walletClient) {
455
+ throw new Error("No account or wallet client found")
456
+ }
457
+
458
+ try {
459
+ setIsLoadingWithdraw(true)
460
+
461
+ // Create initial pending state
462
+ const initialState: TransactionState = {
463
+ transactionHash: "", // Will be set after tx is sent
464
+ explorerUrl: "",
465
+ chainId: selectedPool?.chainId as number,
466
+ state: "pending",
467
+ label: "Withdraw",
468
+ }
469
+ onTransactionStateChange([initialState])
470
+
471
+ const txHash = await sendOriginTransaction(account, walletClient, {
472
+ to: selectedPool?.depositAddress as `0x${string}`,
473
+ data: withdrawCalldata as `0x${string}`,
474
+ value: 0n,
475
+ chain: walletClient.chain as Chain,
476
+ })
477
+
478
+ const receipt = await publicClient!.waitForTransactionReceipt({
479
+ hash: txHash,
480
+ })
481
+
482
+ // Convert receipt to TransactionState
483
+ const finalState = getTransactionStateFromReceipt(
484
+ receipt,
485
+ selectedPool?.chainId as number,
486
+ "Withdraw",
487
+ )
488
+
489
+ onTransactionStateChange([finalState])
490
+
491
+ // Navigate to receipt screen
492
+ onComplete({ transactionStates: [finalState] })
493
+
494
+ setIsLoadingWithdraw(false)
495
+ } catch (error) {
496
+ logger.console.error(
497
+ "[pool-withdraw] ❌ Error submitting withdraw:",
498
+ error,
499
+ )
500
+ setIsLoadingWithdraw(false)
501
+ onError(
502
+ error instanceof Error ? error.message : "Failed to withdraw from pool",
503
+ )
504
+ }
562
505
  }
563
506
 
507
+ // Get chain info for selected pool
508
+ const chainInfo = useMemo(() => {
509
+ return selectedPool ? getChainInfo(selectedPool.chainId) : null
510
+ }, [selectedPool])
511
+
564
512
  if (showEarnPools) {
565
513
  return (
566
514
  <EarnPools
@@ -572,273 +520,215 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
572
520
 
573
521
  return (
574
522
  <div className="space-y-2">
575
- <div className="space-y-1">
576
- {/* Pool Selection (Primary Section) */}
577
- <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">
578
- {selectedPool ? (
579
- <div className="p-3 trails-border-radius-container trails-bg-secondary transition-all overflow-hidden">
580
- {/* Vault Label */}
581
- <div className="flex justify-between items-center mb-2">
582
- <div className="text-sm font-semibold trails-text-secondary text-left">
583
- Withdraw From
584
- </div>
523
+ {/* Pool Selection (Primary Section) */}
524
+ <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">
525
+ {selectedPool ? (
526
+ <div className="p-3 trails-border-radius-container trails-bg-secondary transition-all overflow-hidden">
527
+ {/* Vault Label */}
528
+ <div className="flex justify-between items-center mb-2">
529
+ <div className="text-sm font-semibold trails-text-secondary text-left">
530
+ From
585
531
  </div>
532
+ </div>
586
533
 
587
- <div className="px-1">
588
- <div className="flex items-center justify-between">
589
- <div className="flex items-center space-x-3">
590
- <div style={{ width: "32px", height: "32px" }}>
591
- <a
592
- href={getExplorerUrlForAddress({
593
- address: selectedPool.token.address,
594
- chainId: selectedPool.chainId,
595
- })}
596
- target="_blank"
597
- rel="noopener noreferrer"
598
- className="cursor-pointer"
599
- >
600
- <TokenImage
601
- symbol={selectedPool.token.symbol}
602
- imageUrl={selectedPool.token.logoUrl}
603
- chainId={selectedPool.chainId}
604
- contractAddress={selectedPool.token.address}
605
- size={32}
606
- />
607
- </a>
608
- </div>
609
- <div>
610
- <h3 className="font-medium text-gray-900 dark:text-white text-sm">
611
- {selectedPool.poolUrl ? (
534
+ <div className="px-1">
535
+ <div className="flex items-center justify-between">
536
+ <div className="flex items-center space-x-3">
537
+ <div style={{ width: "32px", height: "32px" }}>
538
+ <a
539
+ href={getExplorerUrlForAddress({
540
+ address: selectedPool.token.address,
541
+ chainId: selectedPool.chainId,
542
+ })}
543
+ target="_blank"
544
+ rel="noopener noreferrer"
545
+ className="cursor-pointer"
546
+ >
547
+ <TokenImage
548
+ symbol={selectedPool.token.symbol}
549
+ imageUrl={selectedPool.token.logoUrl}
550
+ chainId={selectedPool.chainId}
551
+ contractAddress={selectedPool.token.address}
552
+ size={32}
553
+ />
554
+ </a>
555
+ </div>
556
+ <div>
557
+ <h3 className="font-medium text-gray-900 dark:text-white text-sm">
558
+ {selectedPool.poolUrl ? (
559
+ <a
560
+ href={selectedPool.poolUrl}
561
+ target="_blank"
562
+ rel="noopener noreferrer"
563
+ className="hover:underline cursor-pointer"
564
+ >
565
+ {selectedPool.name}
566
+ </a>
567
+ ) : (
568
+ selectedPool.name
569
+ )}
570
+ </h3>
571
+ <div className="flex items-center space-x-2">
572
+ <span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
573
+ {selectedPool.protocol === "Aave" && (
574
+ <img
575
+ src={aaveLogo}
576
+ alt="Aave"
577
+ className="w-3 h-3 mr-1"
578
+ />
579
+ )}
580
+ {selectedPool.protocol === "Morpho" && (
581
+ <img
582
+ src={morphoLogo}
583
+ alt="Morpho"
584
+ className="w-3 h-3 mr-1"
585
+ />
586
+ )}
587
+ {selectedPool.protocolUrl ? (
612
588
  <a
613
- href={selectedPool.poolUrl}
589
+ href={selectedPool.protocolUrl}
614
590
  target="_blank"
615
591
  rel="noopener noreferrer"
616
592
  className="hover:underline cursor-pointer"
617
593
  >
618
- {selectedPool.name}
594
+ {selectedPool.protocol}
619
595
  </a>
620
596
  ) : (
621
- selectedPool.name
597
+ selectedPool.protocol
622
598
  )}
623
- </h3>
624
- <div className="flex items-center space-x-2">
625
- <span className="text-xs text-gray-500 dark:text-gray-400 flex items-center">
626
- {selectedPool.protocol === "Aave" && (
627
- <img
628
- src={aaveLogo}
629
- alt="Aave"
630
- className="w-3 h-3 mr-1"
631
- />
632
- )}
633
- {selectedPool.protocol === "Morpho" && (
634
- <img
635
- src={morphoLogo}
636
- alt="Morpho"
637
- className="w-3 h-3 mr-1"
638
- />
639
- )}
640
- {selectedPool.protocolUrl ? (
641
- <a
642
- href={selectedPool.protocolUrl}
643
- target="_blank"
644
- rel="noopener noreferrer"
645
- className="hover:underline cursor-pointer"
646
- >
647
- {selectedPool.protocol}
648
- </a>
649
- ) : (
650
- selectedPool.protocol
651
- )}
652
- </span>
653
- </div>
599
+ </span>
654
600
  </div>
655
601
  </div>
656
- <button
657
- type="button"
658
- title="Select Vault"
659
- onClick={() => setShowEarnPools(true)}
660
- 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"
661
- >
662
- <div>
663
- <div className="flex items-center justify-end space-x-1 text-green-600 dark:text-green-400 mb-1 whitespace-nowrap">
664
- <TrendingUp className="w-3 h-3" />
665
- <span className="font-semibold text-sm">
666
- {selectedPool.apy.toFixed(1)}% APY
667
- </span>
668
- </div>
669
- <p className="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
670
- TVL: {formatTvl(selectedPool.tvl)}
671
- </p>
672
- </div>
673
- <ChevronRight className="w-4 h-4 text-gray-400" />
674
- </button>
675
602
  </div>
603
+ <button
604
+ type="button"
605
+ title="Select Vault"
606
+ onClick={() => setShowEarnPools(true)}
607
+ 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"
608
+ >
609
+ <div>
610
+ <div className="flex items-center justify-end space-x-1 text-green-600 dark:text-green-400 mb-1 whitespace-nowrap">
611
+ <TrendingUp className="w-3 h-3" />
612
+ <span className="font-semibold text-sm">
613
+ {selectedPool.apy.toFixed(1)}% APY
614
+ </span>
615
+ </div>
616
+ <p className="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
617
+ TVL: {formatTvl(selectedPool.tvl)}
618
+ </p>
619
+ </div>
620
+ <ChevronRight className="w-4 h-4 text-gray-400" />
621
+ </button>
676
622
  </div>
677
623
  </div>
678
- ) : (
679
- <button
680
- type="button"
681
- onClick={() => setShowEarnPools(true)}
682
- className="w-full py-6 px-4 trails-list-item trails-border-radius-container transition-all duration-200 cursor-pointer"
683
- >
684
- <div className="flex items-center justify-between">
685
- <div className="flex items-center space-x-3 flex-1">
686
- <div className="w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
687
- <Search className="w-4 h-4 text-gray-400" />
688
- </div>
689
- <div className="text-left flex-1">
690
- <div className="font-semibold text-gray-900 dark:text-white text-sm">
691
- Select vault to withdraw from
692
- </div>
624
+ </div>
625
+ ) : (
626
+ <button
627
+ type="button"
628
+ onClick={() => setShowEarnPools(true)}
629
+ className="w-full py-6 px-4 trails-list-item trails-border-radius-container transition-all duration-200 cursor-pointer"
630
+ >
631
+ <div className="flex items-center justify-between">
632
+ <div className="flex items-center space-x-3 flex-1">
633
+ <div className="w-8 h-8 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
634
+ <Search className="w-4 h-4 text-gray-400" />
635
+ </div>
636
+ <div className="text-left flex-1">
637
+ <div className="font-semibold text-gray-900 dark:text-white text-sm">
638
+ Select vault to withdraw from
693
639
  </div>
694
640
  </div>
695
- <ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
696
641
  </div>
697
- </button>
698
- )}
699
- </div>
642
+ <ChevronRight className="w-4 h-4 trails-text-muted flex-shrink-0" />
643
+ </div>
644
+ </button>
645
+ )}
646
+ </div>
700
647
 
701
- {/* Amount Input Section */}
702
- {selectedPool && (
703
- <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">
704
- {/* Withdraw Label */}
705
- <div className="flex justify-between items-center mb-2">
706
- <div className="text-sm font-semibold trails-text-secondary text-left">
707
- Amount
648
+ {/* Amount Input Section */}
649
+ {selectedPool && (
650
+ <div className="pt-4 pb-4 mb-4 trails-bg-secondary trails-bg-secondary-hover trails-border-radius-container p-3 group transition-all duration-200 border border-transparent focus-within:!bg-white dark:focus-within:!bg-gray-800 trails-focus-border-secondary min-h-[120px] flex flex-col">
651
+ {/* Withdraw Label */}
652
+ <div className="mb-4 flex justify-between items-center">
653
+ <div className="text-sm font-medium trails-text-secondary text-left m-0">
654
+ Amount
655
+ </div>
656
+ </div>
657
+
658
+ <div className="flex items-center space-x-2 flex-1">
659
+ {/* Amount Input */}
660
+ <div className="flex-1">
661
+ <div className="flex items-center space-x-2">
662
+ <DynamicSizeInputField
663
+ ref={inputRef}
664
+ value={amount}
665
+ onChange={(e) => handleAmountChange(e.target.value)}
666
+ isLoading={isLoadingWithdraw}
667
+ variant="default"
668
+ />
708
669
  </div>
709
670
  </div>
710
671
 
711
- <div className="flex items-center space-x-2">
712
- {/* Amount Input */}
713
- <div className="flex-1">
672
+ {/* Token Display (Not Selectable) */}
673
+ <TokenDisplayNonSelectable
674
+ symbol={selectedPool.token.symbol}
675
+ imageUrl={selectedPool.token.logoUrl}
676
+ chainId={selectedPool.chainId}
677
+ contractAddress={selectedPool.token.address}
678
+ chainName={chainInfo?.name}
679
+ />
680
+ </div>
681
+
682
+ {/* Bottom Info Row */}
683
+ <div className="mt-4 flex justify-between items-center">
684
+ {/* USD Amount */}
685
+ <div className="text-xs text-gray-500 dark:text-gray-400">
686
+ {selectedPool && amount ? (
687
+ <>≈ {underlyingTokenUsdDisplay || "$0.00"}</>
688
+ ) : (
689
+ <span>&nbsp;</span>
690
+ )}
691
+ </div>
692
+
693
+ {/* Pool Balance and Percentage Buttons */}
694
+ {poolBalance && (
695
+ <div className="flex items-center space-x-2">
714
696
  <button
715
697
  type="button"
716
- className="flex items-center justify-start cursor-text bg-transparent border-none p-0 w-full"
717
- onClick={() => inputRef.current?.focus()}
698
+ className="text-xs text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 transition-colors bg-transparent border-none p-0"
699
+ onClick={() => handleAmountSelect(poolBalance || "0")}
700
+ onKeyDown={(e) => {
701
+ if (e.key === "Enter" || e.key === " ") {
702
+ e.preventDefault()
703
+ handleAmountSelect(poolBalance || "0")
704
+ }
705
+ }}
706
+ title="Click to withdraw full balance"
718
707
  >
719
- <div className="flex items-center">
720
- <input
721
- ref={inputRef}
722
- type="text"
723
- value={amount}
724
- onChange={(e) => handleAmountChange(e.target.value)}
725
- placeholder="0"
726
- className={`bg-transparent border-none outline-none font-bold text-left trails-text-primary placeholder-trails-text-primary ${
727
- isLoadingQuote ? "animate-pulse" : ""
728
- }`}
729
- style={{
730
- fontSize:
731
- amount.length > 12
732
- ? "0.875rem"
733
- : amount.length > 9
734
- ? "1rem"
735
- : amount.length > 6
736
- ? "1.125rem"
737
- : amount.length > 3
738
- ? "1.25rem"
739
- : "1.5rem",
740
- width: `${Math.max((amount || "0").length, 1)}ch`,
741
- minWidth: "1ch",
742
- maxWidth: "270px",
743
- padding: "0",
744
- margin: "0",
745
- transition: "all 0.1s ease-in-out",
746
- }}
747
- inputMode="decimal"
748
- />
749
- <span
750
- className="font-bold text-gray-400 dark:text-gray-500"
751
- style={{
752
- fontSize:
753
- amount.length > 12
754
- ? "0.875rem"
755
- : amount.length > 9
756
- ? "1rem"
757
- : amount.length > 6
758
- ? "1.125rem"
759
- : amount.length > 3
760
- ? "1.25rem"
761
- : "1.5rem",
762
- marginLeft: "0.1em",
763
- padding: "0",
764
- transition: "all 0.2s ease-in-out",
765
- }}
766
- >
767
- {selectedPool?.token.symbol.slice(0, 4) || ""}
768
- </span>
769
- {isLoadingQuote && (
770
- <div className="ml-2 animate-spin rounded-full h-4 w-4 border-solid border-b-2 trails-primary" />
771
- )}
772
- </div>
708
+ Pool Balance:{" "}
709
+ {isBalanceVisible
710
+ ? isLoadingBalance
711
+ ? "Loading..."
712
+ : poolBalance || "0.00"
713
+ : "••••••"}
773
714
  </button>
774
- </div>
775
715
 
776
- {/* Token Display (Not Selectable) */}
777
- <div className="flex items-center space-x-2 trails-bg-card trails-border-radius-input px-2.5 py-1.5 border trails-border-primary">
778
- <TokenImage
779
- symbol={selectedPool.token.symbol}
780
- imageUrl={selectedPool.token.logoUrl}
716
+ {/* Percentage Buttons */}
717
+ <PercentageMaxButtons
718
+ userBalance={poolBalance || undefined}
719
+ isNativeToken={false} // Pool tokens are never native tokens
720
+ gasCostFormatted={gasCostFormatted}
781
721
  chainId={selectedPool.chainId}
782
- contractAddress={selectedPool.token.address}
783
- size={20}
722
+ onAmountSelect={handleAmountSelect}
723
+ className="opacity-100"
784
724
  />
785
- <span className="font-medium trails-text-primary text-sm">
786
- {selectedPool.token.symbol}
787
- </span>
788
725
  </div>
789
- </div>
790
-
791
- {/* Bottom Info Row */}
792
- <div className="mt-2 flex justify-between items-center">
793
- {/* USD Amount */}
794
- <div className="text-xs trails-text-muted">
795
- {selectedPool && amount ? (
796
- <>≈ {underlyingTokenUsdDisplay || "$0.00"}</>
797
- ) : (
798
- <span>&nbsp;</span>
799
- )}
800
- </div>
801
-
802
- {/* Pool Balance and Percentage Buttons */}
803
- {poolBalance && (
804
- <div className="flex items-center space-x-2">
805
- <button
806
- type="button"
807
- className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
808
- onClick={() => handleAmountSelect(poolBalance || "0")}
809
- onKeyDown={(e) => {
810
- if (e.key === "Enter" || e.key === " ") {
811
- e.preventDefault()
812
- handleAmountSelect(poolBalance || "0")
813
- }
814
- }}
815
- title="Click to withdraw full balance"
816
- >
817
- Pool Balance:{" "}
818
- {isBalanceVisible
819
- ? isLoadingBalance
820
- ? "Loading..."
821
- : poolBalance || "0.00"
822
- : "••••••"}
823
- </button>
824
-
825
- {/* Percentage Buttons */}
826
- <PercentageMaxButtons
827
- userBalance={poolBalance || undefined}
828
- isNativeToken={false} // Pool tokens are never native tokens
829
- gasCostFormatted={prepareSendQuote?.gasCostFormatted}
830
- chainId={selectedPool.chainId}
831
- onAmountSelect={handleAmountSelect}
832
- className="opacity-100"
833
- />
834
- </div>
835
- )}
836
- </div>
726
+ )}
837
727
  </div>
838
- )}
839
- </div>
728
+ </div>
729
+ )}
840
730
 
841
- {prepareSendQuote?.noSufficientBalance ? (
731
+ {hasEnoughATokens === false ? (
842
732
  <div className="px-2 py-3 rounded-lg bg-amber-500/10 border border-solid border-amber-500/30">
843
733
  <div className="flex items-center space-x-2">
844
734
  <svg
@@ -862,35 +752,23 @@ export const PoolWithdraw: React.FC<PoolWithdrawProps> = ({
862
752
  </div>
863
753
  ) : null}
864
754
 
865
- {/* Quote Details */}
866
- {prepareSendQuote && (
867
- <div className="space-y-2">
868
- <QuoteDetails quote={prepareSendQuote} showContent={true} />
869
- </div>
870
- )}
871
-
872
755
  {selectedPool && (
873
756
  <form onSubmit={handleSubmit}>
874
757
  <button
875
758
  type="submit"
876
759
  disabled={
877
- !amount ||
878
- !isValidRecipient ||
879
- isSubmitting ||
880
- isLoadingQuote ||
881
- !prepareSendQuote ||
882
- prepareSendQuote?.noSufficientBalance
760
+ !amount || isLoadingWithdraw || hasEnoughATokens === false
883
761
  }
884
762
  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`}
885
763
  >
886
- {isSubmitting ? (
764
+ {isLoadingWithdraw ? (
887
765
  <div className="flex items-center justify-center">
888
766
  <Loader2
889
767
  className={`w-5 h-5 animate-spin mr-2 ${"text-gray-400"}`}
890
768
  />
891
- <span>{buttonText}</span>
769
+ <span>Processing...</span>
892
770
  </div>
893
- ) : prepareSendQuote?.noSufficientBalance ? (
771
+ ) : hasEnoughATokens === false ? (
894
772
  "Insufficient Balance"
895
773
  ) : (
896
774
  "Withdraw"