0xtrails 0.1.2 → 0.1.4

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 (158) hide show
  1. package/dist/address.d.ts.map +1 -1
  2. package/dist/analytics.d.ts +86 -1
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/apiClient.d.ts +1 -1
  5. package/dist/apiClient.d.ts.map +1 -1
  6. package/dist/{ccip-BmFTEOaB.js → ccip-dLSEJjCf.js} +55 -55
  7. package/dist/cctpqueue.d.ts +1 -1
  8. package/dist/cctpqueue.d.ts.map +1 -1
  9. package/dist/chains.d.ts +9 -3
  10. package/dist/chains.d.ts.map +1 -1
  11. package/dist/constants.d.ts +1 -0
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/decoders.d.ts +58 -0
  14. package/dist/decoders.d.ts.map +1 -0
  15. package/dist/ens.d.ts +13 -0
  16. package/dist/ens.d.ts.map +1 -0
  17. package/dist/error.d.ts +9 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/{index-BPsVj7zK.js → index-BXbaLmtt.js} +28779 -25738
  20. package/dist/index.js +2 -2
  21. package/dist/intents.d.ts +4 -4
  22. package/dist/intents.d.ts.map +1 -1
  23. package/dist/lifi.d.ts +4 -0
  24. package/dist/lifi.d.ts.map +1 -0
  25. package/dist/metaTxns.d.ts +1 -1
  26. package/dist/metaTxns.d.ts.map +1 -1
  27. package/dist/mode.d.ts +1 -1
  28. package/dist/mode.d.ts.map +1 -1
  29. package/dist/preconditions.d.ts +1 -1
  30. package/dist/preconditions.d.ts.map +1 -1
  31. package/dist/prepareSend.d.ts +32 -24
  32. package/dist/prepareSend.d.ts.map +1 -1
  33. package/dist/prices.d.ts +3 -1
  34. package/dist/prices.d.ts.map +1 -1
  35. package/dist/proxyCaller.d.ts +0 -1
  36. package/dist/proxyCaller.d.ts.map +1 -1
  37. package/dist/relaySdk.d.ts.map +1 -1
  38. package/dist/relayer.d.ts.map +1 -1
  39. package/dist/tokenBalances.d.ts +1 -1
  40. package/dist/tokenBalances.d.ts.map +1 -1
  41. package/dist/tokens.d.ts +2 -1
  42. package/dist/tokens.d.ts.map +1 -1
  43. package/dist/trails.d.ts +4 -4
  44. package/dist/trails.d.ts.map +1 -1
  45. package/dist/transactions.d.ts +4 -0
  46. package/dist/transactions.d.ts.map +1 -1
  47. package/dist/utils.d.ts +6 -0
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/wallets.d.ts +247 -5
  50. package/dist/wallets.d.ts.map +1 -1
  51. package/dist/widget/components/ChainFilterDropdown.d.ts +2 -0
  52. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  53. package/dist/widget/components/ConnectWallet.d.ts +1 -0
  54. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  55. package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -1
  56. package/dist/widget/components/ErrorDisplay.d.ts +9 -0
  57. package/dist/widget/components/ErrorDisplay.d.ts.map +1 -0
  58. package/dist/widget/components/FundSendForm.d.ts +2 -2
  59. package/dist/widget/components/FundSendForm.d.ts.map +1 -1
  60. package/dist/widget/components/OriginTransferInformation.d.ts +10 -0
  61. package/dist/widget/components/OriginTransferInformation.d.ts.map +1 -0
  62. package/dist/widget/components/PaySendForm.d.ts +2 -2
  63. package/dist/widget/components/PaySendForm.d.ts.map +1 -1
  64. package/dist/widget/components/QrCode.d.ts +1 -1
  65. package/dist/widget/components/QrCode.d.ts.map +1 -1
  66. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  67. package/dist/widget/components/Receipt.d.ts.map +1 -1
  68. package/dist/widget/components/Receive.d.ts +12 -0
  69. package/dist/widget/components/Receive.d.ts.map +1 -0
  70. package/dist/widget/components/RefundAddressInput.d.ts +13 -0
  71. package/dist/widget/components/RefundAddressInput.d.ts.map +1 -0
  72. package/dist/widget/components/Swap.d.ts +47 -0
  73. package/dist/widget/components/Swap.d.ts.map +1 -0
  74. package/dist/widget/components/SwapDisplay.d.ts +9 -0
  75. package/dist/widget/components/SwapDisplay.d.ts.map +1 -0
  76. package/dist/widget/components/TokenList.d.ts +0 -2
  77. package/dist/widget/components/TokenList.d.ts.map +1 -1
  78. package/dist/widget/components/TokenSelector.d.ts +26 -0
  79. package/dist/widget/components/TokenSelector.d.ts.map +1 -0
  80. package/dist/widget/components/TransferPendingVertical.d.ts +2 -0
  81. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  82. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  83. package/dist/widget/components/WalletConnectionPending.d.ts +12 -0
  84. package/dist/widget/components/WalletConnectionPending.d.ts.map +1 -0
  85. package/dist/widget/components/WalletList.d.ts.map +1 -1
  86. package/dist/widget/components/YellowWarningAnimation.d.ts +2 -0
  87. package/dist/widget/components/YellowWarningAnimation.d.ts.map +1 -0
  88. package/dist/widget/hooks/useAmountUsd.d.ts +1 -3
  89. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  90. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  91. package/dist/widget/hooks/useDebugScreens.d.ts +22 -0
  92. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -0
  93. package/dist/widget/hooks/useSendForm.d.ts +12 -6
  94. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  95. package/dist/widget/hooks/useTokenList.d.ts +2 -3
  96. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  97. package/dist/widget/index.js +1 -1
  98. package/dist/widget/widget.d.ts.map +1 -1
  99. package/package.json +19 -15
  100. package/src/aave.ts +13 -13
  101. package/src/address.ts +3 -0
  102. package/src/analytics.ts +192 -8
  103. package/src/apiClient.ts +1 -1
  104. package/src/cctpqueue.ts +1 -1
  105. package/src/chains.ts +45 -7
  106. package/src/constants.ts +7 -4
  107. package/src/decoders.ts +310 -0
  108. package/src/ens.ts +32 -0
  109. package/src/error.ts +101 -1
  110. package/src/intents.ts +10 -2
  111. package/src/lifi.ts +58 -0
  112. package/src/metaTxns.ts +1 -1
  113. package/src/mode.ts +1 -1
  114. package/src/morpho.ts +3 -3
  115. package/src/pools.ts +18 -18
  116. package/src/preconditions.ts +1 -1
  117. package/src/prepareSend.ts +463 -113
  118. package/src/prices.ts +26 -1
  119. package/src/proxyCaller.ts +2 -14
  120. package/src/relaySdk.ts +1 -0
  121. package/src/relayer.ts +8 -0
  122. package/src/tokenBalances.ts +24 -17
  123. package/src/tokens.ts +147 -22
  124. package/src/trails.ts +4 -4
  125. package/src/transactions.ts +35 -17
  126. package/src/utils.ts +28 -0
  127. package/src/wallets.ts +275 -35
  128. package/src/widget/compiled.css +2 -2
  129. package/src/widget/components/ChainFilterDropdown.tsx +42 -33
  130. package/src/widget/components/ChainImage.tsx +1 -1
  131. package/src/widget/components/ConnectWallet.tsx +92 -128
  132. package/src/widget/components/DebugScreensDropdown.tsx +6 -0
  133. package/src/widget/components/ErrorDisplay.tsx +150 -0
  134. package/src/widget/components/FundSendForm.tsx +78 -11
  135. package/src/widget/components/OriginTransferInformation.tsx +59 -0
  136. package/src/widget/components/PaySendForm.tsx +80 -13
  137. package/src/widget/components/QRCodeDeposit.tsx +6 -6
  138. package/src/widget/components/QrCode.tsx +278 -17
  139. package/src/widget/components/QuoteDetails.tsx +93 -25
  140. package/src/widget/components/Receipt.tsx +296 -103
  141. package/src/widget/components/Receive.tsx +146 -0
  142. package/src/widget/components/RecentTokens.tsx +1 -1
  143. package/src/widget/components/RefundAddressInput.tsx +149 -0
  144. package/src/widget/components/Swap.tsx +769 -0
  145. package/src/widget/components/SwapDisplay.tsx +68 -0
  146. package/src/widget/components/TokenList.tsx +27 -363
  147. package/src/widget/components/TokenSelector.tsx +405 -0
  148. package/src/widget/components/TransferPendingVertical.tsx +162 -112
  149. package/src/widget/components/WalletConnect.tsx +9 -7
  150. package/src/widget/components/WalletConnectionPending.tsx +157 -0
  151. package/src/widget/components/WalletList.tsx +6 -5
  152. package/src/widget/components/YellowWarningAnimation.tsx +146 -0
  153. package/src/widget/hooks/useAmountUsd.ts +3 -8
  154. package/src/widget/hooks/useCheckout.ts +3 -2
  155. package/src/widget/hooks/useDebugScreens.ts +583 -0
  156. package/src/widget/hooks/useSendForm.ts +111 -35
  157. package/src/widget/hooks/useTokenList.ts +155 -122
  158. package/src/widget/widget.tsx +503 -523
