0xtrails 0.13.0 → 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 (307) hide show
  1. package/dist/{ccip-Cg9-lJ6K.js → ccip-CT_An6eM.js} +39 -39
  2. package/dist/chains.d.ts +4 -3
  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/error.d.ts +1 -0
  8. package/dist/error.d.ts.map +1 -1
  9. package/dist/gasless.d.ts +1 -2
  10. package/dist/gasless.d.ts.map +1 -1
  11. package/dist/{index-DEojZg7b.js → index-RfqL5Foz.js} +56672 -43550
  12. package/dist/index.d.ts +5 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +385 -333
  15. package/dist/intents.d.ts +8 -2
  16. package/dist/intents.d.ts.map +1 -1
  17. package/dist/keyMachineClient.d.ts +9 -0
  18. package/dist/keyMachineClient.d.ts.map +1 -0
  19. package/dist/keymachine/index.d.ts +14 -0
  20. package/dist/keymachine/index.d.ts.map +1 -0
  21. package/dist/keymachine/key-machine.gen.d.ts +461 -0
  22. package/dist/keymachine/key-machine.gen.d.ts.map +1 -0
  23. package/dist/onramp/MeshConnectFlow.d.ts +18 -0
  24. package/dist/onramp/MeshConnectFlow.d.ts.map +1 -0
  25. package/dist/onramp/MeshConnectIframe.d.ts +13 -0
  26. package/dist/onramp/MeshConnectIframe.d.ts.map +1 -0
  27. package/dist/onramp/SendFromExchangeButton.d.ts +16 -0
  28. package/dist/onramp/SendFromExchangeButton.d.ts.map +1 -0
  29. package/dist/onramp/TrailsOnRampProvider.d.ts +31 -0
  30. package/dist/onramp/TrailsOnRampProvider.d.ts.map +1 -0
  31. package/dist/onramp/index.d.ts +13 -0
  32. package/dist/onramp/index.d.ts.map +1 -0
  33. package/dist/onramp/meshconnect.d.ts +30 -0
  34. package/dist/onramp/meshconnect.d.ts.map +1 -0
  35. package/dist/onramp/trailsOnramp.d.ts +24 -0
  36. package/dist/onramp/trailsOnramp.d.ts.map +1 -0
  37. package/dist/onramp-client/index.d.ts +3 -3
  38. package/dist/onramp-client/index.d.ts.map +1 -1
  39. package/dist/paymasterSend.d.ts.map +1 -1
  40. package/dist/prepareSend.d.ts.map +1 -1
  41. package/dist/query/balance.fetchers.d.ts +31 -2
  42. package/dist/query/balance.fetchers.d.ts.map +1 -1
  43. package/dist/query/balance.hooks.d.ts +21 -2
  44. package/dist/query/balance.hooks.d.ts.map +1 -1
  45. package/dist/query/balance.queries.d.ts +18 -1
  46. package/dist/query/balance.queries.d.ts.map +1 -1
  47. package/dist/query/chains.queries.d.ts.map +1 -1
  48. package/dist/query/meld.fetchers.d.ts +1 -1
  49. package/dist/query/meld.fetchers.d.ts.map +1 -1
  50. package/dist/query/meld.hooks.d.ts +3 -3
  51. package/dist/query/meld.hooks.d.ts.map +1 -1
  52. package/dist/query/meld.queries.d.ts +1 -1
  53. package/dist/query/meld.queries.d.ts.map +1 -1
  54. package/dist/query/price.fetchers.d.ts +15 -0
  55. package/dist/query/price.fetchers.d.ts.map +1 -0
  56. package/dist/query/price.hooks.d.ts +352 -0
  57. package/dist/query/price.hooks.d.ts.map +1 -0
  58. package/dist/query/price.queries.d.ts +34 -0
  59. package/dist/query/price.queries.d.ts.map +1 -0
  60. package/dist/query/tokenList.queries.d.ts +54 -0
  61. package/dist/query/tokenList.queries.d.ts.map +1 -0
  62. package/dist/recover.d.ts +6 -4
  63. package/dist/recover.d.ts.map +1 -1
  64. package/dist/tokens.d.ts +13 -0
  65. package/dist/tokens.d.ts.map +1 -1
  66. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +2 -2
  67. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  68. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +2 -2
  69. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  70. package/dist/transactionIntent/deposits/standardDeposit.d.ts +1 -1
  71. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  72. package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
  73. package/dist/transactionIntent/helpers/transactionStateHelpers.d.ts.map +1 -1
  74. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  75. package/dist/transactionIntent/types.d.ts +1 -1
  76. package/dist/transactionIntent/types.d.ts.map +1 -1
  77. package/dist/transactions.d.ts +4 -0
  78. package/dist/transactions.d.ts.map +1 -1
  79. package/dist/umd/trails.min.js +291 -202
  80. package/dist/utils/format.d.ts +7 -0
  81. package/dist/utils/format.d.ts.map +1 -1
  82. package/dist/walletUtils.d.ts +2 -1
  83. package/dist/walletUtils.d.ts.map +1 -1
  84. package/dist/wallets.d.ts +13 -54
  85. package/dist/wallets.d.ts.map +1 -1
  86. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  87. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  88. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  89. package/dist/widget/components/ConnectedWallets.d.ts.map +1 -1
  90. package/dist/widget/components/DirectTransfer.d.ts +1 -1
  91. package/dist/widget/components/DirectTransfer.d.ts.map +1 -1
  92. package/dist/widget/components/EarnPools.d.ts.map +1 -1
  93. package/dist/widget/components/ExecutionStatusBadge.d.ts.map +1 -1
  94. package/dist/widget/components/Fund.d.ts.map +1 -1
  95. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  96. package/dist/widget/components/HighPriceImpactBlock.d.ts +7 -0
  97. package/dist/widget/components/HighPriceImpactBlock.d.ts.map +1 -0
  98. package/dist/widget/components/MeldHistory.d.ts.map +1 -1
  99. package/dist/widget/components/MeshExchangeSelection.d.ts +11 -0
  100. package/dist/widget/components/MeshExchangeSelection.d.ts.map +1 -0
  101. package/dist/widget/components/OnrampHistoryRow.d.ts +1 -1
  102. package/dist/widget/components/OnrampHistoryRow.d.ts.map +1 -1
  103. package/dist/widget/components/OnrampProviderConfirmation.d.ts.map +1 -1
  104. package/dist/widget/components/Pay.d.ts.map +1 -1
  105. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  106. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  107. package/dist/widget/components/QRCodeWalletSelect.d.ts +1 -1
  108. package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -1
  109. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  110. package/dist/widget/components/Receipt.d.ts.map +1 -1
  111. package/dist/widget/components/Recipients.d.ts.map +1 -1
  112. package/dist/widget/components/RefundWarning.d.ts.map +1 -1
  113. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  114. package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
  115. package/dist/widget/components/TransactionHistoryItem.d.ts +2 -0
  116. package/dist/widget/components/TransactionHistoryItem.d.ts.map +1 -1
  117. package/dist/widget/components/TransferPendingVertical.d.ts +1 -0
  118. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  119. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  120. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  121. package/dist/widget/components/WalletImage.d.ts.map +1 -1
  122. package/dist/widget/components/WalletList.d.ts.map +1 -1
  123. package/dist/widget/components/Withdraw.d.ts.map +1 -1
  124. package/dist/widget/css/compiled.css +1 -1
  125. package/dist/widget/hooks/useAddressWalletIcon.d.ts.map +1 -1
  126. package/dist/widget/hooks/useCombinedHistory.d.ts +6 -5
  127. package/dist/widget/hooks/useCombinedHistory.d.ts.map +1 -1
  128. package/dist/widget/hooks/useCustomTokenSearch.d.ts +6 -1
  129. package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -1
  130. package/dist/widget/hooks/useDefaultDestinationToken.d.ts.map +1 -1
  131. package/dist/widget/hooks/useDefaultOriginToken.d.ts.map +1 -1
  132. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts +1 -1
  133. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts.map +1 -1
  134. package/dist/widget/hooks/useGetIntent.d.ts +3 -2
  135. package/dist/widget/hooks/useGetIntent.d.ts.map +1 -1
  136. package/dist/widget/hooks/useIntentReceiptBalances.d.ts +1 -1
  137. package/dist/widget/hooks/useIntentReceiptBalances.d.ts.map +1 -1
  138. package/dist/widget/hooks/useIntentTransactionHistory.d.ts +3 -2
  139. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  140. package/dist/widget/hooks/useMeldTransactionHistory.d.ts +1 -1
  141. package/dist/widget/hooks/useMeldTransactionHistory.d.ts.map +1 -1
  142. package/dist/widget/hooks/useMeldTransactionStatus.d.ts +1 -1
  143. package/dist/widget/hooks/useMeldTransactionStatus.d.ts.map +1 -1
  144. package/dist/widget/hooks/useOnRampQuote.d.ts +1 -1
  145. package/dist/widget/hooks/useOnRampQuote.d.ts.map +1 -1
  146. package/dist/widget/hooks/useOnRampTransactionStatus.d.ts +1 -1
  147. package/dist/widget/hooks/useOnRampTransactionStatus.d.ts.map +1 -1
  148. package/dist/widget/hooks/useQuote.d.ts +2 -2
  149. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  150. package/dist/widget/hooks/useSelectedFundMethod.d.ts +7 -0
  151. package/dist/widget/hooks/useSelectedFundMethod.d.ts.map +1 -1
  152. package/dist/widget/hooks/useSendForm.d.ts +0 -1
  153. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  154. package/dist/widget/hooks/useTokenList.d.ts +7 -1
  155. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  156. package/dist/widget/hooks/useViewManager.d.ts +1 -1
  157. package/dist/widget/hooks/useViewManager.d.ts.map +1 -1
  158. package/dist/widget/index.js +1 -1
  159. package/dist/widget/providers/TrailsProvider.d.ts +2 -0
  160. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  161. package/dist/widget/utils/createWagmiConfig.d.ts +2 -2
  162. package/dist/widget/utils/createWagmiConfig.d.ts.map +1 -1
  163. package/dist/widget/utils/fundMethodSwitchState.d.ts +1 -0
  164. package/dist/widget/utils/fundMethodSwitchState.d.ts.map +1 -1
  165. package/dist/widget/utils/historyFilters.d.ts +13 -0
  166. package/dist/widget/utils/historyFilters.d.ts.map +1 -0
  167. package/dist/widget/utils/meldProviderUtils.d.ts +1 -1
  168. package/dist/widget/utils/meldProviderUtils.d.ts.map +1 -1
  169. package/dist/widget/utils/meshSupportedTokens.d.ts +4 -0
  170. package/dist/widget/utils/meshSupportedTokens.d.ts.map +1 -0
  171. package/dist/widget/utils/onrampConfig.d.ts +11 -0
  172. package/dist/widget/utils/onrampConfig.d.ts.map +1 -0
  173. package/dist/widget/utils/statusLabel.d.ts +2 -0
  174. package/dist/widget/utils/statusLabel.d.ts.map +1 -0
  175. package/dist/widget/utils/trailsOnrampConfig.d.ts +18 -0
  176. package/dist/widget/utils/trailsOnrampConfig.d.ts.map +1 -0
  177. package/dist/widget/widget.d.ts +24 -8
  178. package/dist/widget/widget.d.ts.map +1 -1
  179. package/package.json +9 -7
  180. package/src/chains.ts +26 -9
  181. package/src/constants.ts +2 -0
  182. package/src/customTokens.ts +22 -7
  183. package/src/error.ts +7 -0
  184. package/src/gasless.ts +5 -2
  185. package/src/index.ts +8 -5
  186. package/src/intents.ts +56 -60
  187. package/src/keyMachineClient.ts +29 -0
  188. package/src/keymachine/index.ts +175 -0
  189. package/src/keymachine/key-machine.gen.ts +993 -0
  190. package/src/onramp/MeshConnectFlow.tsx +86 -0
  191. package/src/onramp/MeshConnectIframe.tsx +661 -0
  192. package/src/onramp/SendFromExchangeButton.tsx +81 -0
  193. package/src/onramp/TrailsOnRampProvider.tsx +59 -0
  194. package/src/onramp/index.ts +31 -0
  195. package/src/onramp/meshconnect.ts +277 -0
  196. package/src/onramp/trailsOnramp.tsx +130 -0
  197. package/src/onramp-client/index.ts +4 -6
  198. package/src/paymasterSend.ts +0 -5
  199. package/src/prepareSend.ts +45 -44
  200. package/src/query/balance.fetchers.ts +172 -17
  201. package/src/query/balance.hooks.ts +69 -6
  202. package/src/query/balance.queries.ts +63 -0
  203. package/src/query/chains.queries.ts +1 -6
  204. package/src/query/meld.fetchers.ts +1 -1
  205. package/src/query/meld.hooks.ts +1 -1
  206. package/src/query/meld.queries.ts +1 -1
  207. package/src/query/price.fetchers.ts +53 -0
  208. package/src/query/price.hooks.ts +46 -0
  209. package/src/query/price.queries.ts +364 -0
  210. package/src/query/tokenList.queries.ts +118 -0
  211. package/src/recover.ts +89 -26
  212. package/src/tokens.ts +108 -26
  213. package/src/transactionIntent/deposits/depositOrchestrator.ts +11 -11
  214. package/src/transactionIntent/deposits/gaslessDeposit.ts +38 -39
  215. package/src/transactionIntent/deposits/standardDeposit.ts +5 -30
  216. package/src/transactionIntent/handlers/intentHandler.ts +29 -12
  217. package/src/transactionIntent/helpers/transactionStateHelpers.ts +5 -2
  218. package/src/transactionIntent/quote/normalizeQuote.ts +11 -5
  219. package/src/transactionIntent/types.ts +1 -1
  220. package/src/transactions.ts +5 -1
  221. package/src/utils/format.ts +85 -1
  222. package/src/walletUtils.ts +2 -1
  223. package/src/wallets.ts +184 -380
  224. package/src/widget/compiled.css +1 -1
  225. package/src/widget/components/AccountIntentTransactionHistory.tsx +134 -109
  226. package/src/widget/components/ClassicSwap.tsx +26 -24
  227. package/src/widget/components/ConnectWallet.tsx +4 -2
  228. package/src/widget/components/ConnectedWallets.tsx +2 -5
  229. package/src/widget/components/DirectTransfer.tsx +5 -2
  230. package/src/widget/components/EarnPools.tsx +1 -2
  231. package/src/widget/components/ExecutionStatusBadge.tsx +10 -4
  232. package/src/widget/components/Fund.tsx +169 -110
  233. package/src/widget/components/FundMethods.tsx +5 -9
  234. package/src/widget/components/HighPriceImpactBlock.tsx +44 -0
  235. package/src/widget/components/MeldHistory.tsx +4 -28
  236. package/src/widget/components/MeshExchangeSelection.tsx +218 -0
  237. package/src/widget/components/OnrampHistoryRow.tsx +3 -27
  238. package/src/widget/components/OnrampProviderConfirmation.tsx +0 -25
  239. package/src/widget/components/Pay.tsx +20 -36
  240. package/src/widget/components/PoolDeposit.tsx +14 -24
  241. package/src/widget/components/PoolWithdraw.tsx +1 -63
  242. package/src/widget/components/QRCodeWalletSelect.tsx +5 -2
  243. package/src/widget/components/QuoteDetails.tsx +113 -106
  244. package/src/widget/components/Receipt.tsx +0 -11
  245. package/src/widget/components/Recipients.tsx +2 -1
  246. package/src/widget/components/RefundWarning.tsx +5 -10
  247. package/src/widget/components/ThemeProvider.tsx +4 -4
  248. package/src/widget/components/TokenSelector.tsx +85 -16
  249. package/src/widget/components/TransactionDetails.tsx +46 -0
  250. package/src/widget/components/TransactionHistoryItem.tsx +14 -23
  251. package/src/widget/components/TransferPendingVertical.tsx +17 -11
  252. package/src/widget/components/WaasFeeOptions.tsx +4 -42
  253. package/src/widget/components/WalletConnect.tsx +2 -5
  254. package/src/widget/components/WalletImage.tsx +6 -18
  255. package/src/widget/components/WalletList.tsx +1 -1
  256. package/src/widget/components/Withdraw.tsx +22 -23
  257. package/src/widget/hooks/useAddressWalletIcon.ts +2 -1
  258. package/src/widget/hooks/useAmountUsd.ts +1 -1
  259. package/src/widget/hooks/useCombinedHistory.ts +37 -93
  260. package/src/widget/hooks/useCustomTokenSearch.tsx +63 -33
  261. package/src/widget/hooks/useDefaultDestinationToken.tsx +2 -5
  262. package/src/widget/hooks/useDefaultOriginToken.tsx +2 -5
  263. package/src/widget/hooks/useFiatOnRampCurrencies.ts +1 -1
  264. package/src/widget/hooks/useGetIntent.ts +5 -4
  265. package/src/widget/hooks/useIntentReceiptBalances.ts +3 -3
  266. package/src/widget/hooks/useIntentTransactionHistory.ts +24 -47
  267. package/src/widget/hooks/useMeldTransactionHistory.ts +4 -2
  268. package/src/widget/hooks/useMeldTransactionStatus.ts +13 -11
  269. package/src/widget/hooks/useOnRampQuote.ts +3 -3
  270. package/src/widget/hooks/useOnRampTransactionStatus.ts +8 -6
  271. package/src/widget/hooks/useQuote.ts +56 -48
  272. package/src/widget/hooks/useSelectedFundMethod.tsx +14 -1
  273. package/src/widget/hooks/useSendForm.ts +52 -31
  274. package/src/widget/hooks/useTokenList.ts +209 -140
  275. package/src/widget/hooks/useTrailsSendTransaction.ts +1 -1
  276. package/src/widget/hooks/useViewManager.tsx +1 -0
  277. package/src/widget/providers/TrailsProvider.tsx +5 -0
  278. package/src/widget/styles.ts +1 -1
  279. package/src/widget/utils/createWagmiConfig.ts +7 -2
  280. package/src/widget/utils/fundMethodSwitchState.ts +2 -0
  281. package/src/widget/utils/historyFilters.ts +157 -0
  282. package/src/widget/utils/meldProviderUtils.ts +8 -2
  283. package/src/widget/utils/meshSupportedTokens.ts +28 -0
  284. package/src/widget/utils/onrampConfig.ts +15 -0
  285. package/src/widget/utils/statusLabel.ts +3 -0
  286. package/src/widget/utils/trailsOnrampConfig.ts +39 -0
  287. package/src/widget/widget.tsx +235 -185
  288. package/dist/onramp-client/trails-onramp.gen.d.ts +0 -570
  289. package/dist/onramp-client/trails-onramp.gen.d.ts.map +0 -1
  290. package/dist/prices.d.ts +0 -34
  291. package/dist/prices.d.ts.map +0 -1
  292. package/dist/useGasEstimation.d.ts +0 -34
  293. package/dist/useGasEstimation.d.ts.map +0 -1
  294. package/dist/widget/hooks/useCustomTokenFetch.d.ts +0 -19
  295. package/dist/widget/hooks/useCustomTokenFetch.d.ts.map +0 -1
  296. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts +0 -18
  297. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts.map +0 -1
  298. package/src/onramp-client/trails-onramp.gen.ts +0 -1320
  299. package/src/prices.ts +0 -528
  300. package/src/useGasEstimation.ts +0 -147
  301. package/src/widget/assets/Binance_Icon_Logo.svg +0 -14
  302. package/src/widget/assets/Bitfinex_Icon_Logo.svg +0 -5
  303. package/src/widget/assets/Coinbase_Icon_Logo.svg +0 -1
  304. package/src/widget/assets/WalletConnect-logo-blue-bg.svg +0 -11
  305. package/src/widget/assets/sequence-logo.svg +0 -15
  306. package/src/widget/hooks/useCustomTokenFetch.tsx +0 -74
  307. package/src/widget/hooks/useTokenWithFreshBalance.ts +0 -246
