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
@@ -8,6 +8,7 @@ import { truncateAddress } from "../../utils.js"
8
8
  import { getExplorerUrlForAddress } from "../../explorer.js"
9
9
  import ChainImage from "../components/ChainImage.js"
10
10
  import TokenImage from "../components/TokenImage.js"
11
+ import { Identicon } from "../components/Identicon.js"
11
12
  import { logger } from "../../logger.js"
12
13
 
13
14
  export interface PayMessagePart {
@@ -85,25 +86,23 @@ export function usePayMessage(): UsePayMessageReturn {
85
86
  })
86
87
 
87
88
  const parsed = useMemo(() => {
88
- // Default message if no payMessage provided
89
- const defaultMessage = appName
90
- ? `Pay {APP_IMAGE} {APP_NAME} {TO_AMOUNT_USD}`
91
- : `Pay {TO_AMOUNT_USD}`
92
-
93
- const template = payMessage || defaultMessage
89
+ // If no payMessage provided, return empty parts (default layout handled separately)
90
+ if (!payMessage) {
91
+ return []
92
+ }
94
93
 
95
94
  // Parse the template into parts
96
95
  const parts: PayMessagePart[] = []
97
96
  const regex = /\{([^}]+)\}/g
98
97
  let lastIndex = 0
99
- let match: RegExpExecArray | null = regex.exec(template)
98
+ let match: RegExpExecArray | null = regex.exec(payMessage)
100
99
 
101
100
  while (match !== null) {
102
101
  // Add text before the placeholder
103
102
  if (match.index > lastIndex) {
104
103
  parts.push({
105
104
  type: "text",
106
- value: template.substring(lastIndex, match.index),
105
+ value: payMessage.substring(lastIndex, match.index),
107
106
  })
108
107
  }
109
108
 
@@ -176,14 +175,14 @@ export function usePayMessage(): UsePayMessageReturn {
176
175
  }
177
176
 
178
177
  lastIndex = regex.lastIndex
179
- match = regex.exec(template)
178
+ match = regex.exec(payMessage)
180
179
  }
181
180
 
182
181
  // Add remaining text
183
- if (lastIndex < template.length) {
182
+ if (lastIndex < payMessage.length) {
184
183
  parts.push({
185
184
  type: "text",
186
- value: template.substring(lastIndex),
185
+ value: payMessage.substring(lastIndex),
187
186
  })
188
187
  }
189
188
 
@@ -206,6 +205,78 @@ export function usePayMessage(): UsePayMessageReturn {
206
205
 
207
206
  // Render the message
208
207
  const message = useMemo(() => {
208
+ // If no payMessage provided, render default layout
209
+ if (!payMessage) {
210
+ return (
211
+ <div className="flex flex-col items-center justify-center px-8 py-4 space-y-4 min-w-full">
212
+ {/* App image above the amount if it exists */}
213
+ {appImageUrl && (
214
+ <img
215
+ src={appImageUrl}
216
+ alt={appName || "App"}
217
+ className="w-12 h-12 rounded-lg"
218
+ onError={(e) => {
219
+ logger.console.error(
220
+ `Error loading app image: ${e}, appImageUrl: ${appImageUrl}`,
221
+ )
222
+ // Hide image on error
223
+ e.currentTarget.style.display = "none"
224
+ }}
225
+ />
226
+ )}
227
+
228
+ {/* Large amount in center */}
229
+ <div className="font-bold text-center">
230
+ {(() => {
231
+ const amountToShow = targetAmountUsdFormatted || toAmount || "0"
232
+ const tokenSymbol = tokenInfo?.symbol || ""
233
+ const displayText = targetAmountUsdFormatted
234
+ ? amountToShow
235
+ : `${amountToShow} ${tokenSymbol}`.trim()
236
+
237
+ // Dynamic font size based on text length
238
+ const textLength = displayText.length
239
+ let fontSize = "text-6xl" // Default large size
240
+
241
+ if (textLength > 12) {
242
+ fontSize = "text-2xl"
243
+ } else if (textLength > 10) {
244
+ fontSize = "text-3xl"
245
+ } else if (textLength > 8) {
246
+ fontSize = "text-4xl"
247
+ } else if (textLength > 6) {
248
+ fontSize = "text-5xl"
249
+ }
250
+
251
+ return <div className={fontSize}>{displayText}</div>
252
+ })()}
253
+ </div>
254
+
255
+ {/* Recipient info below */}
256
+ {toAddress && (
257
+ <div className="flex items-center space-x-2 text-center">
258
+ <span className="text-sm text-gray-600 dark:text-gray-400">
259
+ to
260
+ </span>
261
+ <Identicon value={toAddress} size={20} />
262
+ <a
263
+ href={getExplorerUrlForAddress({
264
+ address: toAddress,
265
+ chainId: toChainId ? Number(toChainId) : 1,
266
+ })}
267
+ target="_blank"
268
+ rel="noopener noreferrer"
269
+ className="text-sm hover:underline cursor-pointer text-blue-600 dark:text-blue-400"
270
+ >
271
+ {truncateAddress(toAddress)}
272
+ </a>
273
+ </div>
274
+ )}
275
+ </div>
276
+ )
277
+ }
278
+
279
+ // Render user-provided message with handlebar interpolation
209
280
  return (
210
281
  <>
211
282
  {parsed.map((part) => {
@@ -354,6 +425,7 @@ export function usePayMessage(): UsePayMessageReturn {
354
425
  </>
355
426
  )
356
427
  }, [
428
+ payMessage,
357
429
  parsed,
358
430
  appName,
359
431
  appUrl,
@@ -361,6 +433,9 @@ export function usePayMessage(): UsePayMessageReturn {
361
433
  appDescription,
362
434
  tokenInfo,
363
435
  toChainId,
436
+ toAddress,
437
+ targetAmountUsdFormatted,
438
+ toAmount,
364
439
  ])
365
440
 
366
441
  return {
@@ -0,0 +1,413 @@
1
+ import { useQuery } from "@tanstack/react-query"
2
+ import { useRef } from "react"
3
+ import type { TransactionReceipt } from "viem"
4
+ import { zeroAddress } from "viem"
5
+ import { useAPIClient } from "../../apiClient.js"
6
+ import { useIndexerGatewayClient } from "../../indexerClient.js"
7
+ import { getTokenBalancesWithPrices } from "../../tokenBalances.js"
8
+ import { logger } from "../../logger.js"
9
+ import { getSequenceEnv } from "../../config.js"
10
+ import { useSupportedTokens } from "../../tokens.js"
11
+ import { useTrailsClient } from "../../trailsClient.js"
12
+ import { getExplorerUrl } from "../../explorer.js"
13
+ import { getFullErrorMessage } from "../../error.js"
14
+ import { prepareSend } from "../../prepareSend.js"
15
+ import { abortControllerRegistry } from "../../abortController.js"
16
+ import { useRelayers } from "../../relayer.js"
17
+ import { TradeType } from "../../prepareSend.js"
18
+ import type { Chain } from "../../chains.js"
19
+ import type { SupportedToken } from "../../tokens.js"
20
+ import type { TransactionState } from "../../transactions.js"
21
+ import type { PrepareSendFees } from "../../prepareSend.js"
22
+ import type { MetaTxnReceipt } from "../../relayer.js"
23
+ import type { RelayerEnv } from "../../relayer.js"
24
+ import type { SequenceEnv } from "../../config.js"
25
+
26
+ export type UseQuoteProps = {
27
+ walletClient?: any // TODO: fix this, has to do with viem/wagmi versions
28
+ fromTokenAddress?: string | null
29
+ fromChainId?: number | null
30
+ toTokenAddress?: string | null
31
+ toChainId?: number | null
32
+ toCalldata?: string | null
33
+ swapAmount?: string | bigint
34
+ toRecipient?: string | null
35
+ tradeType?: TradeType | null
36
+ slippageTolerance?: string | number | null
37
+ onStatusUpdate?: ((transactionStates: TransactionState[]) => void) | null
38
+ quoteProvider?: string | null
39
+ gasless?: boolean
40
+ paymasterUrl?: string
41
+ selectedFeeToken?: {
42
+ tokenAddress: string
43
+ tokenSymbol?: string
44
+ } | null
45
+ abortSignal?: AbortSignal
46
+ }
47
+
48
+ export type SwapReturn = {
49
+ originTransaction: {
50
+ transactionHash?: string | null
51
+ explorerUrl?: string | null
52
+ receipt: TransactionReceipt | MetaTxnReceipt | null
53
+ }
54
+ destinationTransaction: {
55
+ transactionHash?: string | null
56
+ explorerUrl?: string | null
57
+ receipt: MetaTxnReceipt | null
58
+ }
59
+ totalCompletionSeconds?: number
60
+ }
61
+
62
+ export type QuoteProviderInfo = {
63
+ id: string
64
+ name: string
65
+ url: string
66
+ }
67
+
68
+ export type Quote = {
69
+ fromAmount: string
70
+ fromAmountMin: string
71
+ toAmount: string
72
+ toAmountMin: string
73
+ originToken: SupportedToken
74
+ destinationToken: SupportedToken
75
+ originChain: Chain
76
+ destinationChain: Chain
77
+ fees: PrepareSendFees
78
+ slippageTolerance: string
79
+ priceImpact: string
80
+ completionEstimateSeconds: number
81
+ transactionStates?: TransactionState[]
82
+ originTokenRate?: string
83
+ quoteProvider?: QuoteProviderInfo | null
84
+ destinationTokenRate?: string
85
+ fromAmountUsdDisplay?: string
86
+ toAmountUsdDisplay?: string
87
+ gasCostUsd?: number
88
+ gasCostUsdDisplay?: string
89
+ gasCost?: string
90
+ gasCostFormatted?: string
91
+ }
92
+
93
+ export type UseQuoteReturn = {
94
+ quote: Quote | null
95
+ swap: (() => Promise<SwapReturn | null>) | null
96
+ isLoadingQuote: boolean
97
+ quoteError: unknown
98
+ refetchQuote: () => void
99
+ abort: () => void
100
+ }
101
+
102
+ export function useQuote({
103
+ walletClient,
104
+ fromTokenAddress,
105
+ fromChainId,
106
+ toTokenAddress,
107
+ toChainId,
108
+ swapAmount,
109
+ tradeType,
110
+ toRecipient,
111
+ toCalldata,
112
+ slippageTolerance,
113
+ onStatusUpdate,
114
+ quoteProvider,
115
+ gasless,
116
+ paymasterUrl,
117
+ selectedFeeToken,
118
+ relayerEnv,
119
+ nodeGatewayEnv,
120
+ abortSignal: externalAbortSignal,
121
+ }: Partial<
122
+ UseQuoteProps & { relayerEnv?: RelayerEnv; nodeGatewayEnv?: SequenceEnv }
123
+ > = {}): UseQuoteReturn {
124
+ // Set node gateway environment override for this quote session
125
+ if (nodeGatewayEnv) {
126
+ ;(globalThis as any).__testNodeGatewayEnv = nodeGatewayEnv
127
+ }
128
+
129
+ // Create abort controller for this hook instance
130
+ const abortControllerRef = useRef<AbortController>(new AbortController())
131
+
132
+ // Combine external abort signal with internal abort controller
133
+ const combinedAbortSignal = externalAbortSignal
134
+ ? (() => {
135
+ const controller = new AbortController()
136
+
137
+ // Listen to external abort signal
138
+ externalAbortSignal.addEventListener("abort", () => {
139
+ controller.abort()
140
+ abortControllerRef.current?.abort()
141
+ })
142
+
143
+ // Listen to internal abort controller
144
+ abortControllerRef.current?.signal.addEventListener("abort", () => {
145
+ controller.abort()
146
+ })
147
+
148
+ return controller.signal
149
+ })()
150
+ : abortControllerRef.current.signal
151
+
152
+ const apiClient = useAPIClient()
153
+ const trailsClient = useTrailsClient()
154
+ const { getRelayer } = useRelayers({
155
+ env: relayerEnv || (getSequenceEnv() as RelayerEnv),
156
+ })
157
+ const indexerGatewayClient = useIndexerGatewayClient()
158
+
159
+ const { supportedTokens } = useSupportedTokens()
160
+
161
+ const { data, isLoading, error, refetch } = useQuery({
162
+ queryKey: [
163
+ "quote",
164
+ fromTokenAddress,
165
+ fromChainId,
166
+ toTokenAddress,
167
+ toChainId,
168
+ swapAmount?.toString(),
169
+ toRecipient,
170
+ toCalldata,
171
+ tradeType,
172
+ slippageTolerance,
173
+ quoteProvider,
174
+ ],
175
+ queryFn: async () => {
176
+ try {
177
+ // Reset the abort controller for new queries to ensure fresh state
178
+ if (abortControllerRef.current.signal.aborted) {
179
+ logger.console.log(
180
+ "[trails-sdk] Resetting aborted controller for new query",
181
+ )
182
+ abortControllerRef.current = new AbortController()
183
+ }
184
+
185
+ if (
186
+ !walletClient ||
187
+ !apiClient ||
188
+ !trailsClient ||
189
+ !fromTokenAddress ||
190
+ !toTokenAddress ||
191
+ !swapAmount ||
192
+ !toRecipient ||
193
+ !fromChainId ||
194
+ !toChainId ||
195
+ !indexerGatewayClient
196
+ ) {
197
+ return null
198
+ }
199
+
200
+ // Get token balance using async method
201
+ const { balances } = await getTokenBalancesWithPrices({
202
+ account: walletClient.account!.address,
203
+ indexerGatewayClient,
204
+ apiClient,
205
+ })
206
+
207
+ const originTokenBalance = balances.find(
208
+ (b) =>
209
+ b.chainId === fromChainId &&
210
+ (b.contractAddress?.toLowerCase() ===
211
+ fromTokenAddress.toLowerCase() ||
212
+ (!b.contractAddress && fromTokenAddress === zeroAddress)),
213
+ )
214
+
215
+ const originTokenBalanceAmount = originTokenBalance?.balance ?? "0"
216
+ const destinationRelayer = getRelayer(toChainId)
217
+ const originRelayer = getRelayer(fromChainId)
218
+
219
+ // Note: Disable this check for now to allow fetching a quote even when the origin balance is zero
220
+ // if (originTokenBalanceAmount === "0") {
221
+ // return null
222
+ // }
223
+
224
+ // logger.console.log("supportedTokens", supportedTokens)
225
+
226
+ const originToken = supportedTokens?.find(
227
+ (token) =>
228
+ token.contractAddress?.toLowerCase() ===
229
+ fromTokenAddress?.toLowerCase() && token.chainId === fromChainId,
230
+ )
231
+ const destinationToken = supportedTokens?.find(
232
+ (token) =>
233
+ token.contractAddress?.toLowerCase() ===
234
+ toTokenAddress?.toLowerCase() && token.chainId === toChainId,
235
+ )
236
+
237
+ const sourceTokenDecimals = originToken?.decimals
238
+ if (!sourceTokenDecimals) {
239
+ logger.console.error(
240
+ "[trails-sdk] [useQuote] Missing source token decimals:",
241
+ {
242
+ originToken,
243
+ fromTokenAddress,
244
+ fromChainId,
245
+ },
246
+ )
247
+ throw new Error("Source token decimals not found")
248
+ }
249
+ const destinationTokenDecimals = destinationToken?.decimals
250
+ if (!destinationTokenDecimals) {
251
+ logger.console.error(
252
+ "[trails-sdk] Missing destination token decimals:",
253
+ {
254
+ destinationToken,
255
+ toTokenAddress,
256
+ toChainId,
257
+ },
258
+ )
259
+ throw new Error("Destination token decimals not found")
260
+ }
261
+ const destinationTokenSymbol = destinationToken?.symbol ?? ""
262
+ const originTokenSymbol = originToken?.symbol ?? ""
263
+
264
+ const options = {
265
+ account: walletClient.account!,
266
+ originTokenAddress: fromTokenAddress,
267
+ originChainId: fromChainId,
268
+ originTokenBalance: originTokenBalanceAmount,
269
+ destinationChainId: toChainId,
270
+ recipient: toRecipient,
271
+ destinationTokenAddress: toTokenAddress,
272
+ swapAmount: swapAmount.toString(),
273
+ tradeType: tradeType ?? TradeType.EXACT_OUTPUT,
274
+ originTokenSymbol: originTokenSymbol,
275
+ destinationTokenSymbol: destinationTokenSymbol,
276
+ destinationCalldata: toCalldata as string,
277
+ client: walletClient,
278
+ apiClient,
279
+ trailsClient,
280
+ originRelayer,
281
+ destinationRelayer,
282
+ sourceTokenDecimals,
283
+ destinationTokenDecimals,
284
+ fee: "0",
285
+ dryMode: false,
286
+ onTransactionStateChange: onStatusUpdate ?? (() => {}),
287
+ slippageTolerance: slippageTolerance?.toString(),
288
+ quoteProvider: quoteProvider,
289
+ gasless: gasless ?? false,
290
+ paymasterUrl: paymasterUrl,
291
+ selectedFeeToken: selectedFeeToken ?? undefined,
292
+ abortSignal: combinedAbortSignal,
293
+ }
294
+
295
+ logger.console.log("[trails-sdk] options", options)
296
+
297
+ const { quote: prepareSendQuote, send } = await prepareSend(options)
298
+
299
+ const quote = {
300
+ fromAmount: prepareSendQuote.originAmount,
301
+ toAmount: prepareSendQuote.destinationAmount,
302
+ fromAmountMin: prepareSendQuote.originAmountMin,
303
+ toAmountMin: prepareSendQuote.destinationAmountMin,
304
+ originToken: prepareSendQuote.originToken,
305
+ destinationToken: prepareSendQuote.destinationToken,
306
+ originChain: prepareSendQuote.originChain,
307
+ destinationChain: prepareSendQuote.destinationChain,
308
+ fees: prepareSendQuote.fees,
309
+ priceImpact: prepareSendQuote.priceImpact,
310
+ completionEstimateSeconds: prepareSendQuote.completionEstimateSeconds,
311
+ slippageTolerance: prepareSendQuote.slippageTolerance,
312
+ transactionStates: prepareSendQuote.transactionStates,
313
+ originTokenRate: prepareSendQuote.originTokenRate,
314
+ destinationTokenRate: prepareSendQuote.destinationTokenRate,
315
+ quoteProvider: prepareSendQuote.quoteProvider,
316
+ fromAmountUsdDisplay:
317
+ prepareSendQuote.originAmountUsdDisplay ?? undefined,
318
+ toAmountUsdDisplay:
319
+ prepareSendQuote.destinationAmountUsdDisplay ?? undefined,
320
+ gasCostUsd: prepareSendQuote.gasCostUsd ?? undefined,
321
+ gasCostUsdDisplay: prepareSendQuote.gasCostUsdDisplay ?? undefined,
322
+ gasCost: prepareSendQuote.gasCost ?? undefined,
323
+ gasCostFormatted: prepareSendQuote.gasCostFormatted ?? undefined,
324
+ }
325
+
326
+ const swap = async (): Promise<SwapReturn> => {
327
+ const {
328
+ originUserTxReceipt,
329
+ destinationMetaTxnReceipt,
330
+ totalCompletionSeconds,
331
+ } = await send({
332
+ selectedFeeToken: selectedFeeToken ?? undefined,
333
+ })
334
+
335
+ return {
336
+ originTransaction: {
337
+ transactionHash: originUserTxReceipt?.transactionHash,
338
+ explorerUrl: getExplorerUrl({
339
+ txHash: originUserTxReceipt?.transactionHash as string,
340
+ chainId: fromChainId,
341
+ }),
342
+ receipt: originUserTxReceipt,
343
+ },
344
+ destinationTransaction: {
345
+ transactionHash: destinationMetaTxnReceipt?.txnHash,
346
+ explorerUrl: getExplorerUrl({
347
+ txHash: destinationMetaTxnReceipt?.txnHash as string,
348
+ chainId: toChainId,
349
+ }),
350
+ receipt: destinationMetaTxnReceipt,
351
+ },
352
+ totalCompletionSeconds,
353
+ }
354
+ }
355
+
356
+ return {
357
+ quote,
358
+ swap,
359
+ }
360
+ } catch (error) {
361
+ logger.console.error(
362
+ "[trails-sdk] [useQuote] Error getting quote:",
363
+ error,
364
+ )
365
+ throw getFullErrorMessage(error)
366
+ }
367
+ },
368
+ // Prevent unnecessary refetching
369
+ enabled: Boolean(
370
+ walletClient &&
371
+ apiClient &&
372
+ fromTokenAddress &&
373
+ toTokenAddress &&
374
+ swapAmount &&
375
+ toRecipient &&
376
+ fromChainId &&
377
+ toChainId &&
378
+ indexerGatewayClient,
379
+ ),
380
+ staleTime: 30 * 1000, // Consider data fresh for 30 seconds
381
+ refetchOnWindowFocus: false, // Don't refetch when window regains focus
382
+ refetchOnMount: false, // Don't refetch on component remount if data exists
383
+ refetchInterval: false, // Disable automatic polling
384
+ retry: 2, // Limit retry attempts
385
+ refetchOnReconnect: true, // Refetch when network reconnects
386
+ })
387
+
388
+ return {
389
+ quote: data?.quote || null,
390
+ swap: data?.swap || null,
391
+ isLoadingQuote: isLoading,
392
+ quoteError: error,
393
+ refetchQuote: () => refetch(),
394
+ abort: () => {
395
+ logger.console.log("[trails-sdk] useQuote abort() called")
396
+ logger.console.log(
397
+ "[trails-sdk] Active operations before abort:",
398
+ abortControllerRegistry.getAll(),
399
+ )
400
+
401
+ // Abort the internal abort controller
402
+ abortControllerRef.current.abort()
403
+
404
+ // Also abort all active polling operations in prepareSend
405
+ abortControllerRegistry.abortAll()
406
+
407
+ logger.console.log(
408
+ "[trails-sdk] Abort completed, active operations:",
409
+ abortControllerRegistry.getAll(),
410
+ )
411
+ },
412
+ }
413
+ }
@@ -0,0 +1,41 @@
1
+ import { createContext, useContext, useState, type ReactNode } from "react"
2
+
3
+ interface SelectedFundMethodContextType {
4
+ selectedFundMethod: string
5
+ setSelectedFundMethod: (method: string) => void
6
+ }
7
+
8
+ const SelectedFundMethodContext = createContext<
9
+ SelectedFundMethodContextType | undefined
10
+ >(undefined)
11
+
12
+ interface SelectedFundMethodProviderProps {
13
+ children: ReactNode
14
+ }
15
+
16
+ export const SelectedFundMethodProvider = ({
17
+ children,
18
+ }: SelectedFundMethodProviderProps) => {
19
+ const [selectedFundMethod, setSelectedFundMethod] = useState<string>("wallet")
20
+
21
+ const value: SelectedFundMethodContextType = {
22
+ selectedFundMethod,
23
+ setSelectedFundMethod,
24
+ }
25
+
26
+ return (
27
+ <SelectedFundMethodContext.Provider value={value}>
28
+ {children}
29
+ </SelectedFundMethodContext.Provider>
30
+ )
31
+ }
32
+
33
+ export const useSelectedFundMethod = (): SelectedFundMethodContextType => {
34
+ const context = useContext(SelectedFundMethodContext)
35
+ if (context === undefined) {
36
+ throw new Error(
37
+ "useSelectedFundMethod must be used within a SelectedFundMethodProvider",
38
+ )
39
+ }
40
+ return context
41
+ }
@@ -2,8 +2,10 @@ import React, {
2
2
  createContext,
3
3
  useContext,
4
4
  useState,
5
+ useEffect,
5
6
  type ReactNode,
6
7
  } from "react"
8
+ import { useAccount } from "wagmi"
7
9
 
8
10
  interface SelectedRecipientContextType {
9
11
  selectedRecipient: string | null
@@ -21,10 +23,18 @@ interface SelectedRecipientProviderProps {
21
23
  export const SelectedRecipientProvider: React.FC<
22
24
  SelectedRecipientProviderProps
23
25
  > = ({ children }) => {
26
+ const { address } = useAccount()
24
27
  const [selectedRecipient, setSelectedRecipient] = useState<string | null>(
25
28
  null,
26
29
  )
27
30
 
31
+ // Set recipient to address only if address exists and no recipient is selected
32
+ useEffect(() => {
33
+ if (address && !selectedRecipient) {
34
+ setSelectedRecipient(address)
35
+ }
36
+ }, [address, selectedRecipient])
37
+
28
38
  return (
29
39
  <SelectedRecipientContext.Provider
30
40
  value={{