package/src/prices.ts CHANGED
@@ -1,4 +1,8 @@
1
- import type { SequenceAPIClient, Token, TokenPrice } from "@0xsequence/api"
1
+ import type {
2
+ SequenceAPIClient,
3
+ Token,
4
+ TokenPrice,
5
+ } from "@0xsequence/trails-api"
2
6
  import { QueryClient, useQuery } from "@tanstack/react-query"
3
7
  import { zeroAddress } from "viem"
4
8
 
@@ -204,3 +208,24 @@ export function formatTvl(tvl: number): string {
204
208
  if (tvl >= 1e3) return `$${(tvl / 1e3).toFixed(1)}K`
205
209
  return `$${tvl.toFixed(0)}`
206
210
  }
211
+
212
+ // Helper function to validate numeric values
213
+ export const isValidNumeric = (
214
+ value: number | string | null | undefined,
215
+ ): boolean => {
216
+ if (value === null || value === undefined || value === "") {
217
+ return true // Empty values are considered valid
218
+ }
219
+ const strValue = String(value).trim()
220
+ return /^\d+(\.\d+)?$/.test(strValue)
221
+ }
222
+
223
+ export const isValidInteger = (
224
+ value: number | string | null | undefined,
225
+ ): boolean => {
226
+ if (value === null || value === undefined || value === "") {
227
+ return true // Empty values are considered valid
228
+ }
229
+ const strValue = String(value).trim()
230
+ return /^\d+$/.test(strValue)
231
+ }
@@ -1,18 +1,9 @@
1
1
  import { encodeFunctionData } from "viem"
