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
@@ -3,7 +3,7 @@ import { trackPaymentError, trackPaymentStarted } from "./analytics.js"
3
3
  import { getSlippageToleranceValue } from "./widget/components/SlippageToleranceSettings.js"
4
4
  import { addressEqual, isNativeToken, isZeroAccount } from "./utils/address.js"
5
5
  import { getERC20TransferData } from "./utils.js"
6
- import { getTokenPrice } from "./query/price.queries.js"
6
+ import { getTokenPrices } from "./query/price.queries.js"
7
7
  import {
8
8
  calldataHasPlaceholder,
9
9
  replacePlaceholderInCalldata,
@@ -221,22 +221,28 @@ export async function prepareSend(
221
221
  )
222
222
  }
223
223
 
224
- let intentProtocolVersion = IntentProtocolVersion.v1 // Default to v1
224
+ let intentProtocol = IntentProtocolVersion.v1 // Default to v1
225
225
 
226
226
  // Only fetch supported versions when using dev trails API environments for now
227
- const shouldFetchVersions = getShouldFetchVersions()
227
+ const shouldFetchVersions = getShouldFetchVersions(trailsApiUrl)
228
228
 
229
229
  if (shouldFetchVersions) {
230
230
  try {
231
- const supportedIntentProtocolVersions =
232
- await trailsClient.getSupportedIntentProtocolVersions()
233
- intentProtocolVersion =
234
- supportedIntentProtocolVersions.versions[0] ?? IntentProtocolVersion.v1
231
+ const normalizeIntentProtocolVersion = (
232
+ version?: string,
233
+ ): IntentProtocolVersion => {
234
+ if (version === "v1.5" || version === "v1_5") {
235
+ return IntentProtocolVersion.v1_5
236
+ }
237
+ return IntentProtocolVersion.v1
238
+ }
239
+
240
+ const response = await trailsClient.getDefaultIntentProtocol()
241
+ intentProtocol = normalizeIntentProtocolVersion(response.version)
235
242
  logger.console.log(
236
- "[trails-sdk] [intent-protocol] Fetched supported protocol versions from API",
243
+ "[trails-sdk] [intent-protocol] Fetched default protocol version from API",
237
244
  {
238
- supportedVersions: supportedIntentProtocolVersions.versions,
239
- selectedVersion: intentProtocolVersion,
245
+ intentProtocol,
240
246
  },
241
247
  )
242
248
  } catch (_error) {
@@ -254,9 +260,9 @@ export async function prepareSend(
254
260
  logger.console.log(
255
261
  "[trails-sdk] [intent-protocol] === Protocol Version Selected ===",
256
262
  {
257
- intentProtocolVersion,
258
- isV1: intentProtocolVersion === IntentProtocolVersion.v1,
259
- isV1_5: intentProtocolVersion === IntentProtocolVersion.v1_5,
263
+ intentProtocol,
264
+ isV1: intentProtocol === IntentProtocolVersion.v1,
265
+ isV1_5: intentProtocol === IntentProtocolVersion.v1_5,
260
266
  },
261
267
  )
262
268
 
@@ -264,15 +270,15 @@ export async function prepareSend(
264
270
  logger.console.log(
265
271
  "[trails-sdk] [intent-protocol] Calldata wrapping required for custom calldata",
266
272
  {
267
- intentProtocolVersion,
273
+ intentProtocol,
268
274
  wrapperType:
269
- intentProtocolVersion === "v1"
275
+ intentProtocol === IntentProtocolVersion.v1
270
276
  ? "TrailsRouter"
271
277
  : "TrailsUtils (Hydrate)",
272
278
  },
273
279
  )
274
280
 
275
- if (intentProtocolVersion === "v1") {
281
+ if (intentProtocol === IntentProtocolVersion.v1) {
276
282
  // @deprecated - v1 only
277
283
 
278
284
  logger.console.log(
@@ -378,36 +384,31 @@ export async function prepareSend(
378
384
  }
379
385
  }
380
386
 
381
- // Fallback price fetching if prices are 0
382
- if (!sourceTokenPriceUsd) {
383
- try {
384
- const price = await getTokenPrice(trailsClient, {
385
- tokenSymbol: originTokenAddress,
386
- tokenAddress: originTokenAddress,
387
- chainId: originChainId,
388
- })
389
- sourceTokenPriceUsd = price?.priceUsd ?? 0
390
- } catch (error) {
391
- logger.console.error(
392
- "[trails-sdk] Error getting source token price:",
393
- error,
394
- )
395
- }
396
- }
397
-
398
- if (!destinationTokenPriceUsd) {
387
+ // Fallback price fetching if any prices are missing
388
+ if (!sourceTokenPriceUsd || !destinationTokenPriceUsd) {
399
389
  try {
400
- const price = await getTokenPrice(trailsClient, {
401
- tokenSymbol: destinationTokenAddress,
402
- tokenAddress: destinationTokenAddress,
403
- chainId: destinationChainId,
404
- })
405
- destinationTokenPriceUsd = price?.priceUsd ?? 0
390
+ const prices = await getTokenPrices(trailsClient, [
391
+ {
392
+ tokenSymbol: originTokenSymbol,
393
+ tokenAddress: originTokenAddress,
394
+ chainId: originChainId,
395
+ },
396
+ {
397
+ tokenSymbol: destinationTokenSymbol,
398
+ tokenAddress: destinationTokenAddress,
399
+ chainId: destinationChainId,
400
+ },
401
+ ])
402
+ sourceTokenPriceUsd =
403
+ prices.find((p) =>
404
+ addressEqual(p.token.tokenAddress, originTokenAddress),
405
+ )?.priceUsd ?? 0
406
+ destinationTokenPriceUsd =
407
+ prices.find((p) =>
408
+ addressEqual(p.token.tokenAddress, destinationTokenAddress),
409
+ )?.priceUsd ?? 0
406
410
  } catch (error) {
407
- logger.console.error(
408
- "[trails-sdk] Error getting destination token price:",
409
- error,
410
- )
411
+ logger.console.error("[trails-sdk] Error getting token prices:", error)
411
412
  }
412
413
  }
413
414
 
@@ -10,19 +10,22 @@ import type {
10
10
  } from "@0xsequence/indexer"
11
11
  import { ContractVerificationStatus } from "@0xsequence/indexer"
12
12
  import type { TokenPrice } from "@0xtrails/api"
13
- import { parseUnits, zeroAddress } from "viem"
13
+ import { erc20Abi, parseUnits, zeroAddress } from "viem"
14
14
  import { getTokenPrices } from "./price.queries.js"
15
- import { getChainInfo } from "../chains.js"
15
+ import { getChainInfo, getChainRpcClient } from "../chains.js"
16
16
  import { getTokenImageUrl, type Token } from "../tokens.js"
17
17
  import type { TrailsClient } from "../trailsClient.js"
18
-
19
18
  import { logger } from "../logger.js"
20
- import { addressEqual } from "../utils/address.js"
19
+ import {
20
+ addressEqual,
21
+ isNativeToken as isNativeTokenAddress,
22
+ } from "../utils/address.js"
21
23
  import {
22
24
  formatBalanceFields,
23
25
  formatPriceFields,
24
26
  formatUsdFields,
25
27
  getTokenBalanceUsd,
28
+ rawAmountToNumber,
26
29
  type Price,
27
30
  } from "../utils/format.js"
28
31
  import { isNetworkError } from "./helpers.js"
@@ -594,6 +597,133 @@ export async function fetchBalancesWithPrices({
594
597
  }
595
598
  }
596
599
 
600
+ // ============================================================================
601
+ // On-chain Balance Fetcher — bypasses the indexer entirely
602
+ // ============================================================================
603
+
604
+ export type FetchTokenBalanceOnchainParams = {
605
+ token: Token
606
+ accountAddress: string
607
+ }
608
+
609
+ export type FetchTokenBalanceOnchainWithPricesParams = {
610
+ token: Token
611
+ accountAddress: string
612
+ trailsClient: TrailsClient
613
+ }
614
+
615
+ export type FetchTokenBalanceOnchainReturn = {
616
+ token: Token
617
+ }
618
+
619
+ /**
620
+ * Reads the on-chain balance for a single token via RPC.
621
+ * Fast path — only a single RPC call, no price API.
622
+ *
623
+ * Returns a Token with fresh balance fields (balance, balanceFormatted,
624
+ * balanceDisplay, balanceLocaleDisplay).
625
+ */
626
+ export async function fetchTokenBalanceOnchain({
627
+ token,
628
+ accountAddress,
629
+ }: FetchTokenBalanceOnchainParams): Promise<FetchTokenBalanceOnchainReturn> {
630
+ const chainId = token.chainId
631
+ if (!chainId) {
632
+ throw new Error("Token must have a chainId")
633
+ }
634
+
635
+ const publicClient = getChainRpcClient(chainId)
636
+ const isNative = isNativeTokenAddress(token.contractAddress)
637
+
638
+ let rawBalance: bigint
639
+ if (isNative) {
640
+ rawBalance = await publicClient.getBalance({
641
+ address: accountAddress as `0x${string}`,
642
+ })
643
+ } else {
644
+ rawBalance = (await publicClient.readContract({
645
+ address: token.contractAddress as `0x${string}`,
646
+ abi: erc20Abi,
647
+ functionName: "balanceOf",
648
+ args: [accountAddress as `0x${string}`],
649
+ })) as bigint
650
+ }
651
+
652
+ const balanceFields = formatBalanceFields(
653
+ rawBalance.toString(),
654
+ token.decimals,
655
+ )
656
+
657
+ return {
658
+ token: {
659
+ ...token,
660
+ ...balanceFields,
661
+ isSufficientBalance: true,
662
+ },
663
+ }
664
+ }
665
+
666
+ /**
667
+ * Reads the on-chain balance via RPC, then enriches it with price and USD
668
+ * data from the Trails price API.
669
+ *
670
+ * Use this variant only when the caller needs balanceUsd / priceUsd fields.
671
+ */
672
+ export async function fetchTokenBalanceOnchainWithPrices({
673
+ token,
674
+ accountAddress,
675
+ trailsClient,
676
+ }: FetchTokenBalanceOnchainWithPricesParams): Promise<FetchTokenBalanceOnchainReturn> {
677
+ const { token: tokenWithBalance } = await fetchTokenBalanceOnchain({
678
+ token,
679
+ accountAddress,
680
+ })
681
+
682
+ const chainId = token.chainId!
683
+ const isNative = isNativeTokenAddress(token.contractAddress)
684
+
685
+ let priceUsd: number | undefined
686
+ try {
687
+ const tokenPrices = await getTokenPrices(trailsClient, [
688
+ {
689
+ chainId,
690
+ tokenAddress: isNative ? zeroAddress : token.contractAddress,
691
+ tokenSymbol: isNative ? "" : token.symbol,
692
+ },
693
+ ])
694
+ if (tokenPrices.length > 0 && tokenPrices[0]?.priceUsd !== undefined) {
695
+ priceUsd = tokenPrices[0].priceUsd
696
+ }
697
+ } catch (err) {
698
+ logger.console.warn(
699
+ "[trails-sdk] Price fetch failed, returning balance without prices:",
700
+ err,
701
+ )
702
+ }
703
+
704
+ const priceFields = priceUsd !== undefined ? formatPriceFields(priceUsd) : {}
705
+
706
+ let usdFields: ReturnType<typeof formatUsdFields> = {}
707
+ if (priceUsd !== undefined && tokenWithBalance.balance) {
708
+ const balanceNum = rawAmountToNumber(
709
+ tokenWithBalance.balance,
710
+ token.decimals,
711
+ )
712
+ const balanceUsdValue = balanceNum * priceUsd
713
+ if (Number.isFinite(balanceUsdValue)) {
714
+ usdFields = formatUsdFields(balanceUsdValue)
715
+ }
716
+ }
717
+
718
+ return {
719
+ token: {
720
+ ...tokenWithBalance,
721
+ ...priceFields,
722
+ ...usdFields,
723
+ },
724
+ }
725
+ }
726
+
597
727
  // ============================================================================
598
728
  // Pure Sufficiency Check
599
729
  // ============================================================================
@@ -215,6 +215,67 @@ export type UseAccountTokenBalanceParams = {
215
215
  trailsClient?: TrailsClient | null
216
216
  }
217
217
 
218
+ /**
219
+ * Fetches a single token's on-chain balance via RPC.
220
+ *
221
+ * Pass `withPrices: true` to additionally fetch USD price data from the
222
+ * Trails price API (uses a separate query key so balance-only consumers
223
+ * are not blocked by the price call).
224
+ *
225
+ * Auto-disables when `accountAddress` or `token` is nullish.
226
+ */
227
+ export function useAccountTokenBalanceOnchain(
228
+ token: Token | null | undefined,
229
+ accountAddress: string | null | undefined,
230
+ options?: {
231
+ refetchInterval?: number | false
232
+ disabled?: boolean
233
+ withPrices?: boolean
234
+ },
235
+ ): {
236
+ token: Token | null | undefined
237
+ isLoadingBalance: boolean
238
+ error: Error | null
239
+ refetch: () => void
240
+ } {
241
+ const trailsClient = useTrailsClient()
242
+ const { isUserActive } = useUserActivityContext()
243
+
244
+ const isDisabled = options?.disabled || !accountAddress || !token
245
+ const effectiveToken = isDisabled ? null : token
246
+ const effectiveAddress = isDisabled ? null : accountAddress
247
+
248
+ const refetchInterval = isUserActive
249
+ ? clampRefetchInterval(options?.refetchInterval)
250
+ : false
251
+
252
+ const queryOpts = options?.withPrices
253
+ ? balanceQueries.onchainWithPrices(
254
+ effectiveToken,
255
+ effectiveAddress,
256
+ trailsClient,
257
+ )
258
+ : balanceQueries.onchain(effectiveToken, effectiveAddress)
259
+
260
+ const { data, isLoading, error, refetch } = useQuery({
261
+ ...queryOpts,
262
+ refetchInterval,
263
+ refetchIntervalInBackground: false,
264
+ refetchOnWindowFocus: true,
265
+ refetchOnReconnect: true,
266
+ refetchOnMount: true,
267
+ })
268
+
269
+ const enrichedToken = data?.token ?? token ?? null
270
+
271
+ return {
272
+ token: isDisabled ? token : enrichedToken,
273
+ isLoadingBalance: isLoading,
274
+ error,
275
+ refetch,
276
+ }
277
+ }
278
+
218
279
  export function useAccountTokenBalance({
219
280
  account,
220
281
  token,
@@ -285,13 +346,11 @@ export function useHasSufficientBalanceUsd(
285
346
  export function useAccountTotalBalanceUsd(account: string | null) {
286
347
  const indexerGatewayClient = useIndexerGatewayClient()
287
348
  const trailsClient = useTrailsClient()
288
- const { isUserActive } = useUserActivityContext()
289
349
 
290
350
  return useQuery({
291
351
  ...balanceQueries.totalUsd(account, indexerGatewayClient, trailsClient),
292
352
  refetchOnWindowFocus: false,
293
353
  refetchOnReconnect: true,
294
- refetchInterval: isUserActive ? WIDGET_REFRESH_INTERVAL : false,
295
354
  refetchIntervalInBackground: false,
296
355
  refetchOnMount: true,
297
356
  })
@@ -10,10 +10,14 @@ import { createRetry, retryDelay } from "./helpers.js"
10
10
  import {
11
11
  fetchMultiAccountBalances,
12
12
  fetchBalancesWithPrices,
13
+ fetchTokenBalanceOnchain,
14
+ fetchTokenBalanceOnchainWithPrices,
13
15
  checkSufficientToken,
14
16
  type GetTokenBalancesForMultipleAccountsReturn,
15
17
  type GetTokenBalancesWithPriceReturn,
18
+ type FetchTokenBalanceOnchainReturn,
16
19
  } from "./balance.fetchers.js"
20
+ import type { Token } from "../tokens.js"
17
21
  import { queryClient as defaultQueryClient } from "./client.js"
18
22
  import { logger } from "../logger.js"
19
23
 
@@ -143,6 +147,65 @@ export const balanceQueries = {
143
147
  Number(targetAmountUsd),
144
148
  })
145
149
  },
150
+
151
+ // single-token on-chain balance (RPC only, no price API)
152
+ onchain: (
153
+ token: Token | null | undefined,
154
+ accountAddress: string | null | undefined,
155
+ ) => {
156
+ const canFetch = !!token && !!token.chainId && !!accountAddress
157
+ return queryOptions<FetchTokenBalanceOnchainReturn>({
158
+ queryKey: [
159
+ ...balanceQueries.all,
160
+ "onchain",
161
+ accountAddress ?? null,
162
+ token?.contractAddress ?? null,
163
+ token?.chainId ?? null,
164
+ ] as const,
165
+ queryFn: canFetch
166
+ ? () =>
167
+ fetchTokenBalanceOnchain({
168
+ token,
169
+ accountAddress,
170
+ })
171
+ : skipToken,
172
+ staleTime: BALANCE_STALE_MS,
173
+ gcTime: BALANCE_GC_MS,
174
+ retry: createRetry(2),
175
+ retryDelay,
176
+ })
177
+ },
178
+
179
+ // single-token on-chain balance + price/USD enrichment (RPC + price API)
180
+ onchainWithPrices: (
181
+ token: Token | null | undefined,
182
+ accountAddress: string | null | undefined,
183
+ trailsClient: TrailsClient | null | undefined,
184
+ ) => {
185
+ const canFetch =
186
+ !!token && !!token.chainId && !!accountAddress && !!trailsClient
187
+ return queryOptions<FetchTokenBalanceOnchainReturn>({
188
+ queryKey: [
189
+ ...balanceQueries.all,
190
+ "onchainWithPrices",
191
+ accountAddress ?? null,
192
+ token?.contractAddress ?? null,
193
+ token?.chainId ?? null,
194
+ ] as const,
195
+ queryFn: canFetch
196
+ ? () =>
197
+ fetchTokenBalanceOnchainWithPrices({
198
+ token,
199
+ accountAddress,
200
+ trailsClient,
201
+ })
202
+ : skipToken,
203
+ staleTime: BALANCE_STALE_MS,
204
+ gcTime: BALANCE_GC_MS,
205
+ retry: createRetry(2),
206
+ retryDelay,
207
+ })
208
+ },
146
209
  }
147
210
 
148
211
  // ============================================================================
@@ -8,12 +8,7 @@ export const chainQueries = {
8
8
 
9
9
  supported: (config?: { trailsApiUrl?: string; trailsApiKey?: string }) =>
10
10
  queryOptions<Chain[]>({
11
- queryKey: [
12
- ...chainQueries.all,
13
- "supported",
14
- config?.trailsApiUrl || "default",
15
- config?.trailsApiKey || "",
16
- ] as const,
11
+ queryKey: [...chainQueries.all, "supported"] as const,
17
12
  queryFn: () => getSupportedChains(config),
18
13
  staleTime: HOUR_MS,
19
14
  gcTime: DAY_MS,
@@ -11,7 +11,7 @@ import type {
11
11
  MeldCryptoCurrency,
12
12
  MeldCountryDefault,
13
13
  MeldTransaction,
14
- } from "../onramp-client/trails-onramp.gen.js"
14
+ } from "@0xtrails/api/onramp"
15
15
  import { normalizeAddress } from "../utils/address.js"
16
16
 
17
17
  export interface MeldCountryDefaults {
@@ -1,6 +1,6 @@
1
1
  import { useQuery } from "@tanstack/react-query"
2
2
  import { useOnrampClient } from "../onrampClient.js"
3
- import type { GetMeldServiceProvidersRequest } from "../onramp-client/trails-onramp.gen.js"
3
+ import type { GetMeldServiceProvidersRequest } from "@0xtrails/api/onramp"
4
4
  import { meldQueries } from "./meld.queries.js"
5
5
 
6
6
  export function useMeldPaymentMethods(fiatCurrency?: string) {
@@ -3,7 +3,7 @@ import type { TrailsOnramp } from "../onrampClient.js"
3
3
  import type {
4
4
  GetMeldServiceProvidersRequest,
5
5
  MeldServiceProvider,
6
- } from "../onramp-client/trails-onramp.gen.js"
6
+ } from "@0xtrails/api/onramp"
7
7
  import { DAY_MS, MINUTE_MS } from "../utils/time.js"
8
8
  import { createRetry, retryDelay, STATIC_QUERY_OPTIONS } from "./helpers.js"
9
9
  import { queryPersister } from "./persister.js"
@@ -0,0 +1,118 @@
1
+ import { queryOptions, skipToken } from "@tanstack/react-query"
2
+ import type { TrailsClient } from "../trailsClient.js"
3
+ import { DAY_MS, MINUTE_MS, SECOND_MS } from "../utils/time.js"
4
+ import { createRetry, retryDelay, STATIC_QUERY_OPTIONS } from "./helpers.js"
5
+
6
+ type TokenListResult = {
7
+ tokens: unknown[]
8
+ }
9
+
10
+ const EMPTY_RESULT: TokenListResult = { tokens: [] }
11
+
12
+ export const tokenListQueries = {
13
+ all: ["tokenList"] as const,
14
+
15
+ catalog: (
16
+ trailsClient: TrailsClient | null | undefined,
17
+ config: { trailsApiUrl?: string; trailsApiKey?: string },
18
+ params: { chainIds: number[]; includeAllListed?: boolean; limit?: number },
19
+ ) => {
20
+ const hasChains = params.chainIds.length > 0
21
+ const canFetch = !!trailsClient && !!config.trailsApiKey && hasChains
22
+ return queryOptions<TokenListResult>({
23
+ queryKey: [
24
+ ...tokenListQueries.all,
25
+ "catalog",
26
+ [...params.chainIds].sort((a, b) => a - b).join(","),
27
+ params.includeAllListed ?? true,
28
+ params.limit ?? null,
29
+ ] as const,
30
+ queryFn: canFetch
31
+ ? async () => {
32
+ return (await trailsClient.getTokenList({
33
+ chainIds: params.chainIds,
34
+ includeAllListed: params.includeAllListed ?? true,
35
+ limit: params.limit,
36
+ })) as TokenListResult
37
+ }
38
+ : skipToken,
39
+ staleTime: DAY_MS,
40
+ gcTime: DAY_MS,
41
+ retry: createRetry(2),
42
+ retryDelay,
43
+ ...STATIC_QUERY_OPTIONS,
44
+ })
45
+ },
46
+
47
+ search: (
48
+ trailsClient: TrailsClient | null | undefined,
49
+ config: { trailsApiUrl?: string; trailsApiKey?: string },
50
+ params: { searchQuery: string; chainIds: number[]; limit?: number },
51
+ ) => {
52
+ const searchQuery = params.searchQuery.trim()
53
+ const canFetch =
54
+ !!trailsClient &&
55
+ !!config.trailsApiKey &&
56
+ searchQuery.length > 0 &&
57
+ params.chainIds.length > 0
58
+
59
+ return queryOptions<TokenListResult>({
60
+ queryKey: [
61
+ ...tokenListQueries.all,
62
+ "search",
63
+ searchQuery,
64
+ [...params.chainIds].sort((a, b) => a - b).join(","),
65
+ params.limit ?? 30,
66
+ ] as const,
67
+ queryFn: canFetch
68
+ ? async () => {
69
+ return (await trailsClient.getTokenList({
70
+ searchQuery,
71
+ chainIds: params.chainIds,
72
+ includeAllListed: true,
73
+ limit: params.limit ?? 30,
74
+ })) as TokenListResult
75
+ }
76
+ : skipToken,
77
+ staleTime: 30 * SECOND_MS,
78
+ gcTime: 5 * MINUTE_MS,
79
+ retry: createRetry(1),
80
+ retryDelay,
81
+ refetchOnWindowFocus: false,
82
+ refetchOnReconnect: false,
83
+ })
84
+ },
85
+
86
+ byChain: (
87
+ trailsClient: TrailsClient | null | undefined,
88
+ config: { trailsApiUrl?: string; trailsApiKey?: string },
89
+ params: { chainId: number | null; limit?: number },
90
+ ) => {
91
+ const canFetch =
92
+ !!trailsClient && !!config.trailsApiKey && params.chainId != null
93
+ return queryOptions<TokenListResult>({
94
+ queryKey: [
95
+ ...tokenListQueries.all,
96
+ "byChain",
97
+ params.chainId,
98
+ params.limit ?? 100,
99
+ ] as const,
100
+ queryFn: canFetch
101
+ ? async () => {
102
+ return (await trailsClient.getTokenList({
103
+ chainIds: [params.chainId as number],
104
+ includeAllListed: true,
105
+ limit: params.limit ?? 100,
106
+ })) as TokenListResult
107
+ }
108
+ : skipToken,
109
+ staleTime: DAY_MS,
110
+ gcTime: DAY_MS,
111
+ retry: createRetry(2),
112
+ retryDelay,
113
+ ...STATIC_QUERY_OPTIONS,
114
+ })
115
+ },
116
+
117
+ empty: EMPTY_RESULT,
118
+ }