0xtrails 0.13.1 → 0.13.2

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 (229) hide show
  1. package/dist/{ccip-DStzFCYT.js → ccip-CT_An6eM.js} +28 -28
  2. package/dist/chains.d.ts +1 -1
  3. package/dist/chains.d.ts.map +1 -1
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/customTokens.d.ts.map +1 -1
  7. package/dist/{index-HY9_ppit.js → index-RfqL5Foz.js} +56523 -43196
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +383 -332
  11. package/dist/intents.d.ts +8 -2
  12. package/dist/intents.d.ts.map +1 -1
  13. package/dist/keyMachineClient.d.ts +9 -0
  14. package/dist/keyMachineClient.d.ts.map +1 -0
  15. package/dist/keymachine/index.d.ts +14 -0
  16. package/dist/keymachine/index.d.ts.map +1 -0
  17. package/dist/keymachine/key-machine.gen.d.ts +461 -0
  18. package/dist/keymachine/key-machine.gen.d.ts.map +1 -0
  19. package/dist/onramp/MeshConnectFlow.d.ts +18 -0
  20. package/dist/onramp/MeshConnectFlow.d.ts.map +1 -0
  21. package/dist/onramp/MeshConnectIframe.d.ts +13 -0
  22. package/dist/onramp/MeshConnectIframe.d.ts.map +1 -0
  23. package/dist/onramp/SendFromExchangeButton.d.ts +16 -0
  24. package/dist/onramp/SendFromExchangeButton.d.ts.map +1 -0
  25. package/dist/onramp/TrailsOnRampProvider.d.ts +31 -0
  26. package/dist/onramp/TrailsOnRampProvider.d.ts.map +1 -0
  27. package/dist/onramp/index.d.ts +13 -0
  28. package/dist/onramp/index.d.ts.map +1 -0
  29. package/dist/onramp/meshconnect.d.ts +30 -0
  30. package/dist/onramp/meshconnect.d.ts.map +1 -0
  31. package/dist/onramp/trailsOnramp.d.ts +24 -0
  32. package/dist/onramp/trailsOnramp.d.ts.map +1 -0
  33. package/dist/onramp-client/index.d.ts +3 -3
  34. package/dist/onramp-client/index.d.ts.map +1 -1
  35. package/dist/prepareSend.d.ts.map +1 -1
  36. package/dist/query/balance.fetchers.d.ts +27 -0
  37. package/dist/query/balance.fetchers.d.ts.map +1 -1
  38. package/dist/query/balance.hooks.d.ts +19 -0
  39. package/dist/query/balance.hooks.d.ts.map +1 -1
  40. package/dist/query/balance.queries.d.ts +18 -1
  41. package/dist/query/balance.queries.d.ts.map +1 -1
  42. package/dist/query/chains.queries.d.ts.map +1 -1
  43. package/dist/query/meld.fetchers.d.ts +1 -1
  44. package/dist/query/meld.fetchers.d.ts.map +1 -1
  45. package/dist/query/meld.hooks.d.ts +3 -3
  46. package/dist/query/meld.hooks.d.ts.map +1 -1
  47. package/dist/query/meld.queries.d.ts +1 -1
  48. package/dist/query/meld.queries.d.ts.map +1 -1
  49. package/dist/query/tokenList.queries.d.ts +54 -0
  50. package/dist/query/tokenList.queries.d.ts.map +1 -0
  51. package/dist/recover.d.ts +3 -1
  52. package/dist/recover.d.ts.map +1 -1
  53. package/dist/tokens.d.ts +13 -0
  54. package/dist/tokens.d.ts.map +1 -1
  55. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +2 -2
  56. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  57. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +2 -2
  58. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  59. package/dist/transactionIntent/deposits/standardDeposit.d.ts +1 -1
  60. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  61. package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
  62. package/dist/transactionIntent/helpers/transactionStateHelpers.d.ts.map +1 -1
  63. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  64. package/dist/transactionIntent/types.d.ts +1 -1
  65. package/dist/transactionIntent/types.d.ts.map +1 -1
  66. package/dist/transactions.d.ts +3 -0
  67. package/dist/transactions.d.ts.map +1 -1
  68. package/dist/umd/trails.min.js +291 -202
  69. package/dist/walletUtils.d.ts +2 -1
  70. package/dist/walletUtils.d.ts.map +1 -1
  71. package/dist/wallets.d.ts +13 -54
  72. package/dist/wallets.d.ts.map +1 -1
  73. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  74. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  75. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  76. package/dist/widget/components/DirectTransfer.d.ts +1 -1
  77. package/dist/widget/components/DirectTransfer.d.ts.map +1 -1
  78. package/dist/widget/components/Fund.d.ts.map +1 -1
  79. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  80. package/dist/widget/components/MeshExchangeSelection.d.ts +11 -0
  81. package/dist/widget/components/MeshExchangeSelection.d.ts.map +1 -0
  82. package/dist/widget/components/OnrampHistoryRow.d.ts +1 -1
  83. package/dist/widget/components/OnrampHistoryRow.d.ts.map +1 -1
  84. package/dist/widget/components/Pay.d.ts.map +1 -1
  85. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  86. package/dist/widget/components/QRCodeWalletSelect.d.ts +1 -1
  87. package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -1
  88. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  89. package/dist/widget/components/Recipients.d.ts.map +1 -1
  90. package/dist/widget/components/RefundWarning.d.ts.map +1 -1
  91. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  92. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  93. package/dist/widget/components/TransferPendingVertical.d.ts +1 -0
  94. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  95. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  96. package/dist/widget/components/WalletImage.d.ts.map +1 -1
  97. package/dist/widget/components/WalletList.d.ts.map +1 -1
  98. package/dist/widget/components/Withdraw.d.ts.map +1 -1
  99. package/dist/widget/css/compiled.css +1 -1
  100. package/dist/widget/hooks/useAddressWalletIcon.d.ts.map +1 -1
  101. package/dist/widget/hooks/useCustomTokenSearch.d.ts +6 -1
  102. package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -1
  103. package/dist/widget/hooks/useDefaultDestinationToken.d.ts.map +1 -1
  104. package/dist/widget/hooks/useDefaultOriginToken.d.ts.map +1 -1
  105. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts +1 -1
  106. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts.map +1 -1
  107. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  108. package/dist/widget/hooks/useQuote.d.ts +1 -1
  109. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  110. package/dist/widget/hooks/useSelectedFundMethod.d.ts +7 -0
  111. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -1
  112. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  113. package/dist/widget/hooks/useTokenList.d.ts +7 -1
  114. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  115. package/dist/widget/hooks/useViewManager.d.ts +1 -1
  116. package/dist/widget/hooks/useViewManager.d.ts.map +1 -1
  117. package/dist/widget/index.js +1 -1
  118. package/dist/widget/providers/TrailsProvider.d.ts +2 -0
  119. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  120. package/dist/widget/utils/createWagmiConfig.d.ts +2 -2
  121. package/dist/widget/utils/createWagmiConfig.d.ts.map +1 -1
  122. package/dist/widget/utils/fundMethodSwitchState.d.ts +1 -0
  123. package/dist/widget/utils/fundMethodSwitchState.d.ts.map +1 -1
  124. package/dist/widget/utils/meldProviderUtils.d.ts +1 -1
  125. package/dist/widget/utils/meldProviderUtils.d.ts.map +1 -1
  126. package/dist/widget/utils/meshSupportedTokens.d.ts +4 -0
  127. package/dist/widget/utils/meshSupportedTokens.d.ts.map +1 -0
  128. package/dist/widget/utils/onrampConfig.d.ts +11 -0
  129. package/dist/widget/utils/onrampConfig.d.ts.map +1 -0
  130. package/dist/widget/utils/trailsOnrampConfig.d.ts +18 -0
  131. package/dist/widget/utils/trailsOnrampConfig.d.ts.map +1 -0
  132. package/dist/widget/widget.d.ts +24 -8
  133. package/dist/widget/widget.d.ts.map +1 -1
  134. package/package.json +9 -7
  135. package/src/chains.ts +9 -4
  136. package/src/constants.ts +2 -0
  137. package/src/customTokens.ts +21 -0
  138. package/src/index.ts +1 -0
  139. package/src/intents.ts +49 -41
  140. package/src/keyMachineClient.ts +29 -0
  141. package/src/keymachine/index.ts +175 -0
  142. package/src/keymachine/key-machine.gen.ts +993 -0
  143. package/src/onramp/MeshConnectFlow.tsx +86 -0
  144. package/src/onramp/MeshConnectIframe.tsx +661 -0
  145. package/src/onramp/SendFromExchangeButton.tsx +81 -0
  146. package/src/onramp/TrailsOnRampProvider.tsx +59 -0
  147. package/src/onramp/index.ts +31 -0
  148. package/src/onramp/meshconnect.ts +277 -0
  149. package/src/onramp/trailsOnramp.tsx +130 -0
  150. package/src/onramp-client/index.ts +4 -6
  151. package/src/prepareSend.ts +45 -44
  152. package/src/query/balance.fetchers.ts +134 -4
  153. package/src/query/balance.hooks.ts +61 -2
  154. package/src/query/balance.queries.ts +63 -0
  155. package/src/query/chains.queries.ts +1 -6
  156. package/src/query/meld.fetchers.ts +1 -1
  157. package/src/query/meld.hooks.ts +1 -1
  158. package/src/query/meld.queries.ts +1 -1
  159. package/src/query/tokenList.queries.ts +118 -0
  160. package/src/recover.ts +86 -23
  161. package/src/tokens.ts +108 -26
  162. package/src/transactionIntent/deposits/depositOrchestrator.ts +11 -11
  163. package/src/transactionIntent/deposits/gaslessDeposit.ts +38 -38
  164. package/src/transactionIntent/deposits/standardDeposit.ts +0 -4
  165. package/src/transactionIntent/handlers/intentHandler.ts +28 -11
  166. package/src/transactionIntent/helpers/transactionStateHelpers.ts +5 -2
  167. package/src/transactionIntent/quote/normalizeQuote.ts +11 -5
  168. package/src/transactionIntent/types.ts +1 -1
  169. package/src/transactions.ts +3 -0
  170. package/src/walletUtils.ts +2 -1
  171. package/src/wallets.ts +184 -380
  172. package/src/widget/compiled.css +1 -1
  173. package/src/widget/components/ClassicSwap.tsx +22 -5
  174. package/src/widget/components/ConnectWallet.tsx +4 -2
  175. package/src/widget/components/ConnectedWallets.tsx +2 -5
  176. package/src/widget/components/DirectTransfer.tsx +5 -2
  177. package/src/widget/components/Fund.tsx +144 -12
  178. package/src/widget/components/FundMethods.tsx +5 -9
  179. package/src/widget/components/MeldHistory.tsx +1 -1
  180. package/src/widget/components/MeshExchangeSelection.tsx +218 -0
  181. package/src/widget/components/OnrampHistoryRow.tsx +1 -1
  182. package/src/widget/components/Pay.tsx +20 -36
  183. package/src/widget/components/PoolDeposit.tsx +13 -22
  184. package/src/widget/components/QRCodeWalletSelect.tsx +5 -2
  185. package/src/widget/components/QuoteDetails.tsx +77 -68
  186. package/src/widget/components/Recipients.tsx +2 -1
  187. package/src/widget/components/RefundWarning.tsx +5 -10
  188. package/src/widget/components/TokenSelector.tsx +85 -16
  189. package/src/widget/components/TransactionDetails.tsx +46 -0
  190. package/src/widget/components/TransferPendingVertical.tsx +27 -19
  191. package/src/widget/components/WalletConnect.tsx +2 -5
  192. package/src/widget/components/WalletImage.tsx +6 -18
  193. package/src/widget/components/WalletList.tsx +1 -1
  194. package/src/widget/components/Withdraw.tsx +22 -4
  195. package/src/widget/hooks/useAddressWalletIcon.ts +2 -1
  196. package/src/widget/hooks/useCustomTokenSearch.tsx +63 -33
  197. package/src/widget/hooks/useDefaultDestinationToken.tsx +2 -5
  198. package/src/widget/hooks/useDefaultOriginToken.tsx +2 -5
  199. package/src/widget/hooks/useFiatOnRampCurrencies.ts +1 -1
  200. package/src/widget/hooks/useIntentTransactionHistory.ts +5 -0
  201. package/src/widget/hooks/useMeldTransactionStatus.ts +1 -1
  202. package/src/widget/hooks/useQuote.ts +51 -45
  203. package/src/widget/hooks/useSelectedFundMethod.tsx +14 -1
  204. package/src/widget/hooks/useSendForm.ts +26 -10
  205. package/src/widget/hooks/useTokenList.ts +208 -139
  206. package/src/widget/hooks/useViewManager.tsx +1 -0
  207. package/src/widget/providers/TrailsProvider.tsx +5 -0
  208. package/src/widget/styles.ts +1 -1
  209. package/src/widget/utils/createWagmiConfig.ts +7 -2
  210. package/src/widget/utils/fundMethodSwitchState.ts +2 -0
  211. package/src/widget/utils/meldProviderUtils.ts +8 -2
  212. package/src/widget/utils/meshSupportedTokens.ts +28 -0
  213. package/src/widget/utils/onrampConfig.ts +15 -0
  214. package/src/widget/utils/trailsOnrampConfig.ts +39 -0
  215. package/src/widget/widget.tsx +164 -98
  216. package/dist/onramp-client/trails-onramp.gen.d.ts +0 -570
  217. package/dist/onramp-client/trails-onramp.gen.d.ts.map +0 -1
  218. package/dist/widget/hooks/useCustomTokenFetch.d.ts +0 -19
  219. package/dist/widget/hooks/useCustomTokenFetch.d.ts.map +0 -1
  220. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts +0 -18
  221. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts.map +0 -1
  222. package/src/onramp-client/trails-onramp.gen.ts +0 -1320
  223. package/src/widget/assets/Binance_Icon_Logo.svg +0 -14
  224. package/src/widget/assets/Bitfinex_Icon_Logo.svg +0 -5
  225. package/src/widget/assets/Coinbase_Icon_Logo.svg +0 -1
  226. package/src/widget/assets/WalletConnect-logo-blue-bg.svg +0 -11
  227. package/src/widget/assets/sequence-logo.svg +0 -15
  228. package/src/widget/hooks/useCustomTokenFetch.tsx +0 -74
  229. package/src/widget/hooks/useTokenWithFreshBalance.ts +0 -246