2
- import * as chains from "viem/chains"
2
+ import { TRAILS_BALANCE_INJECTOR_ADDRESS } from "./constants.js"
3
3
 
4
4
  export const TRAILS_CONTRACT_PLACEHOLDER_AMOUNT =
5
5
  0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefn
6
6
 
7
- const proxyCallers: Record<string, string> = {
8
- [chains.base.id]: "0x4F54Dc2C6bCa2D01e066fa36f4CFdA96B544DD20",
9
- [chains.arbitrum.id]: "0x8c070e800D0e0c33A0A4ceef7A91677410ed9444",
10
- }
11
-
12
- export function getProxyCallerAddress(chainId: number) {
13
- return proxyCallers[chainId]
14
- }
15
-
16
7
  function getAmountOffset(calldata: `0x${string}`, placeholder: bigint): number {
17
8
  const hex = placeholder.toString(16).padStart(64, "0")
18
9
  const search = hex.toLowerCase()
@@ -99,10 +90,7 @@ export function wrapCalldataWithProxyCallerIfNeeded({
99
90
  }
100
91
  }
101
92
 
102
- const proxyCallerAddress = getProxyCallerAddress(destinationChainId)
103
- if (!proxyCallerAddress) {
104
- return null
105
- }
93
+ const proxyCallerAddress = TRAILS_BALANCE_INJECTOR_ADDRESS
106
94
 
107
95
  const amountOffset = getAmountOffset(
108
96
  calldata,
package/src/relaySdk.ts CHANGED
@@ -242,6 +242,7 @@ export const relaySupportedChains: Record<number, Chain> = {
242
242
  [chains.zircuit.id]: chains.zircuit,
243
243
  [chains.zksync.id]: chains.zksync,
244
244
  [chains.zora.id]: chains.zora,
245
+ [chains.katana.id]: chains.katana,
245
246
  }
246
247
 
247
248
  export async function getRelaySupportedChains(): Promise<Chain[]> {
package/src/relayer.ts CHANGED
@@ -162,6 +162,10 @@ export function getRelayerUrl(
162
162
  relayerUrl = "https://v3-xai-relayer.sequence.app"
163
163
  } else if (chainId === 56) {
164
164
  relayerUrl = "https://v3-bsc-relayer.sequence.app"
165
+ } else if (chainId === 747474) {
166
+ relayerUrl = "https://v3-katana-relayer.sequence.app"
167
+ } else if (chainId === 42793) {
168
+ relayerUrl = "https://v3-etherlink-relayer.sequence.app"
165
169
  } else {
166
170
  // Fallback to general dev relayer for other chains if V3 is specified but chain not V3-supported
167
171
  relayerUrl = `${baseUrl}${getChainInfo(chainId)!.name?.replace(" ", "-")}-relayer.sequence.app`
@@ -207,6 +211,10 @@ export function getRelayerUrl(
207
211
  relayerUrl = "https://dev-xai-relayer.sequence.app"
208
212
  } else if (chainId === 56) {
209
213
  relayerUrl = "https://dev-bsc-relayer.sequence.app"
214
+ } else if (chainId === 747474) {
215
+ relayerUrl = "https://dev-katana-relayer.sequence.app"
216
+ } else if (chainId === 42793) {
217
+ relayerUrl = "https://dev-etherlink-relayer.sequence.app"
210
218
  } else {
211
219
  // Fallback to general dev relayer for other chains if V3 is specified but chain not V3-supported
212
220
  relayerUrl = `${baseUrl}${getChainInfo(chainId)!.name?.replace(" ", "-")}-relayer.sequence.app`
@@ -7,7 +7,7 @@ import type {
7
7
  TokenBalance,
8
8
  } from "@0xsequence/indexer"
9
9
  import { ContractVerificationStatus } from "@0xsequence/indexer"
10
- import type { Page, Price, SequenceAPIClient } from "@0xsequence/api"
10
+ import type { Page, Price, SequenceAPIClient } from "@0xsequence/trails-api"
11
11
  import { QueryClient, useQuery } from "@tanstack/react-query"
12
12
  import type { Address } from "ox"
13
13
  import { useEffect, useState } from "react"
@@ -18,12 +18,14 @@ import { getTokenPrices, useTokenPrices } from "./prices.js"
18
18
 
19
19
  export type { NativeTokenBalance, TokenBalance }
20
20
 
21
+ const REFRESH_INTERVAL = 10000 // 10 seconds
22
+
21
23
  // Initialize query client for token balances
22
24
  const tokenBalancesQueryClient = new QueryClient({
23
25
  defaultOptions: {
24
26
  queries: {
25
- staleTime: 60000, // 1 minute
26
- gcTime: 300000, // 5 minutes
27
+ staleTime: REFRESH_INTERVAL, // 10 seconds - faster updates for balance changes
28
+ gcTime: REFRESH_INTERVAL, // 10 seconds
27
29
  retry: 2,
28
30
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
29
31
  refetchOnWindowFocus: false,
@@ -167,8 +169,8 @@ export function useTokenBalances(
167
169
  }
168
170
  },
169
171
  enabled: !!address && !!indexerClient,
170
- staleTime: 60000, // 1 minute
171
- gcTime: 300000, // 5 minutes cache time
172
+ staleTime: 10000, // 10 seconds - faster updates for balance changes
173
+ gcTime: 10000, // 10 seconds cache time
172
174
  retry: (failureCount, error) => {
173
175
  // Don't retry 404s or network errors after 3 attempts
174
176
  if (error && "status" in error && error.status === 404) return false
@@ -176,10 +178,11 @@ export function useTokenBalances(
176
178
  return false
177
179
  },
178
180
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff
179
- refetchOnWindowFocus: false, // Prevent refetch on window focus
181
+ refetchOnWindowFocus: true, // Prevent refetch on window focus
180
182
  refetchOnReconnect: true, // Refetch on reconnect
181
- refetchInterval: 300000, // Background refetch every 5 minutes
183
+ refetchInterval: REFRESH_INTERVAL, // Background refetch every 10 seconds
182
184
  refetchIntervalInBackground: true,
185
+ refetchOnMount: true,
183
186
  })
184
187
 
185
188
  const { tokenPrices, isLoadingTokenPrices } = useTokenPrices(
@@ -262,9 +265,9 @@ export function useTokenBalances(
262
265
  !isLoadingTokenPrices &&
263
266
  !!tokenBalancesData &&
264
267
  !!tokenPrices,
265
- staleTime: 30000, // 30 seconds for sorted tokens
266
- gcTime: 120000, // 2 minutes cache time
267
- refetchOnWindowFocus: false,
268
+ staleTime: REFRESH_INTERVAL, // 10 seconds for sorted tokens
269
+ gcTime: REFRESH_INTERVAL, // 10 seconds cache time
270
+ refetchOnWindowFocus: true,
268
271
  })
269
272
 
270
273
  return {
@@ -283,6 +286,9 @@ export function formatRawAmount(
283
286
  balance: string | bigint,
284
287
  decimals: number = 18,
285
288
  ): string {
289
+ if (!balance) {
290
+ return "0"
291
+ }
286
292
  try {
287
293
  const formatted = formatUnits(BigInt(balance), decimals)
288
294
  return formatAmount(formatted)
@@ -429,8 +435,8 @@ export async function getTokenBalances({
429
435
  queryKey: ["tokenBalances", "summary", account],
430
436
  queryFn: () =>
431
437
  fetchGetTokenBalancesSummary({ account, indexerGatewayClient }),
432
- staleTime: 60000, // 1 minute
433
- gcTime: 300000, // 5 minutes
438
+ staleTime: REFRESH_INTERVAL, // 10 seconds - faster updates for balance changes
439
+ gcTime: REFRESH_INTERVAL, // 10 seconds
434
440
  })
435
441
  }
436
442
 
@@ -724,8 +730,8 @@ export function useHasSufficientBalanceUsd(
724
730
  })
725
731
  : false,
726
732
  enabled: !!account && !!targetAmountUsd,
727
- staleTime: 45000, // 45 seconds
728
- gcTime: 180000, // 3 minutes cache time
733
+ staleTime: REFRESH_INTERVAL, // 10 seconds
734
+ gcTime: REFRESH_INTERVAL, // 10 seconds cache time
729
735
  retry: (failureCount, error) => {
730
736
  if (error && "status" in error && error.status === 404) return false
731
737
  if (failureCount < 2) return true
@@ -783,8 +789,8 @@ export function useAccountTotalBalanceUsd(account: string): {
783
789
  })
784
790
  : null,
785
791
  enabled: !!account,
786
- staleTime: 60000, // 1 minute
787
- gcTime: 300000, // 5 minutes cache time
792
+ staleTime: REFRESH_INTERVAL, // 10 seconds - faster updates for balance changes
793
+ gcTime: REFRESH_INTERVAL, // 10 seconds cache time
788
794
  retry: (failureCount, error) => {
789
795
  if (error && "status" in error && error.status === 404) return false
790
796
  if (failureCount < 2) return true
@@ -793,8 +799,9 @@ export function useAccountTotalBalanceUsd(account: string): {
793
799
  retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
794
800
  refetchOnWindowFocus: false,
795
801
  refetchOnReconnect: true,
796
- refetchInterval: 300000, // Background refetch every 5 minutes
802
+ refetchInterval: REFRESH_INTERVAL, // Background refetch every 10 seconds
797
803
  refetchIntervalInBackground: true,
804
+ refetchOnMount: true,
798
805
  })
799
806
 
800
807
  return {
package/src/tokens.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { QueryClient, useQuery } from "@tanstack/react-query"
2
- import { zeroAddress, erc20Abi } from "viem"
3
- import * as chains from "viem/chains"
2
+ import { zeroAddress, erc20Abi, createPublicClient, http } from "viem"
4
3
  import { getChainInfo, getSupportedChains } from "./chains.js"
4
+ import * as chains from "viem/chains"
5
5
  import { getRelaySupportedTokens } from "./relaySdk.js"
6
6
  import { useReadContracts } from "wagmi"
7
7
  import { useMemo } from "react"
@@ -17,6 +17,21 @@ export type SupportedToken = {
17
17
  imageUrl: string
18
18
  }
19
19
 
20
+ export const commonTokenImages: Record<string, string> = {
21
+ ETH: "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
22
+ WETH: "https://assets.coingecko.com/coins/images/39723/small/WETH.PNG?1723733967",
23
+ POL: "https://assets.sequence.info/images/tokens/large/137/0x0000000000000000000000000000000000000000.webp",
24
+ USDC: "https://assets.sequence.info/images/tokens/large/1/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.webp",
25
+ USDT: "https://assets.sequence.info/images/tokens/large/1/0xdac17f958d2ee523a2206206994597c13d831ec7.webp",
26
+ DAI: "https://assets.sequence.info/images/tokens/large/1/0x6b175474e89094c44da98b954eedeac495271d0f.webp",
27
+ WBTC: "https://assets.sequence.info/images/tokens/large/1/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599.webp",
28
+ BAT: "https://assets.sequence.info/images/tokens/large/1/0x0d8775f648430679a709e98d2b0cb6250d2887ef.webp",
29
+ ARB: "https://assets.sequence.info/images/tokens/large/42161/0x912ce59144191c1204e64559fe8253a0e49e6548.webp",
30
+ LINK: "https://assets.sequence.info/images/tokens/large/1/0x514910771af9ca656af840dff83e8264ecf986ca.webp",
31
+ XTZ: "https://assets.sequence.info/images/tokens/large/42793/0x0000000000000000000000000000000000000000.webp",
32
+ WXTZ: "https://assets.coingecko.com/coins/images/976/standard/Tezos-logo.png?1696502091",
33
+ }
34
+
20
35
  const cacheVersion = "01"
21
36
 
22
37
  // LocalStorage cache utilities for token images
@@ -303,8 +318,13 @@ export async function getTokenImageUrlOrFallback({
303
318
  }
304
319
  }
305
320
 
321
+ export async function getAllTokens(): Promise<SupportedToken[]> {
322
+ const relayTokens = (await getRelaySupportedTokens()) as SupportedToken[]
323
+ return relayTokens
324
+ }
325
+
306
326
  export async function getSupportedTokens(): Promise<SupportedToken[]> {
307
- const tokens = await getRelaySupportedTokens()
327
+ const tokens = await getAllTokens()
308
328
  for (const token of tokens) {
309
329
  if (!token.imageUrl) {
310
330
  token.imageUrl = getTokenImageUrl({
@@ -342,9 +362,13 @@ export async function getSupportedTokens(): Promise<SupportedToken[]> {
342
362
 
343
363
  const supportedChains = await getSupportedChains()
344
364
  const allTokens = [...tokens, ...additionalTokens]
345
- const supportedChainTokens = allTokens.filter((token) =>
346
- supportedChains.some((chain) => chain.id === token.chainId),
347
- )
365
+ const supportedChainTokens = allTokens.filter((token) => {
366
+ // Always include Etherlink tokens
367
+ if (token.chainId === chains.etherlink.id) {
368
+ return true
369
+ }
370
+ return supportedChains.some((chain) => chain.id === token.chainId)
371
+ })
348
372
  const uniqueTokens = supportedChainTokens.filter(
349
373
  (token, index, self) =>
350
374
  index ===
@@ -357,7 +381,9 @@ export async function getSupportedTokens(): Promise<SupportedToken[]> {
357
381
  )
358
382
 
359
383
  // Sort tokens according to priority order
360
- return sortTokens(uniqueTokens as SupportedToken[])
384
+ const sortedTokens = sortTokens(uniqueTokens as SupportedToken[])
385
+
386
+ return sortedTokens
361
387
  }
362
388
 
363
389
  export function useSupportedTokens({ chainId }: { chainId?: number } = {}): {
@@ -426,6 +452,7 @@ export const tokenNamePrefixes: Record<string, string> = {
426
452
  [chains.optimism.id]: "Optimistic",
427
453
  [chains.arbitrum.id]: "Arbitrum",
428
454
  [chains.polygon.id]: "Polygon",
455
+ [chains.etherlink.id]: "Etherlink",
429
456
  }
430
457
 
431
458
  export function getFormatttedTokenName(
@@ -508,6 +535,9 @@ export async function getTokenInfo(
508
535
  t.contractAddress.toLowerCase() === normalizedAddress,
509
536
  )
510
537
 
538
+ console.log("tokens", tokens)
539
+ console.log("token", token)
540
+
511
541
  if (token) {
512
542
  // Cache the token info
513
543
  setTokenInfoCache(`${chainId}:${normalizedAddress}`, token)
@@ -515,8 +545,64 @@ export async function getTokenInfo(
515
545
  return token
516
546
  }
517
547
 
518
- // If not found in supported tokens, return null
519
- return null
548
+ // If not found in supported tokens, try to fetch on-chain data
549
+ try {
550
+ const chainInfo = getChainInfo(chainId)
551
+ if (!chainInfo) {
552
+ console.warn(`[trails-sdk] Chain info not found for chainId: ${chainId}`)
553
+ return null
554
+ }
555
+
556
+ // Create a public client for the specific chain
557
+ const publicClient = createPublicClient({
558
+ chain: chainInfo,
559
+ transport: http(),
560
+ })
561
+
562
+ // Read token data on-chain
563
+ const [name, symbol, decimals] = await Promise.all([
564
+ publicClient.readContract({
565
+ address: normalizedAddress as `0x${string}`,
566
+ abi: erc20Abi,
567
+ functionName: "name",
568
+ }),
569
+ publicClient.readContract({
570
+ address: normalizedAddress as `0x${string}`,
571
+ abi: erc20Abi,
572
+ functionName: "symbol",
573
+ }),
574
+ publicClient.readContract({
575
+ address: normalizedAddress as `0x${string}`,
576
+ abi: erc20Abi,
577
+ functionName: "decimals",
578
+ }),
579
+ ])
580
+
581
+ // Create token info from on-chain data
582
+ const onChainTokenInfo: SupportedToken = {
583
+ id: `${symbol}-${chainInfo.name}`,
584
+ name: name,
585
+ symbol: symbol,
586
+ decimals: decimals,
587
+ chainId: chainId,
588
+ contractAddress: normalizedAddress,
589
+ chainName: chainInfo.name,
590
+ imageUrl: getTokenImageUrl({
591
+ chainId,
592
+ contractAddress: normalizedAddress,
593
+ symbol: symbol,
594
+ }),
595
+ }
596
+
597
+ // Cache the on-chain token info
598
+ setTokenInfoCache(`${chainId}:${normalizedAddress}`, onChainTokenInfo)
599
+
600
+ console.log("[trails-sdk] Fetched token info on-chain:", onChainTokenInfo)
601
+ return onChainTokenInfo
602
+ } catch (error) {
603
+ console.error("[trails-sdk] Error fetching on-chain token info:", error)
604
+ return null
605
+ }
520
606
  }
521
607
 
522
608
  export async function getTokenAddress(chainId: number, tokenSymbol: string) {
@@ -788,21 +874,8 @@ export function useTokenInfo({
788
874
  }
789
875
  }
790
876
 
791
- export const commonTokenImages: Record<string, string> = {
792
- ETH: "https://assets.sequence.info/images/tokens/large/1/0x0000000000000000000000000000000000000000.webp",
793
- POL: "https://assets.sequence.info/images/tokens/large/137/0x0000000000000000000000000000000000000000.webp",
794
- USDC: "https://assets.sequence.info/images/tokens/large/1/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.webp",
795
- USDT: "https://assets.sequence.info/images/tokens/large/1/0xdac17f958d2ee523a2206206994597c13d831ec7.webp",
796
- DAI: "https://assets.sequence.info/images/tokens/large/1/0x6b175474e89094c44da98b954eedeac495271d0f.webp",
797
- WBTC: "https://assets.sequence.info/images/tokens/large/1/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599.webp",
798
- BAT: "https://assets.sequence.info/images/tokens/large/1/0x0d8775f648430679a709e98d2b0cb6250d2887ef.webp",
799
- ARB: "https://assets.sequence.info/images/tokens/large/42161/0x912ce59144191c1204e64559fe8253a0e49e6548.webp",
800
- LINK: "https://assets.sequence.info/images/tokens/large/1/0x514910771af9ca656af840dff83e8264ecf986ca.webp",
801
- }
802
-
803
877
  export const tokenImageSymbolMap: Record<string, string> = {
804
878
  ETH: "ETH",
805
- WETH: "ETH",
806
879
  cbETH: "ETH",
807
880
  POL: "POL",
808
881
  WPOL: "POL",
@@ -1018,6 +1091,58 @@ export const commonTokens: SupportedToken[] = [
1018
1091
  chainName: chains.mainnet.name,
1019
1092
  imageUrl: commonTokenImages.LINK!,
1020
1093
  },
1094
+
1095
+ // Etherlink tokens
1096
+ {
1097
+ id: "XTZ-etherlink",
1098
+ symbol: "XTZ",
1099
+ name: "Tezos",
1100
+ contractAddress: "0x0000000000000000000000000000000000000000",
1101
+ decimals: 18,
1102
+ chainId: chains.etherlink.id,
1103
+ chainName: chains.etherlink.name,
1104
+ imageUrl: commonTokenImages.XTZ as string,
1105
+ },
1106
+ {
1107
+ id: "WXTZ-etherlink",
1108
+ symbol: "WXTZ",
1109
+ name: "Wrapped XTZ",
1110
+ contractAddress: "0xc9B53AB2679f573e480d01e0f49e2B5CFB7a3EAb",
1111
+ decimals: 18,
1112
+ chainId: chains.etherlink.id,
1113
+ chainName: chains.etherlink.name,
1114
+ imageUrl: commonTokenImages.WXTZ as string,
1115
+ },
1116
+ {
1117
+ id: "USDC-etherlink",
1118
+ symbol: "USDC",
1119
+ name: "USD Coin",
1120
+ contractAddress: "0x796Ea11Fa2dD751eD01b53C372fFDB4AAa8f00F9",
1121
+ decimals: 6,
1122
+ chainId: chains.etherlink.id,
1123
+ chainName: chains.etherlink.name,
1124
+ imageUrl: commonTokenImages.USDC as string,
1125
+ },
1126
+ {
1127
+ id: "WETH-etherlink",
1128
+ symbol: "WETH",
1129
+ name: "Wrapped ETH",
1130
+ contractAddress: "0xfc24f770F94edBca6D6f885E12d4317320BcB401",
1131
+ decimals: 18,
1132
+ chainId: chains.etherlink.id,
1133
+ chainName: chains.etherlink.name,
1134
+ imageUrl: commonTokenImages.WETH as string,
1135
+ },
1136
+ {
1137
+ id: "USDT-etherlink",
1138
+ symbol: "USDT",
1139
+ name: "Bridged USDT",
1140
+ contractAddress: "0x2C03058C8AFC06713be23e58D2febC8337dbfE6A",
1141
+ decimals: 6,
1142
+ chainId: chains.etherlink.id,
1143
+ chainName: chains.etherlink.name,
1144
+ imageUrl: commonTokenImages.USDT as string,
1145
+ },
1021
1146
  ]
1022
1147
 
1023
1148
  export const wethAddresses: Record<string, string> = {
package/src/trails.ts CHANGED
@@ -4,8 +4,8 @@ import type {
4
4
  GetIntentConfigReturn,
5
5
  IntentCallsPayload,
6
6
  IntentPrecondition,
7
- } from "@0xsequence/api"
8
- import type { SequenceAPIClient } from "@0xsequence/api"
7
+ } from "@0xsequence/trails-api"
8
+ import type { SequenceAPIClient } from "@0xsequence/trails-api"
9
9
  import type { Relayer } from "@0xsequence/wallet-core"
10
10
  import { useMutation, useQuery } from "@tanstack/react-query"
11
11
  import { Address } from "ox"
@@ -98,7 +98,7 @@ export type UseTrailsReturn = {
98
98
  mainSignerAddress: string
99
99
  calls: IntentCallsPayload[]
100
100
  preconditions: IntentPrecondition[]
101
- quoteProvider: "lifi" | "relay" | "cctp"
101
+ quoteProvider: QuoteProvider
102
102
  addressOverrides?: AddressOverrides
103
103
  }) => void
104
104
  commitIntentConfigPending: boolean
@@ -109,7 +109,7 @@ export type UseTrailsReturn = {
109
109
  mainSignerAddress: string
110
110
  calls: IntentCallsPayload[]
111
111
  preconditions: IntentPrecondition[]
112
- quoteProvider: "lifi" | "relay" | "cctp"
112
+ quoteProvider: QuoteProvider
113
113
  addressOverrides?: AddressOverrides
114
114
  }
115
115
  | undefined
@@ -1,6 +1,8 @@
1
1
  import { createPublicClient, http } from "viem"
2
2
  import { getChainInfo } from "./chains.js"
3
- import { getRpcSequenceProjectAccessKey } from "./config.js"
3
+ import { getRpcSequenceProjectAccessKey, getSequenceEnv } from "./config.js"
4
+ import type { GuestModuleEvent, TrailsTokenSweeperEvent } from "./decoders.js"
5
+ import * as chains from "viem/chains"
4
6
 
5
7
  export type TransactionStateStatus = "pending" | "failed" | "confirmed"
6
8
 
@@ -11,6 +13,8 @@ export type TransactionState = {
11
13
  chainId: number
12
14
  state: TransactionStateStatus
13
15
  label: string
16
+ decodedTrailsTokenSweeperEvents?: TrailsTokenSweeperEvent[]
17
+ decodedGuestModuleEvents?: GuestModuleEvent[]
14
18
  }
15
19
 
16
20
  export type TransferType = "SEND" | "RECEIVE"
@@ -163,23 +167,37 @@ export async function getTxTimeDiff(
163
167
  }
164
168
  }
165
169
 
170
+ export function getIndexerUrlChainSlug(chainSlug: string) {
171
+ const env = getSequenceEnv()
172
+ let envPrefix = ""
173
+
174
+ // Only use dev prefix for katana in dev environment, since it's not deployed to prod yet
175
+ if (env === "dev" && chainSlug === "katana") {
176
+ envPrefix = "dev-"
177
+ }
178
+
179
+ return `https://${envPrefix}${chainSlug}-indexer.sequence.app`
180
+ }
181
+
166
182
  const chainIdToIndexerUrl = {
167
- 42161: "https://arbitrum-indexer.sequence.app",
168
- 8453: "https://base-indexer.sequence.app",
169
- 84532: "https://base-sepolia-indexer.sequence.app",
170
- 10: "https://optimism-indexer.sequence.app",
171
- 137: "https://polygon-indexer.sequence.app",
172
- 1: "https://mainnet-indexer.sequence.app",
173
- 33139: "https://apechain-indexer.sequence.app",
174
- 42170: "https://arbitrum-nova-indexer.sequence.app",
175
- 43114: "https://avalanche-indexer.sequence.app",
176
- 8333: "https://b3-indexer.sequence.app",
177
- 81457: "https://blast-indexer.sequence.app",
178
- 100: "https://gnosis-indexer.sequence.app",
179
- 1868: "https://soneium-indexer.sequence.app",
180
- 660279: "https://xai-indexer.sequence.app",
181
- 56: "https://bsc-indexer.sequence.app",
182
- 421613: "https://arbitrum-nova-sepolia-indexer.sequence.app",
183
+ [chains.arbitrum.id]: getIndexerUrlChainSlug("arbitrum"),
184
+ [chains.base.id]: getIndexerUrlChainSlug("base"),
185
+ [chains.baseSepolia.id]: getIndexerUrlChainSlug("base-sepolia"),
186
+ [chains.optimism.id]: getIndexerUrlChainSlug("optimism"),
187
+ [chains.polygon.id]: getIndexerUrlChainSlug("polygon"),
188
+ [chains.mainnet.id]: getIndexerUrlChainSlug("mainnet"),
189
+ [chains.apeChain.id]: getIndexerUrlChainSlug("apechain"),
190
+ [chains.arbitrumNova.id]: getIndexerUrlChainSlug("arbitrum-nova"),
191
+ [chains.avalanche.id]: getIndexerUrlChainSlug("avalanche"),
192
+ [chains.b3.id]: getIndexerUrlChainSlug("b3"),
193
+ [chains.blast.id]: getIndexerUrlChainSlug("blast"),
194
+ [chains.gnosis.id]: getIndexerUrlChainSlug("gnosis"),
195
+ [chains.soneium.id]: getIndexerUrlChainSlug("soneium"),
196
+ [chains.xai.id]: getIndexerUrlChainSlug("xai"),
197
+ [chains.bsc.id]: getIndexerUrlChainSlug("bsc"),
198
+ 421613: getIndexerUrlChainSlug("arbitrum-nova-sepolia"),
199
+ [chains.etherlink.id]: getIndexerUrlChainSlug("etherlink"),
200
+ [chains.katana.id]: getIndexerUrlChainSlug("katana"),
183
201
  }
184
202
 
185
203
  export async function getAccountTransactionHistory({
package/src/utils.ts CHANGED
@@ -40,3 +40,31 @@ export function formatElapsed(seconds: number): string {
40
40
  }
41
41
  }
42
42
  }
43
+
44
+ /**
45
+ * Calculates and formats time ago from a timestamp
46
+ * @param timestamp - Timestamp in milliseconds
47
+ * @returns Formatted time ago string (e.g., "Sent just now", "Sent 5 minutes ago")
48
+ */
49
+ export function getTimeAgo(timestamp?: number): string {
50
+ if (!timestamp) return "Sent just now"
51
+
52
+ const now = Date.now()
53
+ const diffInSeconds = Math.floor((now - timestamp) / 1000)
54
+
55
+ if (diffInSeconds < 1) return "Sent just now"
56
+ if (diffInSeconds === 1) return "Sent 1 second ago"
57
+ if (diffInSeconds < 60) return `Sent ${diffInSeconds} seconds ago`
58
+
59
+ const diffInMinutes = Math.floor(diffInSeconds / 60)
60
+ if (diffInMinutes === 1) return "Sent 1 minute ago"
61
+ if (diffInMinutes < 60) return `Sent ${diffInMinutes} minutes ago`
62
+
63
+ const diffInHours = Math.floor(diffInMinutes / 60)
64
+ if (diffInHours === 1) return "Sent 1 hour ago"
65
+ if (diffInHours < 24) return `Sent ${diffInHours} hours ago`
66
+
67
+ const diffInDays = Math.floor(diffInHours / 24)
68
+ if (diffInDays === 1) return "Sent 1 day ago"
69
+ return `Sent ${diffInDays} days ago`
70
+ }