@@ -0,0 +1,53 @@
1
+ import type { Token, TokenPrice } from "@0xtrails/api"
2
+ import { zeroAddress } from "viem"
3
+ import type { TrailsClient } from "../trailsClient.js"
4
+ import { logger } from "../logger.js"
5
+
6
+ export type FetchTokenPricesParams = {
7
+ trailsClient: TrailsClient
8
+ tokens: Token[]
9
+ }
10
+
11
+ export function normalizeToken(
12
+ token: Token & {
13
+ tokenID?: string
14
+ symbol?: string
15
+ contractAddress?: string
16
+ tokenId?: string
17
+ },
18
+ ): Token {
19
+ return {
20
+ chainId: token.chainId,
21
+ tokenAddress: token.tokenAddress || token.contractAddress || zeroAddress,
22
+ tokenSymbol:
23
+ token.tokenSymbol || token.tokenId || token.tokenID || token.symbol,
24
+ }
25
+ }
26
+
27
+ export function createTokenCacheKey(token: Token): string {
28
+ const tokenAddress = (token.tokenAddress || zeroAddress).toLowerCase()
29
+ const chainId = Number(token.chainId || 0)
30
+ const tokenSymbol = (token.tokenSymbol || "").toUpperCase()
31
+ return `${tokenAddress}-${chainId}-${tokenSymbol}`
32
+ }
33
+
34
+ export async function fetchTokenPrices(
35
+ trailsClient: TrailsClient,
36
+ tokens: Token[],
37
+ ): Promise<TokenPrice[]> {
38
+ if (!trailsClient) {
39
+ throw new Error("Trails client is required")
40
+ }
41
+
42
+ if (tokens.length === 0) {
43
+ return []
44
+ }
45
+
46
+ try {
47
+ const res = await trailsClient.getTokenPrices({ tokens })
48
+ return res?.tokenPrices || []
49
+ } catch (error) {
50
+ logger.console.error("[trails-sdk] Failed to fetch token prices:", error)
51
+ throw error
52
+ }
53
+ }
@@ -0,0 +1,46 @@
1
+ import type { Token } from "@0xtrails/api"
2
+ import { useQuery, useQueryClient } from "@tanstack/react-query"
3
+ import { SECOND_MS } from "../utils/time.js"
4
+ import { useTrailsClient } from "../trailsClient.js"
5
+ import { useUserActivityContext } from "../widget/providers/UserActivityProvider.js"
6
+ import { getTokenPriceSeedFromBatches, priceQueries } from "./price.queries.js"
7
+
8
+ const PRICE_REFETCH_INTERVAL = 90 * SECOND_MS
9
+
10
+ export function useTokenPrices(tokens: Token[], disabled?: boolean) {
11
+ const trailsClient = useTrailsClient()
12
+ const queryClient = useQueryClient()
13
+ const { isUserActive } = useUserActivityContext()
14
+
15
+ const query = useQuery({
16
+ ...priceQueries.byTokens(tokens, trailsClient, queryClient),
17
+ enabled: !disabled,
18
+ refetchInterval: isUserActive ? PRICE_REFETCH_INTERVAL : false,
19
+ })
20
+
21
+ return {
22
+ ...query,
23
+ tokenPrices: query.data,
24
+ isLoadingTokenPrices: query.isLoading,
25
+ }
26
+ }
27
+
28
+ export function useTokenPrice(token?: Token | null) {
29
+ const trailsClient = useTrailsClient()
30
+ const queryClient = useQueryClient()
31
+ const { isUserActive } = useUserActivityContext()
32
+ const seed = getTokenPriceSeedFromBatches(queryClient, token ?? null)
33
+
34
+ const query = useQuery({
35
+ ...priceQueries.byToken(token ?? null, trailsClient, queryClient),
36
+ initialData: seed.price,
37
+ initialDataUpdatedAt: seed.updatedAt,
38
+ refetchInterval: isUserActive ? PRICE_REFETCH_INTERVAL : false,
39
+ })
40
+
41
+ return {
42
+ ...query,
43
+ tokenPrice: query.data,
44
+ isLoadingTokenPrice: query.isLoading,
45
+ }
46
+ }
@@ -0,0 +1,364 @@
1
+ import {
2
+ queryOptions,
3
+ skipToken,
4
+ type QueryClient,
5
+ type QueryKey,
6
+ } from "@tanstack/react-query"
7
+ import type { Token, TokenPrice } from "@0xtrails/api"
8
+ import type { TrailsClient } from "../trailsClient.js"
9
+ import { SECOND_MS } from "../utils/time.js"
10
+ import { queryClient as defaultQueryClient } from "./client.js"
11
+ import { createRetry, retryDelay } from "./helpers.js"
12
+ import { queryPersister } from "./persister.js"
13
+ import {
14
+ createTokenCacheKey,
15
+ fetchTokenPrices,
16
+ normalizeToken,
17
+ } from "./price.fetchers.js"
18
+
19
+ const PRICE_SCOPE = "tokenPrices"
20
+ const PRICE_BATCH_SCOPE = "batch"
21
+ const PRICE_SINGLE_SCOPE = "single"
22
+
23
+ const PRICE_STALE_MS = 90 * SECOND_MS
24
+ const PRICE_GC_MS = 90 * SECOND_MS
25
+
26
+ type TokenPriceSeed = {
27
+ price: TokenPrice | null | undefined
28
+ updatedAt?: number
29
+ }
30
+
31
+ type MatchingBatchQuery = {
32
+ queryKey: QueryKey
33
+ tokens: Token[]
34
+ data?: TokenPrice[]
35
+ dataUpdatedAt: number
36
+ fetchStatus: string
37
+ }
38
+
39
+ function normalizeTokensForQuery(tokens: Token[]): Token[] {
40
+ const uniqueTokens = new Map<string, Token>()
41
+
42
+ for (const token of tokens) {
43
+ const normalized = normalizeToken(token)
44
+ const tokenKey = createTokenCacheKey(normalized)
45
+
46
+ if (!uniqueTokens.has(tokenKey)) {
47
+ uniqueTokens.set(tokenKey, normalized)
48
+ }
49
+ }
50
+
51
+ return Array.from(uniqueTokens.values()).sort((left, right) =>
52
+ createTokenCacheKey(left).localeCompare(createTokenCacheKey(right)),
53
+ )
54
+ }
55
+
56
+ function isTokenLike(value: unknown): value is Token {
57
+ return (
58
+ typeof value === "object" &&
59
+ value !== null &&
60
+ "chainId" in value &&
61
+ typeof value.chainId === "number" &&
62
+ "tokenAddress" in value &&
63
+ typeof value.tokenAddress === "string" &&
64
+ (!("tokenSymbol" in value) || typeof value.tokenSymbol === "string")
65
+ )
66
+ }
67
+
68
+ function getBatchQueryTokens(queryKey: QueryKey): Token[] | null {
69
+ if (queryKey[0] !== PRICE_SCOPE || queryKey[1] !== PRICE_BATCH_SCOPE) {
70
+ return null
71
+ }
72
+
73
+ const tokens = queryKey[2]
74
+ if (!Array.isArray(tokens) || !tokens.every(isTokenLike)) {
75
+ return null
76
+ }
77
+
78
+ return [...tokens]
79
+ }
80
+
81
+ function isFreshBatchData(dataUpdatedAt: number): boolean {
82
+ if (!dataUpdatedAt) {
83
+ return false
84
+ }
85
+
86
+ return Date.now() - dataUpdatedAt < PRICE_STALE_MS
87
+ }
88
+
89
+ function findTokenPrice(
90
+ prices: TokenPrice[] | undefined,
91
+ tokenKey: string,
92
+ ): TokenPrice | null | undefined {
93
+ if (!prices) {
94
+ return undefined
95
+ }
96
+
97
+ return (
98
+ prices.find(
99
+ (price) => createTokenCacheKey(normalizeToken(price.token)) === tokenKey,
100
+ ) ?? null
101
+ )
102
+ }
103
+
104
+ function getMatchingBatchQueries(
105
+ queryClient: QueryClient,
106
+ tokenKey: string,
107
+ ): MatchingBatchQuery[] {
108
+ return queryClient
109
+ .getQueryCache()
110
+ .findAll({ queryKey: priceQueries.batch() })
111
+ .flatMap((query) => {
112
+ const tokens = getBatchQueryTokens(query.queryKey)
113
+
114
+ if (
115
+ !tokens ||
116
+ !tokens.some((token) => createTokenCacheKey(token) === tokenKey)
117
+ ) {
118
+ return []
119
+ }
120
+
121
+ return [
122
+ {
123
+ queryKey: query.queryKey,
124
+ tokens,
125
+ data: Array.isArray(query.state.data)
126
+ ? (query.state.data as TokenPrice[])
127
+ : undefined,
128
+ dataUpdatedAt: query.state.dataUpdatedAt,
129
+ fetchStatus: query.state.fetchStatus,
130
+ },
131
+ ]
132
+ })
133
+ }
134
+
135
+ function seedSingleTokenPriceQueries({
136
+ queryClient,
137
+ trailsClient,
138
+ tokens,
139
+ prices,
140
+ }: {
141
+ queryClient: QueryClient
142
+ trailsClient: TrailsClient
143
+ tokens: Token[]
144
+ prices: TokenPrice[]
145
+ }) {
146
+ const priceMap = new Map(
147
+ prices.map((price) => [
148
+ createTokenCacheKey(normalizeToken(price.token)),
149
+ price,
150
+ ]),
151
+ )
152
+
153
+ for (const token of tokens) {
154
+ const tokenKey = createTokenCacheKey(token)
155
+ queryClient.setQueryData<TokenPrice | null>(
156
+ priceQueries.byToken(token, trailsClient, queryClient).queryKey,
157
+ priceMap.get(tokenKey) ?? null,
158
+ )
159
+ }
160
+ }
161
+
162
+ export function getTokenPriceSeedFromBatches(
163
+ queryClient: QueryClient,
164
+ token: Token | null | undefined,
165
+ ): TokenPriceSeed {
166
+ if (!token) {
167
+ return {
168
+ price: undefined,
169
+ updatedAt: undefined,
170
+ }
171
+ }
172
+
173
+ const normalizedToken = normalizeToken(token)
174
+ const tokenKey = createTokenCacheKey(normalizedToken)
175
+
176
+ const freshestMatch = getMatchingBatchQueries(queryClient, tokenKey)
177
+ .filter((match) => match.data !== undefined)
178
+ .sort((left, right) => right.dataUpdatedAt - left.dataUpdatedAt)[0]
179
+
180
+ if (!freshestMatch?.data) {
181
+ return {
182
+ price: undefined,
183
+ updatedAt: undefined,
184
+ }
185
+ }
186
+
187
+ return {
188
+ price: findTokenPrice(freshestMatch.data, tokenKey),
189
+ updatedAt: freshestMatch.dataUpdatedAt || undefined,
190
+ }
191
+ }
192
+
193
+ async function fetchTokenPriceWithBatchReuse({
194
+ queryClient,
195
+ trailsClient,
196
+ token,
197
+ }: {
198
+ queryClient: QueryClient
199
+ trailsClient: TrailsClient
200
+ token: Token
201
+ }): Promise<TokenPrice | null> {
202
+ const tokenKey = createTokenCacheKey(token)
203
+
204
+ const fetchingMatch = getMatchingBatchQueries(queryClient, tokenKey)
205
+ .filter((match) => match.fetchStatus === "fetching")
206
+ .sort((left, right) => left.tokens.length - right.tokens.length)[0]
207
+
208
+ if (fetchingMatch) {
209
+ try {
210
+ const prices = await queryClient.fetchQuery(
211
+ priceQueries.byTokens(fetchingMatch.tokens, trailsClient, queryClient),
212
+ )
213
+
214
+ return findTokenPrice(prices, tokenKey) ?? null
215
+ } catch {
216
+ // Fall through to an isolated single-token fetch.
217
+ }
218
+ }
219
+
220
+ const freshestBatchMatch = getMatchingBatchQueries(queryClient, tokenKey)
221
+ .filter(
222
+ (match) =>
223
+ match.data !== undefined && isFreshBatchData(match.dataUpdatedAt),
224
+ )
225
+ .sort((left, right) => right.dataUpdatedAt - left.dataUpdatedAt)[0]
226
+
227
+ if (freshestBatchMatch?.data) {
228
+ return findTokenPrice(freshestBatchMatch.data, tokenKey) ?? null
229
+ }
230
+
231
+ const prices = await fetchTokenPrices(trailsClient, [token])
232
+ return prices[0] ?? null
233
+ }
234
+
235
+ function matchesTokenPriceQuery(queryKey: QueryKey, tokenKey: string): boolean {
236
+ if (queryKey[0] !== PRICE_SCOPE) {
237
+ return false
238
+ }
239
+
240
+ if (queryKey[1] === PRICE_SINGLE_SCOPE) {
241
+ return queryKey[2] === tokenKey
242
+ }
243
+
244
+ const batchTokens = getBatchQueryTokens(queryKey)
245
+ return (
246
+ batchTokens?.some(
247
+ (batchToken) => createTokenCacheKey(batchToken) === tokenKey,
248
+ ) ?? false
249
+ )
250
+ }
251
+
252
+ export const priceQueries = {
253
+ all: [PRICE_SCOPE] as const,
254
+ batch: () => [...priceQueries.all, PRICE_BATCH_SCOPE] as const,
255
+ single: () => [...priceQueries.all, PRICE_SINGLE_SCOPE] as const,
256
+ byTokens: (
257
+ tokens: Token[],
258
+ trailsClient: TrailsClient | null | undefined,
259
+ queryClient: QueryClient = defaultQueryClient,
260
+ ) => {
261
+ const normalizedTokens = normalizeTokensForQuery(tokens)
262
+ const canFetch = normalizedTokens.length > 0 && !!trailsClient
263
+
264
+ return queryOptions<TokenPrice[]>({
265
+ queryKey: [...priceQueries.batch(), normalizedTokens] as const,
266
+ queryFn: canFetch
267
+ ? async () => {
268
+ if (!trailsClient) {
269
+ throw new Error("Trails client is required")
270
+ }
271
+
272
+ const prices = await fetchTokenPrices(
273
+ trailsClient,
274
+ normalizedTokens,
275
+ )
276
+
277
+ seedSingleTokenPriceQueries({
278
+ queryClient,
279
+ trailsClient,
280
+ tokens: normalizedTokens,
281
+ prices,
282
+ })
283
+
284
+ return prices
285
+ }
286
+ : skipToken,
287
+ staleTime: PRICE_STALE_MS,
288
+ gcTime: PRICE_GC_MS,
289
+ retry: createRetry(2),
290
+ retryDelay,
291
+ persister: queryPersister.persisterFn,
292
+ })
293
+ },
294
+ byToken: (
295
+ token: Token | null | undefined,
296
+ trailsClient: TrailsClient | null | undefined,
297
+ queryClient: QueryClient = defaultQueryClient,
298
+ ) => {
299
+ const normalizedToken = token ? normalizeToken(token) : null
300
+ const tokenKey = normalizedToken
301
+ ? createTokenCacheKey(normalizedToken)
302
+ : null
303
+ const canFetch = !!normalizedToken && !!trailsClient
304
+
305
+ return queryOptions<TokenPrice | null>({
306
+ queryKey: [...priceQueries.single(), tokenKey] as const,
307
+ queryFn: canFetch
308
+ ? async () => {
309
+ if (!normalizedToken || !trailsClient) {
310
+ throw new Error(
311
+ "Token price query requires a token and Trails client",
312
+ )
313
+ }
314
+
315
+ return fetchTokenPriceWithBatchReuse({
316
+ queryClient,
317
+ trailsClient,
318
+ token: normalizedToken,
319
+ })
320
+ }
321
+ : skipToken,
322
+ staleTime: PRICE_STALE_MS,
323
+ gcTime: PRICE_GC_MS,
324
+ retry: createRetry(2),
325
+ retryDelay,
326
+ persister: queryPersister.persisterFn,
327
+ })
328
+ },
329
+ }
330
+
331
+ export function invalidateTokenPricesCache(token?: Token, qc?: QueryClient) {
332
+ const client = qc ?? defaultQueryClient
333
+
334
+ if (!token) {
335
+ return client.invalidateQueries({
336
+ queryKey: priceQueries.all,
337
+ })
338
+ }
339
+
340
+ const tokenKey = createTokenCacheKey(normalizeToken(token))
341
+
342
+ return client.invalidateQueries({
343
+ predicate: (query) => matchesTokenPriceQuery(query.queryKey, tokenKey),
344
+ })
345
+ }
346
+
347
+ export async function getTokenPrice(
348
+ trailsClient: TrailsClient,
349
+ token: Token,
350
+ ): Promise<TokenPrice | null> {
351
+ return defaultQueryClient.ensureQueryData(
352
+ priceQueries.byToken(token, trailsClient, defaultQueryClient),
353
+ )
354
+ }
355
+
356
+ export async function getTokenPrices(
357
+ trailsClient: TrailsClient,
358
+ tokens: Token[],
359
+ ): Promise<TokenPrice[]> {
360
+ if (tokens.length === 0) return []
361
+ return defaultQueryClient.ensureQueryData(
362
+ priceQueries.byTokens(tokens, trailsClient, defaultQueryClient),
363
+ )
364
+ }
@@ -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
+ }