@@ -1,6 +1,9 @@
1
+ import {
2
+ getWalletIcon,
3
+ wagmiConnectorToWalletId,
4
+ } from "@0xtrails/wallet-registry"
1
5
  import type React from "react"
2
6
  import { useAccount } from "wagmi"
3
- import { getWalletIcon, wagmiConnectorToWalletId } from "../../wallets.js"
4
7
  import { Wallet } from "lucide-react"
5
8
 
6
9
  interface WalletImageProps {
@@ -14,7 +17,7 @@ export const WalletImage: React.FC<WalletImageProps> = ({ size, walletId }) => {
14
17
  // Use provided walletId or derive from current connector
15
18
  const currentWalletId =
16
19
  walletId || (connector ? wagmiConnectorToWalletId(connector) : null)
17
- const walletIcon = currentWalletId ? getWalletIcon(currentWalletId) : null
20
+ const walletIcon = currentWalletId ? getWalletIcon(currentWalletId) : ""
18
21
 
19
22
  // If no icon found, show default wallet icon
20
23
  if (!walletIcon) {
@@ -26,25 +29,10 @@ export const WalletImage: React.FC<WalletImageProps> = ({ size, walletId }) => {
26
29
  )
27
30
  }
28
31
 
29
- // Handle React component icons (like WalletIcon from lucide-react)
30
- if (typeof walletIcon === "function") {
31
- const IconComponent = walletIcon as React.ComponentType<{
32
- className?: string
33
- style?: React.CSSProperties
34
- }>
35
- return (
36
- <IconComponent
37
- className="text-gray-500 dark:text-gray-400"
38
- style={{ width: size, height: size }}
39
- />
40
- )
41
- }
42
-
43
- // Handle string URLs/data URIs
44
32
  return (
45
33
  <div className="relative inline-block">
46
34
  <img
47
- src={walletIcon as string}
35
+ src={walletIcon}
48
36
  alt={`${currentWalletId} wallet`}
49
37
  style={{ width: size, height: size }}
50
38
  onError={(e) => {
@@ -1,8 +1,8 @@
1
1
  import { ChevronRight, Search, Wallet } from "lucide-react"
2
+ import { topShownWallets } from "@0xtrails/wallet-registry"
2
3
  import type React from "react"
3
4
  import { useMemo, useState } from "react"
4
5
  import type { WalletConfig } from "../../wallets.js"
5
- import { topShownWallets } from "../../wallets.js"
6
6
  import { ScreenHeader } from "./ScreenHeader.js"
7
7
 
8
8
  export interface WalletListProps {
@@ -19,9 +19,9 @@ import { useDestinationSelectedToken } from "../hooks/useDestinationSelectedToke
19
19
  import { useOriginSelectedToken } from "../hooks/useOriginSelectedToken.js"
20
20
  import type { ProcessedFeeOption } from "../hooks/useSelectedFeeOption.js"
21
21
  import { useSelectedRecipient } from "../hooks/useSelectedRecipient.js"
22
- import { useSendForm } from "../hooks/useSendForm.js"
22
+ import { useSendForm, type OnCompleteProps } from "../hooks/useSendForm.js"
23
23
  import { useSwapState } from "../hooks/useSwapState.js"
24
- import { useTokenWithFreshBalance } from "../hooks/useTokenWithFreshBalance.js"
24
+ import { useAccountTokenBalanceOnchain } from "../../query/balance.hooks.js"
25
25
  import type { BaseProps } from "../types/commonProps.js"
26
26
  import { isWithinCooldown, SECOND_MS } from "../../utils/time.js"
27
27
  import { forexRateStore } from "../utils/forexRateStore.js"
@@ -97,7 +97,7 @@ const Withdraw: React.FC<WithdrawProps> = ({
97
97
  const {
98
98
  token: freshOriginToken,
99
99
  isLoadingBalance: isLoadingFreshOriginBalance,
100
- } = useTokenWithFreshBalance(originToken, account?.address)
100
+ } = useAccountTokenBalanceOnchain(originToken, account?.address)
101
101
 
102
102
  const { selectedRecipient, setSelectedRecipient } = useSelectedRecipient()
103
103
  const prevToRecipientRef = useRef<string | undefined>(toRecipient)
@@ -126,6 +126,7 @@ const Withdraw: React.FC<WithdrawProps> = ({
126
126
  setSellAmount,
127
127
  buyAmount,
128
128
  setBuyAmount,
129
+ resetSwapState,
129
130
  enterFormMode,
130
131
  } = useSwapState()
131
132
 
@@ -181,6 +182,23 @@ const Withdraw: React.FC<WithdrawProps> = ({
181
182
  const [_isUserTyping, setIsUserTyping] = useState(false)
182
183
  const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null)
183
184
 
185
+ useEffect(() => {
186
+ return () => {
187
+ if (typingTimeoutRef.current) {
188
+ clearTimeout(typingTimeoutRef.current)
189
+ }
190
+ }
191
+ }, [])
192
+
193
+ const handleCompleteAndClearAmounts = useCallback(
194
+ (result: OnCompleteProps) => {
195
+ setInputDisplayValue("")
196
+ resetSwapState()
197
+ onComplete(result)
198
+ },
199
+ [onComplete, resetSwapState],
200
+ )
201
+
184
202
  const {
185
203
  amount,
186
204
  amountRaw,
@@ -219,7 +237,7 @@ const Withdraw: React.FC<WithdrawProps> = ({
219
237
  paymasterUrls,
220
238
  onSend,
221
239
  onConfirm,
222
- onComplete,
240
+ onComplete: handleCompleteAndClearAmounts,
223
241
  selectedToken: originToken ?? undefined,
224
242
  setWalletConfirmRetryHandler,
225
243
  tradeType: tradeType,
@@ -1,7 +1,8 @@
1
1
  import { useMemo } from "react"
2
- import { useWallets, wagmiConnectorToWalletId } from "../../wallets.js"
2
+ import { wagmiConnectorToWalletId } from "@0xtrails/wallet-registry"
3
3
  import { type Connector, useConnections } from "wagmi"
4
4
  import type { Address } from "viem"
5
+ import { useWallets } from "../../wallets.js"
5
6
 
6
7
  /**
7
8
  * Hook to get the wallet icon for an connected address
@@ -1,11 +1,10 @@
1
1
  import { useMemo, useEffect, useCallback } from "react"
2
2
  import { isAddress } from "viem"
3
- import { useQuery } from "@tanstack/react-query"
3
+ import { useQuery, useQueryClient } from "@tanstack/react-query"
4
4
  import { getChainInfo } from "../../chains.js"
5
5
  import { useSupportedChains } from "../../query/chains.hooks.js"
6
- import { getSupportedTokens, getTokenInfo } from "../../tokens.js"
6
+ import { getSupportedTokens, searchTokenByAddress } from "../../tokens.js"
7
7
  import { useCustomTokens } from "../../customTokens.js"
8
- // Removed convertTokenToSupportedToken import - addCustomToken now accepts Token directly
9
8
  import type { Token } from "../../tokens.js"
10
9
  import { logger } from "../../logger.js"
11
10
  import { MINUTE_MS, DAY_MS } from "../../utils/time.js"
@@ -17,10 +16,15 @@ interface UseCustomTokenSearchProps {
17
16
  searchQuery: string
18
17
  filteredTokensFormatted: Token[]
19
18
  filterByChainId: number | null
19
+ /** True once the API search (getTokenList) for an address query
20
+ * has completed with zero results. */
21
+ isApiAddressSearchComplete: boolean
20
22
  }
21
23
 
22
24
  interface UseCustomTokenSearchReturn {
23
25
  chainFilteredTokens: Token[]
26
+ /** All tokens found by findContractInfo across every supported chain (unfiltered by chain selector). */
27
+ foundTokensAllChains: Token[]
24
28
  isLoadingCustomToken: boolean
25
29
  customTokenError: Error | null
26
30
  isCustomToken: (chainId: number, contractAddress: string) => boolean
@@ -34,21 +38,23 @@ export function useCustomTokenSearch({
34
38
  searchQuery,
35
39
  filteredTokensFormatted,
36
40
  filterByChainId,
41
+ isApiAddressSearchComplete,
37
42
  }: UseCustomTokenSearchProps): UseCustomTokenSearchReturn {
38
43
  const { data: supportedChains = [] } = useSupportedChains()
39
44
  const { addCustomToken, customTokens } = useCustomTokens()
40
45
  const trailsConfig = useTrails()
46
+ const queryClient = useQueryClient()
41
47
  // Get official supported tokens (before merging with custom tokens) to check if token is truly custom
42
- const { data: officialSupportedTokens = [] } = useQuery({
43
- queryKey: [
44
- "supportedTokens",
45
- trailsConfig.trailsApiUrl,
46
- trailsConfig.trailsApiKey,
47
- ],
48
+ const {
49
+ data: officialSupportedTokens = [],
50
+ isSuccess: officialTokensLoaded,
51
+ } = useQuery({
52
+ queryKey: ["supportedTokens"],
48
53
  queryFn: () =>
49
54
  getSupportedTokens({
50
55
  trailsApiKey: trailsConfig.trailsApiKey,
51
56
  trailsApiUrl: trailsConfig.trailsApiUrl,
57
+ queryClient,
52
58
  }),
53
59
  staleTime: DAY_MS, // 24 hours - token list is static
54
60
  gcTime: DAY_MS, // 24 hours
@@ -96,7 +102,12 @@ export function useCustomTokenSearch({
96
102
  )
97
103
  }, [searchAddress, filteredTokensFormatted, filterByChainId, customTokens])
98
104
 
99
- // Fetch custom token info from ALL supported chains when address is detected and not in list
105
+ // Single request to find the token across all chains via SearchContractInfo
106
+ const supportedChainIds = useMemo(
107
+ () => new Set(supportedChains.map((c) => c.id)),
108
+ [supportedChains],
109
+ )
110
+
100
111
  const {
101
112
  data: foundTokensAllChains = [],
102
113
  isLoading: isLoadingCustomToken,
@@ -110,24 +121,15 @@ export function useCustomTokenSearch({
110
121
  `[trails-sdk] Searching for token ${searchAddress} across all chains`,
111
122
  )
112
123
 
113
- // Attempt to fetch from all supported chains in parallel
114
- const results = await Promise.allSettled(
115
- supportedChains.map(async (chain) => {
116
- const tokenInfo = await getTokenInfo(chain.id, searchAddress, {
117
- trailsApiKey: trailsConfig.trailsApiKey,
118
- trailsApiUrl: trailsConfig.trailsApiUrl,
119
- sequenceMetadataUrl: trailsConfig.sequenceMetadataUrl,
120
- })
121
- return tokenInfo ? { ...tokenInfo, chainId: chain.id } : null
122
- }),
123
- )
124
+ const allTokens = await searchTokenByAddress(searchAddress, {
125
+ sequenceMetadataUrl: trailsConfig.sequenceMetadataUrl,
126
+ trailsApiKey: trailsConfig.trailsApiKey,
127
+ })
124
128
 
125
- // Extract successful results
126
- const validTokens = results
127
- .filter(
128
- (result) => result.status === "fulfilled" && result.value !== null,
129
- )
130
- .map((result) => (result as PromiseFulfilledResult<Token>).value)
129
+ // Keep only tokens on supported chains
130
+ const validTokens = allTokens.filter(
131
+ (t) => t.chainId != null && supportedChainIds.has(t.chainId),
132
+ )
131
133
 
132
134
  logger.console.log(
133
135
  `[trails-sdk] Found ${validTokens.length} tokens across all chains`,
@@ -136,24 +138,37 @@ export function useCustomTokenSearch({
136
138
  return validTokens
137
139
  },
138
140
  enabled:
139
- !!searchAddress && !addressExistsInList && supportedChains.length > 0,
141
+ !!searchAddress &&
142
+ !addressExistsInList &&
143
+ supportedChains.length > 0 &&
144
+ isApiAddressSearchComplete,
140
145
  staleTime: 5 * MINUTE_MS,
141
146
  gcTime: DAY_MS,
142
147
  retry: 1,
143
148
  refetchOnWindowFocus: false,
144
149
  })
145
150
 
146
- // Add found tokens to storage
151
+ // Add found tokens to storage only after officialSupportedTokens has loaded
152
+ // to avoid incorrectly persisting official tokens as custom tokens.
147
153
  useEffect(() => {
148
- if (foundTokensAllChains.length > 0 && !customTokenError) {
154
+ if (
155
+ foundTokensAllChains.length > 0 &&
156
+ !customTokenError &&
157
+ officialTokensLoaded
158
+ ) {
149
159
  foundTokensAllChains.forEach((tokenInfo) => {
150
160
  const existsInOfficialTokens = officialSupportedTokens.some(
151
161
  (token) =>
152
162
  token.chainId === tokenInfo.chainId &&
153
163
  addressEqual(token.contractAddress, tokenInfo.contractAddress),
154
164
  )
165
+ const existsInCustomTokens = customTokens.some(
166
+ (token) =>
167
+ token.chainId === tokenInfo.chainId &&
168
+ addressEqual(token.contractAddress, tokenInfo.contractAddress),
169
+ )
155
170
 
156
- if (!existsInOfficialTokens) {
171
+ if (!existsInOfficialTokens && !existsInCustomTokens) {
157
172
  addCustomToken(tokenInfo)
158
173
  logger.console.log(
159
174
  "[trails-sdk] Added custom token to storage:",
@@ -167,6 +182,8 @@ export function useCustomTokenSearch({
167
182
  customTokenError,
168
183
  addCustomToken,
169
184
  officialSupportedTokens,
185
+ officialTokensLoaded,
186
+ customTokens,
170
187
  ])
171
188
 
172
189
  // Show error if custom token fetch failed
@@ -190,9 +207,10 @@ export function useCustomTokenSearch({
190
207
  }, [filteredTokensFormatted])
191
208
 
192
209
  // Convert custom tokens from localStorage to TokenFormatted format
193
- // Always include in search results, but prefer balance info from filteredTokensFormatted if available
210
+ // Only include custom tokens that match the current search query
194
211
  const customTokensFromStorage = useMemo(() => {
195
212
  const searchAddressLower = searchAddress?.toLowerCase()
213
+ const trimmedQuery = searchQuery.trim().toLowerCase()
196
214
 
197
215
  return customTokens
198
216
  .filter((token) => {
@@ -200,6 +218,11 @@ export function useCustomTokenSearch({
200
218
  if (searchAddressLower) {
201
219
  if (token.contractAddress.toLowerCase() !== searchAddressLower)
202
220
  return false
221
+ } else if (trimmedQuery) {
222
+ const sym = (token.symbol || "").toLowerCase()
223
+ const name = (token.name || "").toLowerCase()
224
+ if (!sym.includes(trimmedQuery) && !name.includes(trimmedQuery))
225
+ return false
203
226
  }
204
227
  // Apply chain filter if active
205
228
  if (filterByChainId !== null) {
@@ -233,7 +256,13 @@ export function useCustomTokenSearch({
233
256
  isCustomToken: token.isCustomToken ?? true,
234
257
  }
235
258
  })
236
- }, [customTokens, filterByChainId, filteredTokensMap, searchAddress])
259
+ }, [
260
+ customTokens,
261
+ filterByChainId,
262
+ filteredTokensMap,
263
+ searchAddress,
264
+ searchQuery,
265
+ ])
237
266
 
238
267
  // Helper function to check if a token has a balance
239
268
  const hasBalance = useCallback((token: Token): boolean => {
@@ -332,6 +361,7 @@ export function useCustomTokenSearch({
332
361
 
333
362
  return {
334
363
  chainFilteredTokens,
364
+ foundTokensAllChains,
335
365
  isLoadingCustomToken,
336
366
  customTokenError,
337
367
  isCustomToken,
@@ -4,10 +4,7 @@ import { base, arbitrum } from "viem/chains"
4
4
  import type { Address } from "ox"
5
5
  import { useSupportedTokens } from "../../tokens.js"
6
6
  import type { Token } from "../../tokens.js"
7
- import {
8
- useTokenBalances,
9
- WIDGET_REFRESH_INTERVAL,
10
- } from "../../query/balance.hooks.js"
7
+ import { useTokenBalances } from "../../query/balance.hooks.js"
11
8
  import { logger } from "../../logger.js"
12
9
 
13
10
  /**
@@ -36,7 +33,7 @@ export function useDefaultDestinationToken(originToken: Token | null) {
36
33
  const { sortedTokens, isLoadingSortedTokens } = useTokenBalances(
37
34
  originToken ? (address as Address.Address) : null,
38
35
  undefined,
39
- { refetchInterval: WIDGET_REFRESH_INTERVAL },
36
+ { refetchInterval: false },
40
37
  )
41
38
 
42
39
  const isLoading = isLoadingTokens || isLoadingSortedTokens
@@ -3,10 +3,7 @@ import { useAccount } from "wagmi"
3
3
  import type { Address } from "ox"
4
4
  import { useWidgetProps } from "./useWidgetProps.js"
5
5
  import { useTargetAmount } from "./useTargetAmount.js"
6
- import {
7
- useTokenBalances,
8
- WIDGET_REFRESH_INTERVAL,
9
- } from "../../query/balance.hooks.js"
6
+ import { useTokenBalances } from "../../query/balance.hooks.js"
10
7
  import type { Token } from "../../tokens.js"
11
8
  import { getChainInfo } from "../../chains.js"
12
9
  import { isNativeToken } from "../../utils/address.js"
@@ -90,7 +87,7 @@ export function useDefaultOriginToken() {
90
87
  const { sortedTokens, isLoadingSortedTokens } = useTokenBalances(
91
88
  address as Address.Address,
92
89
  undefined,
93
- { refetchInterval: WIDGET_REFRESH_INTERVAL },
90
+ { refetchInterval: false },
94
91
  )
95
92
 
96
93
  const isLoading = isLoadingSortedTokens
@@ -1,7 +1,7 @@
1
1
  import { useQuery } from "@tanstack/react-query"
2
2
  import type { TrailsOnramp } from "../../onrampClient.js"
3
3
  import { useOnrampClient } from "../../onrampClient.js"
4
- import type { MeldFiatCurrency } from "../../onramp-client/trails-onramp.gen.js"
4
+ import type { MeldFiatCurrency } from "@0xtrails/api/onramp"
5
5
  import { meldQueries } from "../../query/meld.queries.js"
6
6
 
7
7
  export type OnrampFiatCurrency = MeldFiatCurrency
@@ -39,11 +39,13 @@ function mapIntentHistoryToTransaction(
39
39
  const depositTransactionHash = receipt.depositTransaction?.txnHash
40
40
  const originTransactionHash = receipt.originTransaction?.txnHash
41
41
  const destinationTransactionHash = receipt.destinationTransaction?.txnHash
42
+ const refundTransactionHash = receipt.refundTransaction?.txnHash
42
43
 
43
44
  // Get transaction statuses from the transaction objects
44
45
  const depositStatus = receipt.depositTransaction?.status
45
46
  const originStatus = receipt.originTransaction?.status
46
47
  const destinationStatus = receipt.destinationTransaction?.status
48
+ const refundStatus = receipt.refundTransaction?.status
47
49
 
48
50
  // Use summary if available, otherwise construct from transaction data
49
51
  const intentId = intentHistory.intentId
@@ -112,6 +114,9 @@ function mapIntentHistoryToTransaction(
112
114
  depositTransactionStatus: depositStatus,
113
115
  originTransactionStatus: originStatus,
114
116
  destinationTransactionStatus: destinationStatus,
117
+ refundTransactionHash: refundTransactionHash,
118
+ refundTransactionStatus: refundStatus,
119
+ refundTransactionChainId: receipt.refundTransaction?.chainId,
115
120
  originToken:
116
121
  originTokenMetadata && originTokenAddress && originTokenAddress !== ""
117
122
  ? ({
@@ -5,7 +5,7 @@ import { logger } from "../../logger.js"
5
5
  import type {
6
6
  SearchMeldTransactionsRequest,
7
7
  SearchMeldTransactionsResponse,
8
- } from "../../onramp-client/trails-onramp.gen.js"
8
+ } from "@0xtrails/api/onramp"
9
9
  import { getAccountTransactionHistory } from "../../transactions.js"
10
10
  import { addressEqual } from "../../utils/address.js"
11
11
  import { toSeconds, elapsed, SECOND_MS } from "../../utils/time.js"
@@ -41,6 +41,7 @@ import type {
41
41
  import type { OnrampTrackingData } from "../types/analytics.js"
42
42
  import { isZeroOrInvalidAmount } from "../../utils/validation.js"
43
43
  import { addressEqual } from "../../utils/address.js"
44
+ import { HOUR_MS } from "../../utils/time.js"
44
45
 
45
46
  /**
46
47
  * Configuration options for the `useQuote` hook.
@@ -197,7 +198,7 @@ export type Quote = {
197
198
  originAmountUsdFormatted: string
198
199
  destinationAmountUsdFormatted: string
199
200
  // Locale display fields
200
- intentProtocolVersion: string
201
+ intentProtocol: string
201
202
  originAmountFormattedLocale?: string
202
203
  originAmountUsdLocaleDisplay?: string
203
204
  destinationAmountFormattedLocale?: string
@@ -586,9 +587,8 @@ export function useQuote({
586
587
  !!senderAddress &&
587
588
  !!fromChainId &&
588
589
  !hasCachedContractResult(senderAddress, fromChainId),
589
- // Only cache for 24h if it's a contract, otherwise keep checking
590
590
  staleTime: (query) => (query.state.data === true ? Infinity : 0),
591
- gcTime: Infinity,
591
+ gcTime: HOUR_MS,
592
592
  })
593
593
 
594
594
  // Query for sender on destination chain - skip if same chain as origin (reuse origin result)
@@ -605,7 +605,7 @@ export function useQuote({
605
605
  !isSameChain &&
606
606
  !hasCachedContractResult(senderAddress, toChainId),
607
607
  staleTime: (query) => (query.state.data === true ? Infinity : 0),
608
- gcTime: Infinity,
608
+ gcTime: HOUR_MS,
609
609
  })
610
610
 
611
611
  // Query for recipient on destination chain - skip if same address as sender (reuse sender result)
@@ -622,7 +622,7 @@ export function useQuote({
622
622
  !isSameAddress &&
623
623
  !hasCachedContractResult(recipientAddress, toChainId),
624
624
  staleTime: (query) => (query.state.data === true ? Infinity : 0),
625
- gcTime: Infinity,
625
+ gcTime: HOUR_MS,
626
626
  })
627
627
 
628
628
  // Derive the final values, reusing results where addresses/chains match
@@ -728,48 +728,54 @@ export function useQuote({
728
728
  const originChain = getChainInfo(fromChainId)
729
729
  const nativeTokenSymbol = originChain?.nativeCurrency?.symbol ?? ""
730
730
 
731
- const [balanceResult, nativePriceResult] = await Promise.all([
732
- queryClient
733
- .ensureQueryData(
734
- balanceQueries.withPrices(
735
- walletClient.account!.address,
736
- indexerGatewayClient,
737
- trailsClient,
738
- ),
739
- )
740
- .catch((balanceError: unknown) => {
741
- if (isNetworkError(balanceError)) {
742
- logger.console.warn(
743
- "[trails-sdk] [useQuote] Network error fetching balances, proceeding with quote using zero balance:",
744
- {
745
- error: balanceError,
746
- account: walletClient.account!.address,
747
- },
748
- )
749
- return { balances: [] }
750
- }
751
- throw balanceError
752
- }),
753
-
754
- nativeTokenSymbol
755
- ? getTokenPrice(trailsClient, {
756
- tokenSymbol: nativeTokenSymbol,
757
- tokenAddress: zeroAddress,
758
- chainId: fromChainId,
759
- }).catch((error) => {
760
- logger.console.error(
761
- "[trails-sdk] [useQuote] Error getting origin native token price:",
762
- error,
763
- )
764
- return null
765
- })
766
- : Promise.resolve(null),
767
- ])
731
+ const balanceResult = await queryClient
732
+ .ensureQueryData(
733
+ balanceQueries.withPrices(
734
+ walletClient.account!.address,
735
+ indexerGatewayClient,
736
+ trailsClient,
737
+ ),
738
+ )
739
+ .catch((balanceError: unknown) => {
740
+ if (isNetworkError(balanceError)) {
741
+ logger.console.warn(
742
+ "[trails-sdk] [useQuote] Network error fetching balances, proceeding with quote using zero balance:",
743
+ {
744
+ error: balanceError,
745
+ account: walletClient.account!.address,
746
+ },
747
+ )
748
+ return { balances: [] }
749
+ }
750
+ throw balanceError
751
+ })
768
752
 
769
753
  const balances = balanceResult.balances
770
- const originNativeTokenPriceUsd = nativePriceResult?.priceUsd ?? 0
754
+ const nativeTokenFromBalances = balances.find(
755
+ (b: any) =>
756
+ b.chainId === fromChainId &&
757
+ (addressEqual(b.contractAddress, zeroAddress) || b.isNativeToken),
758
+ )
759
+ let originNativeTokenPriceUsd = nativeTokenFromBalances?.priceUsd ?? 0
760
+
761
+ // Reuse the price already fetched by balanceQueries.withPrices.
762
+ // Fallback only when native token is missing from the balances payload.
763
+ if (!originNativeTokenPriceUsd && nativeTokenSymbol) {
764
+ const nativePriceResult = await getTokenPrice(trailsClient, {
765
+ tokenSymbol: nativeTokenSymbol,
766
+ tokenAddress: zeroAddress,
767
+ chainId: fromChainId,
768
+ }).catch((error) => {
769
+ logger.console.error(
770
+ "[trails-sdk] [useQuote] Error getting origin native token price:",
771
+ error,
772
+ )
773
+ return null
774
+ })
775
+ originNativeTokenPriceUsd = nativePriceResult?.priceUsd ?? 0
776
+ }
771
777
 
772
- if (nativePriceResult) {
778
+ if (originNativeTokenPriceUsd) {
773
779
  logger.console.log(
774
780
  "[trails-sdk] [useQuote] Origin native token price:",
775
781
  {
@@ -1065,7 +1071,7 @@ export function useQuote({
1065
1071
  destinationApproveAddress:
1066
1072
  prepareSendQuote.destinationApproveAddress ?? "",
1067
1073
  destinationCalldata: prepareSendQuote.destinationCalldata ?? "",
1068
- intentProtocolVersion: prepareSendQuote.intentProtocolVersion,
1074
+ intentProtocol: prepareSendQuote.intentProtocol,
1069
1075
  // Fee breakdown fields
1070
1076
  trailsFeeBreakdown: prepareSendQuote.trailsFeeBreakdown ?? null,
1071
1077
  originGasUsd: prepareSendQuote.originGasUsd ?? null,
@@ -2,11 +2,19 @@ import { createContext, useContext, useState, type ReactNode } from "react"
2
2
  import { logger } from "../../logger.js"
3
3
  import type { FundMethod } from "../../transactionIntent/types.js"
4
4
 
5
+ export interface SelectedMeshExchange {
6
+ integrationId: string
7
+ exchangeKey: string
8
+ exchangeName: string
9
+ }
10
+
5
11
  interface SelectedFundMethodContextType {
6
12
  selectedFundMethod: FundMethod
7
13
  paymentMethod: { id: string; name: string }
14
+ selectedMeshExchange: SelectedMeshExchange | null
8
15
  setSelectedFundMethod: (method: FundMethod) => void
9
16
  setPaymentMethod: (paymentMethod: { id: string; name: string }) => void
17
+ setSelectedMeshExchange: (exchange: SelectedMeshExchange | null) => void
10
18
  }
11
19
 
12
20
  const SelectedFundMethodContext = createContext<
@@ -26,12 +34,16 @@ export const SelectedFundMethodProvider = ({
26
34
  id: string
27
35
  name: string
28
36
  }>({ id: "", name: "" })
37
+ const [selectedMeshExchange, setSelectedMeshExchange] =
38
+ useState<SelectedMeshExchange | null>(null)
29
39
 
30
40
  const value: SelectedFundMethodContextType = {
31
41
  selectedFundMethod,
32
42
  paymentMethod,
43
+ selectedMeshExchange,
33
44
  setSelectedFundMethod,
34
45
  setPaymentMethod,
46
+ setSelectedMeshExchange,
35
47
  }
36
48
 
37
49
  return (
@@ -71,7 +83,8 @@ export function useSkipBalanceFetch(): {
71
83
  // Check if current fund method should skip balance fetching
72
84
  const shouldSkipBalanceFetch =
73
85
  context.selectedFundMethod === "onramp-meld" ||
74
- context.selectedFundMethod === "direct-transfer"
86
+ context.selectedFundMethod === "direct-transfer" ||
87
+ context.selectedFundMethod === "onramp-mesh"
75
88
 
76
89
  return {
77
90
  selectedFundMethod: context.selectedFundMethod,