@solana/connector 0.1.6 → 0.1.8

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 (50) hide show
  1. package/README.md +122 -44
  2. package/dist/{chunk-VMSZJPR5.mjs → chunk-6F6M6L7R.mjs} +152 -173
  3. package/dist/chunk-6F6M6L7R.mjs.map +1 -0
  4. package/dist/{chunk-NQXK7PGX.js → chunk-AOIXHVRH.js} +82 -235
  5. package/dist/chunk-AOIXHVRH.js.map +1 -0
  6. package/dist/chunk-DSUCH44G.js +678 -0
  7. package/dist/chunk-DSUCH44G.js.map +1 -0
  8. package/dist/{chunk-JUZVCBAI.js → chunk-FTXIXM43.js} +240 -271
  9. package/dist/chunk-FTXIXM43.js.map +1 -0
  10. package/dist/{chunk-QKVL45F6.mjs → chunk-G575OAT4.mjs} +73 -218
  11. package/dist/chunk-G575OAT4.mjs.map +1 -0
  12. package/dist/chunk-J7DHGLW6.mjs +638 -0
  13. package/dist/chunk-J7DHGLW6.mjs.map +1 -0
  14. package/dist/{chunk-ULUYX23Q.js → chunk-K3BNIGPX.js} +1023 -404
  15. package/dist/chunk-K3BNIGPX.js.map +1 -0
  16. package/dist/{chunk-3STZXVXD.mjs → chunk-TTOKQAPX.mjs} +998 -388
  17. package/dist/chunk-TTOKQAPX.mjs.map +1 -0
  18. package/dist/compat.d.mts +1 -1
  19. package/dist/compat.d.ts +1 -1
  20. package/dist/compat.js +40 -39
  21. package/dist/compat.js.map +1 -1
  22. package/dist/compat.mjs +39 -38
  23. package/dist/compat.mjs.map +1 -1
  24. package/dist/headless.d.mts +540 -152
  25. package/dist/headless.d.ts +540 -152
  26. package/dist/headless.js +226 -190
  27. package/dist/headless.mjs +3 -3
  28. package/dist/index.d.mts +8 -6
  29. package/dist/index.d.ts +8 -6
  30. package/dist/index.js +286 -218
  31. package/dist/index.mjs +4 -4
  32. package/dist/react.d.mts +283 -16
  33. package/dist/react.d.ts +283 -16
  34. package/dist/react.js +60 -28
  35. package/dist/react.mjs +2 -2
  36. package/dist/{wallet-standard-shim--YcrQNRt.d.ts → standard-shim-CT49DM5l.d.mts} +72 -252
  37. package/dist/{wallet-standard-shim-Dx7H8Ctf.d.mts → standard-shim-D9guL5fz.d.ts} +72 -252
  38. package/dist/{transaction-signer-D9d8nxwb.d.mts → transaction-signer-T-KVQFi8.d.mts} +2 -2
  39. package/dist/{transaction-signer-D9d8nxwb.d.ts → transaction-signer-T-KVQFi8.d.ts} +2 -2
  40. package/package.json +3 -3
  41. package/dist/chunk-3STZXVXD.mjs.map +0 -1
  42. package/dist/chunk-I64FD2EH.js +0 -312
  43. package/dist/chunk-I64FD2EH.js.map +0 -1
  44. package/dist/chunk-JUZVCBAI.js.map +0 -1
  45. package/dist/chunk-NQXK7PGX.js.map +0 -1
  46. package/dist/chunk-QKVL45F6.mjs.map +0 -1
  47. package/dist/chunk-QL3IT3TS.mjs +0 -299
  48. package/dist/chunk-QL3IT3TS.mjs.map +0 -1
  49. package/dist/chunk-ULUYX23Q.js.map +0 -1
  50. package/dist/chunk-VMSZJPR5.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { installPolyfills, ConnectorErrorBoundary, isMainnetCluster, isDevnetCluster, isTestnetCluster, isLocalCluster, getClusterExplorerUrl, getClusterType, formatAddress, copyAddressToClipboard, createTransactionSigner, createKitTransactionSigner, NetworkError, getTransactionUrl, ConnectorClient } from './chunk-VMSZJPR5.mjs';
2
- import { createLogger, __publicField, createSolanaClient, prepareTransaction } from './chunk-QL3IT3TS.mjs';
1
+ import { installPolyfills, ConnectorErrorBoundary, isMainnetCluster, isDevnetCluster, isTestnetCluster, isLocalCluster, getClusterExplorerUrl, getClusterType, formatAddress, copyAddressToClipboard, createTransactionSigner, createKitTransactionSigner, formatLamportsToSolSafe, getTransactionUrl, ConnectorClient, formatBigIntBalance, formatBigIntUsd } from './chunk-6F6M6L7R.mjs';
2
+ import { createLogger, __publicField, createSolanaClient, NetworkError, prepareTransaction } from './chunk-J7DHGLW6.mjs';
3
3
  import React, { createContext, useContext, useSyncExternalStore, useMemo, useState, useCallback, useRef, useEffect } from 'react';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
5
  import { address } from '@solana/addresses';
@@ -54,15 +54,15 @@ function ConnectorProviderInternal({
54
54
  "solana:mainnet",
55
55
  "solana:devnet",
56
56
  "solana:testnet"
57
- ];
58
- registerMwa({
57
+ ], mwaConfig = {
59
58
  appIdentity: mobile.appIdentity,
60
59
  authorizationCache: mobile.authorizationCache ?? createDefaultAuthorizationCache(),
61
60
  chains: mobile.chains ?? defaultChains,
62
61
  chainSelector: mobile.chainSelector ?? createDefaultChainSelector(),
63
62
  remoteHostAuthority: mobile.remoteHostAuthority,
64
63
  onWalletNotFound: mobile.onWalletNotFound ?? createDefaultWalletNotFoundHandler()
65
- });
64
+ };
65
+ registerMwa(mwaConfig);
66
66
  } catch {
67
67
  }
68
68
  })(), () => {
@@ -115,14 +115,15 @@ function useConnector() {
115
115
  function useConnectorClient() {
116
116
  return useContext(ConnectorContext);
117
117
  }
118
- function UnifiedProvider({ children, config, connectorConfig, mobile, providers = [] }) {
119
- let actualConnectorConfig = config?.connectorConfig ?? connectorConfig, actualMobile = config?.mobile ?? mobile, content = /* @__PURE__ */ jsx(ConnectorProvider, { config: actualConnectorConfig, mobile: actualMobile, children });
118
+ function AppProvider({ children, connectorConfig, mobile, providers = [] }) {
119
+ let content = /* @__PURE__ */ jsx(ConnectorProvider, { config: connectorConfig, mobile, children });
120
120
  for (let i = providers.length - 1; i >= 0; i--) {
121
121
  let { component: Provider, props = {} } = providers[i];
122
122
  content = /* @__PURE__ */ jsx(Provider, { ...props, children: content });
123
123
  }
124
124
  return content;
125
125
  }
126
+ var UnifiedProvider = AppProvider;
126
127
  function useCluster() {
127
128
  let { cluster, clusters } = useConnector(), client = useConnectorClient();
128
129
  if (!client)
@@ -299,73 +300,503 @@ function useTransactionPreparer() {
299
300
  [prepare, ready]
300
301
  );
301
302
  }
302
- var LAMPORTS_PER_SOL2 = 1000000000n;
303
- function formatSol(lamports, decimals = 4) {
304
- return (Number(lamports) / Number(LAMPORTS_PER_SOL2)).toLocaleString(void 0, {
305
- minimumFractionDigits: 0,
306
- maximumFractionDigits: decimals
307
- }) + " SOL";
308
- }
309
- function useBalance() {
310
- let { address: address$1, connected } = useAccount(); useCluster(); let client = useSolanaClient(), [lamports, setLamports] = useState(0n), [tokens, setTokens] = useState([]), [isLoading, setIsLoading] = useState(false), [error, setError] = useState(null), [lastUpdated, setLastUpdated] = useState(null), hasDataRef = useRef(false), rpcClient = client?.client ?? null, fetchBalance = useCallback(async () => {
311
- if (!connected || !address$1 || !rpcClient) {
312
- setLamports(0n), setTokens([]), hasDataRef.current = false;
303
+ var DEFAULT_CACHE_TIME_MS = 300 * 1e3, MAX_STORE_SIZE = 100, store = /* @__PURE__ */ new Map();
304
+ function evictStaleEntries() {
305
+ if (store.size <= MAX_STORE_SIZE) return;
306
+ let evictable = [];
307
+ for (let [key, entry] of store)
308
+ entry.subscribers.size === 0 && evictable.push([key, entry]);
309
+ evictable.sort((a, b) => a[1].lastAccessedAt - b[1].lastAccessedAt);
310
+ for (let [key, entry] of evictable) {
311
+ if (store.size <= MAX_STORE_SIZE) break;
312
+ entry.gcTimeoutId && clearTimeout(entry.gcTimeoutId), entry.intervalId && clearInterval(entry.intervalId), entry.abortController && entry.abortController.abort(), store.delete(key);
313
+ }
314
+ }
315
+ function getOrCreateEntry(key) {
316
+ let existing = store.get(key);
317
+ if (existing)
318
+ return existing.lastAccessedAt = Date.now(), existing;
319
+ let entry = {
320
+ snapshot: {
321
+ data: void 0,
322
+ error: null,
323
+ status: "idle",
324
+ updatedAt: null,
325
+ isFetching: false
326
+ },
327
+ subscribers: /* @__PURE__ */ new Set(),
328
+ promise: null,
329
+ abortController: null,
330
+ cacheTimeMs: DEFAULT_CACHE_TIME_MS,
331
+ gcTimeoutId: null,
332
+ queryFn: null,
333
+ intervalCounts: /* @__PURE__ */ new Map(),
334
+ activeIntervalMs: null,
335
+ intervalId: null,
336
+ lastAccessedAt: Date.now()
337
+ };
338
+ return store.set(key, entry), evictStaleEntries(), entry;
339
+ }
340
+ function emit(entry) {
341
+ for (let cb of entry.subscribers)
342
+ cb();
343
+ }
344
+ function setSnapshot(entry, next) {
345
+ let prev = entry.snapshot;
346
+ prev.status === next.status && prev.isFetching === next.isFetching && prev.updatedAt === next.updatedAt && prev.error === next.error && prev.data === next.data || (entry.snapshot = next, emit(entry));
347
+ }
348
+ function getMinIntervalMs(entry) {
349
+ let min = null;
350
+ for (let [ms, count] of entry.intervalCounts)
351
+ count <= 0 || (min === null || ms < min) && (min = ms);
352
+ return min;
353
+ }
354
+ function startOrRestartInterval(key, entry) {
355
+ let nextMs = getMinIntervalMs(entry);
356
+ if (nextMs === null) {
357
+ entry.intervalId && clearInterval(entry.intervalId), entry.intervalId = null, entry.activeIntervalMs = null;
358
+ return;
359
+ }
360
+ entry.activeIntervalMs === nextMs && entry.intervalId || (entry.intervalId && clearInterval(entry.intervalId), entry.activeIntervalMs = nextMs, entry.intervalId = setInterval(() => {
361
+ let fn = entry.queryFn;
362
+ fn && fetchSharedQuery(key, fn, { force: true });
363
+ }, nextMs));
364
+ }
365
+ function subscribeSharedQuery(key, onChange, cacheTimeMs) {
366
+ let entry = getOrCreateEntry(key);
367
+ return entry.subscribers.add(onChange), typeof cacheTimeMs == "number" && (entry.cacheTimeMs = Math.max(entry.cacheTimeMs, cacheTimeMs)), entry.gcTimeoutId && (clearTimeout(entry.gcTimeoutId), entry.gcTimeoutId = null), () => {
368
+ if (entry.subscribers.delete(onChange), entry.subscribers.size > 0) return;
369
+ entry.abortController && entry.abortController.abort(), entry.abortController = null, entry.promise = null, entry.intervalId && clearInterval(entry.intervalId), entry.intervalId = null, entry.activeIntervalMs = null, entry.intervalCounts.clear();
370
+ let gcDelayMs = entry.cacheTimeMs ?? DEFAULT_CACHE_TIME_MS;
371
+ entry.gcTimeoutId = setTimeout(() => {
372
+ entry.subscribers.size === 0 && store.delete(key);
373
+ }, gcDelayMs);
374
+ };
375
+ }
376
+ async function fetchSharedQuery(key, queryFn, options = {}) {
377
+ let entry = getOrCreateEntry(key);
378
+ entry.queryFn = queryFn;
379
+ let staleTimeMs = options.staleTimeMs ?? 0, now = Date.now();
380
+ if (options.force !== true && entry.snapshot.status === "success" && entry.snapshot.updatedAt !== null && now - entry.snapshot.updatedAt < staleTimeMs && entry.snapshot.data !== void 0)
381
+ return entry.snapshot.data;
382
+ if (entry.promise)
383
+ return entry.promise;
384
+ let controller = new AbortController();
385
+ entry.abortController = controller, options.signal && (options.signal.aborted ? controller.abort() : options.signal.addEventListener("abort", () => controller.abort(), { once: true }));
386
+ let isFirstLoad = entry.snapshot.status === "idle" && entry.snapshot.updatedAt === null;
387
+ setSnapshot(entry, {
388
+ ...entry.snapshot,
389
+ status: isFirstLoad ? "loading" : entry.snapshot.status,
390
+ isFetching: true,
391
+ error: null
392
+ });
393
+ let promise = (async () => {
394
+ try {
395
+ let data = await queryFn(controller.signal);
396
+ return setSnapshot(entry, {
397
+ data,
398
+ error: null,
399
+ status: "success",
400
+ updatedAt: Date.now(),
401
+ isFetching: false
402
+ }), data;
403
+ } catch (cause) {
404
+ if (controller.signal.aborted)
405
+ return setSnapshot(entry, {
406
+ data: entry.snapshot.data,
407
+ error: null,
408
+ status: entry.snapshot.status === "idle" ? "idle" : entry.snapshot.status,
409
+ updatedAt: entry.snapshot.updatedAt,
410
+ isFetching: false
411
+ }), entry.snapshot.data;
412
+ let error = cause instanceof Error ? cause : new Error(String(cause));
413
+ throw setSnapshot(entry, {
414
+ data: entry.snapshot.data,
415
+ error,
416
+ status: "error",
417
+ updatedAt: entry.snapshot.updatedAt,
418
+ isFetching: false
419
+ }), error;
420
+ } finally {
421
+ entry.promise = null, entry.abortController = null;
422
+ }
423
+ })();
424
+ return entry.promise = promise, promise;
425
+ }
426
+ function useSharedQuery(key, queryFn, options = {}) {
427
+ let {
428
+ enabled = true,
429
+ staleTimeMs = 0,
430
+ cacheTimeMs,
431
+ refetchOnMount = "stale",
432
+ refetchIntervalMs = false,
433
+ select
434
+ } = options, queryFnRef = useRef(queryFn);
435
+ queryFnRef.current = queryFn;
436
+ let stableQueryFn = useCallback((signal) => queryFnRef.current(signal), []), subscribe = useCallback(
437
+ (onChange) => key ? subscribeSharedQuery(key, onChange, cacheTimeMs) : () => {
438
+ },
439
+ [key, cacheTimeMs]
440
+ ), emptySnapshot = useMemo(
441
+ () => ({
442
+ data: void 0,
443
+ error: null,
444
+ status: "idle",
445
+ updatedAt: null,
446
+ isFetching: false
447
+ }),
448
+ []
449
+ ), getSnapshot = useCallback(() => key ? getOrCreateEntry(key).snapshot : emptySnapshot, [key, emptySnapshot]), snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot), selectedData = useMemo(() => select ? select(snapshot.data) : snapshot.data, [snapshot.data, select]), prevSelectedRef = useRef(selectedData), stableSelectedData = useMemo(() => Object.is(prevSelectedRef.current, selectedData) ? prevSelectedRef.current : (prevSelectedRef.current = selectedData, selectedData), [selectedData]), fetchedKeyRef = useRef(null);
450
+ useEffect(() => {
451
+ if (!key || !enabled) {
452
+ fetchedKeyRef.current = null;
313
453
  return;
314
454
  }
315
- setIsLoading(true), setError(null);
455
+ let entry = getOrCreateEntry(key);
456
+ if (entry.queryFn = stableQueryFn, fetchedKeyRef.current === key && entry.snapshot.status === "success")
457
+ return;
458
+ let current = entry.snapshot, isStale = current.updatedAt === null || Date.now() - current.updatedAt >= staleTimeMs;
459
+ refetchOnMount === true || current.status === "idle" || refetchOnMount === "stale" && isStale ? (fetchedKeyRef.current = key, fetchSharedQuery(key, stableQueryFn, {
460
+ staleTimeMs,
461
+ force: refetchOnMount === true
462
+ }).catch(() => {
463
+ })) : fetchedKeyRef.current = key;
464
+ }, [key, enabled, staleTimeMs, refetchOnMount, stableQueryFn]), useEffect(() => {
465
+ if (!key || !enabled || refetchIntervalMs === false) return;
466
+ let entry = getOrCreateEntry(key);
467
+ entry.queryFn = stableQueryFn;
468
+ let prev = entry.intervalCounts.get(refetchIntervalMs) ?? 0;
469
+ return entry.intervalCounts.set(refetchIntervalMs, prev + 1), startOrRestartInterval(key, entry), () => {
470
+ let count = entry.intervalCounts.get(refetchIntervalMs) ?? 0;
471
+ count <= 1 ? entry.intervalCounts.delete(refetchIntervalMs) : entry.intervalCounts.set(refetchIntervalMs, count - 1), startOrRestartInterval(key, entry);
472
+ };
473
+ }, [key, enabled, refetchIntervalMs, stableQueryFn]);
474
+ let refetch = useCallback(
475
+ async (refetchOptions) => {
476
+ if (key)
477
+ return fetchSharedQuery(key, stableQueryFn, {
478
+ force: true,
479
+ signal: refetchOptions?.signal
480
+ });
481
+ },
482
+ [key, stableQueryFn]
483
+ ), abort = useCallback(() => {
484
+ if (!key) return;
485
+ let entry = store.get(key);
486
+ entry?.abortController && entry.abortController.abort();
487
+ }, [key]);
488
+ return useMemo(
489
+ () => ({
490
+ data: stableSelectedData,
491
+ error: snapshot.error,
492
+ status: snapshot.status,
493
+ updatedAt: snapshot.updatedAt,
494
+ isFetching: snapshot.isFetching,
495
+ refetch,
496
+ abort
497
+ }),
498
+ [stableSelectedData, snapshot.error, snapshot.status, snapshot.updatedAt, snapshot.isFetching, refetch, abort]
499
+ );
500
+ }
501
+ function invalidateSharedQuery(key) {
502
+ let entry = store.get(key);
503
+ entry && setSnapshot(entry, {
504
+ ...entry.snapshot,
505
+ updatedAt: null
506
+ // Mark as stale
507
+ });
508
+ }
509
+ function clearSharedQueryCache() {
510
+ for (let [, entry] of store)
511
+ entry.intervalId && clearInterval(entry.intervalId), entry.gcTimeoutId && clearTimeout(entry.gcTimeoutId), entry.abortController && entry.abortController.abort();
512
+ store.clear();
513
+ }
514
+
515
+ // src/hooks/_internal/use-wallet-assets.ts
516
+ var TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", NATIVE_SOL_MINT = "So11111111111111111111111111111111111111112";
517
+ function isRecord(value) {
518
+ return typeof value == "object" && value !== null;
519
+ }
520
+ function parseBigInt(value) {
521
+ if (typeof value == "bigint") return value;
522
+ if (typeof value == "number" && Number.isSafeInteger(value)) return BigInt(value);
523
+ if (typeof value == "string")
316
524
  try {
317
- let rpc = rpcClient.rpc, walletAddress = address(address$1), balanceResult = await rpc.getBalance(walletAddress).send();
318
- setLamports(balanceResult.value);
319
- try {
320
- let tokenProgramId = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), tokenAccountsResult = await rpc.getTokenAccountsByOwner(walletAddress, { programId: tokenProgramId }, { encoding: "jsonParsed" }).send(), tokenBalances = [];
321
- for (let account of tokenAccountsResult.value) {
322
- let parsed = account.account.data;
323
- if (parsed?.parsed?.info) {
324
- let info = parsed.parsed.info, amount = BigInt(info.tokenAmount?.amount || "0"), decimals = info.tokenAmount?.decimals || 0, formatted = (Number(amount) / Math.pow(10, decimals)).toLocaleString(void 0, {
325
- minimumFractionDigits: 0,
326
- maximumFractionDigits: Math.min(decimals, 6)
327
- });
328
- amount > 0n && tokenBalances.push({
329
- mint: info.mint,
330
- amount,
331
- decimals,
332
- formatted
333
- });
334
- }
335
- }
336
- setTokens(tokenBalances);
337
- } catch (tokenError) {
338
- console.warn("Failed to fetch token balances:", tokenError), setTokens([]);
525
+ return BigInt(value);
526
+ } catch {
527
+ return 0n;
528
+ }
529
+ return 0n;
530
+ }
531
+ function getParsedTokenAccountInfo(data) {
532
+ if (!isRecord(data)) return null;
533
+ let parsed = data.parsed;
534
+ if (!isRecord(parsed)) return null;
535
+ let info = parsed.info;
536
+ return isRecord(info) ? info : null;
537
+ }
538
+ function parseTokenAccount(account, programId) {
539
+ let info = getParsedTokenAccountInfo(account.account.data);
540
+ if (!info) return null;
541
+ let mint = typeof info.mint == "string" ? info.mint : null, owner = typeof info.owner == "string" ? info.owner : null;
542
+ if (!mint || !owner) return null;
543
+ let tokenAmount = isRecord(info.tokenAmount) ? info.tokenAmount : null, amount = parseBigInt(tokenAmount?.amount), decimals = typeof tokenAmount?.decimals == "number" ? tokenAmount.decimals : 0, state = typeof info.state == "string" ? info.state : void 0;
544
+ return {
545
+ pubkey: String(account.pubkey),
546
+ mint,
547
+ owner,
548
+ amount,
549
+ decimals,
550
+ isFrozen: state === "frozen",
551
+ programId
552
+ };
553
+ }
554
+ function getWalletAssetsQueryKey(rpcUrl, address) {
555
+ return !rpcUrl || !address ? null : JSON.stringify(["wallet-assets", rpcUrl, address]);
556
+ }
557
+ function useWalletAssets(options = {}) {
558
+ let {
559
+ enabled = true,
560
+ staleTimeMs = 0,
561
+ cacheTimeMs = 300 * 1e3,
562
+ // 5 minutes
563
+ refetchOnMount = "stale",
564
+ refetchIntervalMs = false,
565
+ client: clientOverride,
566
+ select
567
+ } = options, { address: address$1, connected } = useAccount(), { client: providerClient } = useSolanaClient(), rpcClient = clientOverride ?? providerClient, key = useMemo(() => {
568
+ if (!enabled || !connected || !address$1 || !rpcClient) return null;
569
+ let rpcUrl = rpcClient.urlOrMoniker instanceof URL ? rpcClient.urlOrMoniker.toString() : String(rpcClient.urlOrMoniker);
570
+ return getWalletAssetsQueryKey(rpcUrl, address$1);
571
+ }, [enabled, connected, address$1, rpcClient]), queryFn = useCallback(
572
+ async (signal) => {
573
+ if (!connected || !address$1 || !rpcClient)
574
+ return { lamports: 0n, tokenAccounts: [] };
575
+ if (signal.aborted)
576
+ throw new DOMException("Query aborted", "AbortError");
577
+ let rpc = rpcClient.rpc, walletAddress = address(address$1), tokenProgramId = address(TOKEN_PROGRAM_ID), token2022ProgramId = address(TOKEN_2022_PROGRAM_ID), [balanceResult, tokenAccountsResult, token2022AccountsResult] = await Promise.all([
578
+ rpc.getBalance(walletAddress).send(),
579
+ rpc.getTokenAccountsByOwner(walletAddress, { programId: tokenProgramId }, { encoding: "jsonParsed" }).send(),
580
+ rpc.getTokenAccountsByOwner(
581
+ walletAddress,
582
+ { programId: token2022ProgramId },
583
+ { encoding: "jsonParsed" }
584
+ ).send()
585
+ ]);
586
+ if (signal.aborted)
587
+ throw new DOMException("Query aborted", "AbortError");
588
+ let tokenAccounts = [];
589
+ for (let account of tokenAccountsResult.value) {
590
+ let parsed = parseTokenAccount(account, "token");
591
+ parsed && tokenAccounts.push(parsed);
339
592
  }
340
- hasDataRef.current = true, setLastUpdated(/* @__PURE__ */ new Date());
341
- } catch (err) {
342
- hasDataRef.current || (setError(err), console.error("Failed to fetch balance:", err));
343
- } finally {
344
- setIsLoading(false);
593
+ for (let account of token2022AccountsResult.value) {
594
+ let parsed = parseTokenAccount(account, "token-2022");
595
+ parsed && tokenAccounts.push(parsed);
596
+ }
597
+ return {
598
+ lamports: balanceResult.value,
599
+ tokenAccounts
600
+ };
601
+ },
602
+ [connected, address$1, rpcClient]
603
+ ), { data, error, status, updatedAt, isFetching, refetch, abort } = useSharedQuery(
604
+ key,
605
+ queryFn,
606
+ {
607
+ enabled,
608
+ staleTimeMs,
609
+ cacheTimeMs,
610
+ refetchOnMount,
611
+ refetchIntervalMs,
612
+ select
345
613
  }
346
- }, [connected, address$1, rpcClient]);
347
- useEffect(() => {
348
- fetchBalance();
349
- }, [fetchBalance]), useEffect(() => {
350
- if (!connected) return;
351
- let interval = setInterval(fetchBalance, 3e4);
352
- return () => clearInterval(interval);
353
- }, [connected, fetchBalance]);
354
- let solBalance = useMemo(() => Number(lamports) / Number(LAMPORTS_PER_SOL2), [lamports]), formattedSol = useMemo(() => formatSol(lamports), [lamports]);
614
+ ), isLoading = status === "loading" || status === "idle";
615
+ return useMemo(
616
+ () => ({
617
+ data,
618
+ isLoading,
619
+ isFetching,
620
+ error,
621
+ refetch,
622
+ abort,
623
+ updatedAt
624
+ }),
625
+ [data, isLoading, isFetching, error, refetch, abort, updatedAt]
626
+ );
627
+ }
628
+
629
+ // src/hooks/use-balance.ts
630
+ function getBalanceQueryKey(rpcUrl, address) {
631
+ return getWalletAssetsQueryKey(rpcUrl, address);
632
+ }
633
+ var LAMPORTS_PER_SOL2 = 1000000000n;
634
+ function formatTokenAccount(account) {
635
+ let formatted = formatBigIntBalance(account.amount, account.decimals, {
636
+ maxDecimals: Math.min(account.decimals, 6)
637
+ });
638
+ return {
639
+ mint: account.mint,
640
+ amount: account.amount,
641
+ decimals: account.decimals,
642
+ formatted
643
+ };
644
+ }
645
+ function selectBalance(assets) {
646
+ if (!assets)
647
+ return { lamports: 0n, tokens: [] };
648
+ let tokens = assets.tokenAccounts.filter((account) => account.amount > 0n).map(formatTokenAccount);
649
+ return {
650
+ lamports: assets.lamports,
651
+ tokens
652
+ };
653
+ }
654
+ function useBalance(options = {}) {
655
+ let {
656
+ enabled = true,
657
+ autoRefresh = true,
658
+ refreshInterval = 3e4,
659
+ staleTimeMs = 0,
660
+ cacheTimeMs = 300 * 1e3,
661
+ // 5 minutes
662
+ refetchOnMount = "stale",
663
+ client: clientOverride
664
+ } = options, {
665
+ data,
666
+ error,
667
+ isFetching,
668
+ updatedAt,
669
+ refetch: sharedRefetch,
670
+ abort
671
+ } = useWalletAssets({
672
+ enabled,
673
+ staleTimeMs,
674
+ cacheTimeMs,
675
+ refetchOnMount,
676
+ refetchIntervalMs: autoRefresh ? refreshInterval : false,
677
+ client: clientOverride,
678
+ select: selectBalance
679
+ }), lamports = data?.lamports ?? 0n, tokens = data?.tokens ?? [], solBalance = useMemo(() => Number(lamports) / Number(LAMPORTS_PER_SOL2), [lamports]), formattedSol = useMemo(() => formatLamportsToSolSafe(lamports, { maxDecimals: 4, suffix: true }), [lamports]), visibleError = updatedAt ? null : error, refetch = useCallback(
680
+ async (opts) => {
681
+ await sharedRefetch(opts);
682
+ },
683
+ [sharedRefetch]
684
+ );
355
685
  return useMemo(
356
686
  () => ({
357
687
  solBalance,
358
688
  lamports,
359
689
  formattedSol,
360
690
  tokens,
361
- isLoading,
362
- error,
363
- refetch: fetchBalance,
364
- lastUpdated
691
+ isLoading: isFetching,
692
+ error: visibleError,
693
+ refetch,
694
+ abort,
695
+ lastUpdated: updatedAt ? new Date(updatedAt) : null
365
696
  }),
366
- [solBalance, lamports, formattedSol, tokens, isLoading, error, fetchBalance, lastUpdated]
697
+ [solBalance, lamports, formattedSol, tokens, isFetching, visibleError, refetch, abort, updatedAt]
367
698
  );
368
699
  }
700
+
701
+ // src/utils/abort.ts
702
+ function createTimeoutSignal(ms) {
703
+ if (typeof AbortSignal.timeout == "function")
704
+ return { signal: AbortSignal.timeout(ms), cleanup: () => {
705
+ } };
706
+ let controller = new AbortController(), timeoutId = setTimeout(() => controller.abort(), ms);
707
+ return {
708
+ signal: controller.signal,
709
+ cleanup: () => clearTimeout(timeoutId)
710
+ };
711
+ }
712
+
713
+ // src/hooks/_internal/solana-token-list.ts
714
+ var CLUSTER_CHAIN_IDS = {
715
+ mainnet: 101,
716
+ testnet: 102,
717
+ devnet: 103,
718
+ localnet: 103,
719
+ // Use devnet tokens for localnet
720
+ custom: 101
721
+ // Default to mainnet for custom clusters
722
+ }, TOKEN_LIST_API_BASE_URL = "https://token-list-api.solana.cloud/v1/mints";
723
+ function getTokenListApiUrl(cluster = "mainnet") {
724
+ let chainId = CLUSTER_CHAIN_IDS[cluster];
725
+ return `${TOKEN_LIST_API_BASE_URL}?chainId=${chainId}`;
726
+ }
727
+ var DEFAULT_TIMEOUT_MS = 1e4, TOKEN_LIST_CACHE_MAX_SIZE = 1500, MAX_ADDRESSES_PER_REQUEST = 100, tokenListCache = /* @__PURE__ */ new Map();
728
+ function getCachedTokenListMetadata(mint) {
729
+ let value = tokenListCache.get(mint);
730
+ if (value)
731
+ return tokenListCache.delete(mint), tokenListCache.set(mint, value), value;
732
+ }
733
+ function setCachedTokenListMetadata(mint, value) {
734
+ if (tokenListCache.has(mint) && tokenListCache.delete(mint), tokenListCache.set(mint, value), tokenListCache.size > TOKEN_LIST_CACHE_MAX_SIZE) {
735
+ let oldestKey = tokenListCache.keys().next().value;
736
+ oldestKey && tokenListCache.delete(oldestKey);
737
+ }
738
+ }
739
+ function createLinkedSignal(externalSignal, timeoutMs) {
740
+ let controller = new AbortController(), onAbort = () => controller.abort();
741
+ externalSignal && (externalSignal.aborted ? controller.abort() : externalSignal.addEventListener("abort", onAbort, { once: true }));
742
+ let { signal: timeoutSignal, cleanup: cleanupTimeout } = createTimeoutSignal(timeoutMs), onTimeoutAbort = () => controller.abort();
743
+ return timeoutSignal.aborted ? controller.abort() : timeoutSignal.addEventListener("abort", onTimeoutAbort, { once: true }), {
744
+ signal: controller.signal,
745
+ cleanup: () => {
746
+ cleanupTimeout(), externalSignal && externalSignal.removeEventListener("abort", onAbort), timeoutSignal.removeEventListener("abort", onTimeoutAbort);
747
+ }
748
+ };
749
+ }
750
+ async function fetchSolanaTokenListMetadata(mints, options = {}) {
751
+ let results = /* @__PURE__ */ new Map();
752
+ if (!mints.length) return results;
753
+ let seen = /* @__PURE__ */ new Set(), uncached = [];
754
+ for (let mint of mints) {
755
+ let normalized = mint?.trim();
756
+ if (!normalized || seen.has(normalized)) continue;
757
+ seen.add(normalized);
758
+ let cached = getCachedTokenListMetadata(normalized);
759
+ cached ? results.set(normalized, cached) : uncached.push(normalized);
760
+ }
761
+ if (!uncached.length) return results;
762
+ let timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS, apiUrl = getTokenListApiUrl(options.cluster);
763
+ for (let i = 0; i < uncached.length && !options.signal?.aborted; i += MAX_ADDRESSES_PER_REQUEST) {
764
+ let batch = uncached.slice(i, i + MAX_ADDRESSES_PER_REQUEST), { signal, cleanup } = createLinkedSignal(options.signal, timeoutMs);
765
+ try {
766
+ let response = await fetch(apiUrl, {
767
+ method: "POST",
768
+ headers: { "Content-Type": "application/json" },
769
+ body: JSON.stringify({ addresses: batch }),
770
+ signal
771
+ });
772
+ if (!response.ok) {
773
+ console.warn("[token-list] Solana Token List API error:", response.status, response.statusText);
774
+ continue;
775
+ }
776
+ let data = await response.json();
777
+ if (!data?.content?.length) continue;
778
+ for (let item of data.content)
779
+ item?.address && (results.set(item.address, item), setCachedTokenListMetadata(item.address, item));
780
+ } catch (error) {
781
+ console.warn("[token-list] Solana Token List API failed:", error);
782
+ } finally {
783
+ cleanup();
784
+ }
785
+ }
786
+ return results;
787
+ }
788
+
789
+ // src/utils/image.ts
790
+ function transformImageUrl(url, imageProxy) {
791
+ if (url)
792
+ return imageProxy ? `${imageProxy}${encodeURIComponent(url)}` : url;
793
+ }
794
+
795
+ // src/hooks/use-transactions.ts
796
+ function getTransactionsQueryKey(options) {
797
+ let { rpcUrl, address, clusterId, limit = 20, fetchDetails = false } = options;
798
+ return !rpcUrl || !address || !clusterId ? null : JSON.stringify(["wallet-transactions", rpcUrl, address, clusterId, limit, fetchDetails]);
799
+ }
369
800
  function formatDate(timestamp) {
370
801
  if (!timestamp)
371
802
  return { date: "Unknown", time: "" };
@@ -377,6 +808,45 @@ function formatDate(timestamp) {
377
808
  });
378
809
  return { date: formattedDate, time: formattedTime };
379
810
  }
811
+ var KNOWN_PROGRAMS = {
812
+ "11111111111111111111111111111111": "System Program",
813
+ TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: "Token Program",
814
+ ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL: "Associated Token",
815
+ JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4: "Jupiter",
816
+ whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc: "Orca Whirlpool",
817
+ "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8": "Raydium AMM",
818
+ Stake11111111111111111111111111111111111111: "Stake Program",
819
+ metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s: "Metaplex"
820
+ }, DEFAULT_IGNORED_PROGRAM_IDS = /* @__PURE__ */ new Set([
821
+ "11111111111111111111111111111111",
822
+ // System Program
823
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
824
+ // Token Program
825
+ "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
826
+ // Associated Token
827
+ ]);
828
+ function resolveProgramName(programId, programLabels) {
829
+ return programLabels?.[programId] ?? KNOWN_PROGRAMS[programId];
830
+ }
831
+ function pickPrimaryProgramId(programIds) {
832
+ for (let id of programIds)
833
+ if (!DEFAULT_IGNORED_PROGRAM_IDS.has(id)) return id;
834
+ return programIds.values().next().value;
835
+ }
836
+ function getParsedInstructionTypes(message) {
837
+ if (!Array.isArray(message.instructions)) return;
838
+ let types = [];
839
+ for (let ix of message.instructions) {
840
+ if (!ix || typeof ix != "object") continue;
841
+ let parsed = ix.parsed;
842
+ if (!parsed || typeof parsed != "object" || !("type" in parsed)) continue;
843
+ let t = parsed.type;
844
+ if (typeof t == "string" && (types.push(t), types.length >= 10))
845
+ break;
846
+ }
847
+ let unique = [...new Set(types)];
848
+ return unique.length ? unique : void 0;
849
+ }
380
850
  function isAccountKey(value) {
381
851
  return typeof value == "object" && value !== null && "pubkey" in value && typeof value.pubkey == "string";
382
852
  }
@@ -401,6 +871,13 @@ function isTransactionWithMeta(value) {
401
871
  function isTransactionMessage(value) {
402
872
  return typeof value == "object" && value !== null && "accountKeys" in value && Array.isArray(value.accountKeys);
403
873
  }
874
+ function coerceMaybeAddressString(value) {
875
+ if (typeof value == "string") return value;
876
+ if (value && typeof value == "object") {
877
+ let str = String(value);
878
+ if (str && str !== "[object Object]") return str;
879
+ }
880
+ }
404
881
  function getAccountKeys(message) {
405
882
  return Array.isArray(message.accountKeys) ? message.accountKeys.map((key) => typeof key == "string" ? key : isAccountKey(key) ? key.pubkey : "").filter(Boolean) : [];
406
883
  }
@@ -432,12 +909,12 @@ function parseTokenTransfers(meta, accountKeys, walletAddress) {
432
909
  return null;
433
910
  let preTokenBalances = Array.isArray(meta.preTokenBalances) ? meta.preTokenBalances : [], postTokenBalances = Array.isArray(meta.postTokenBalances) ? meta.postTokenBalances : [], ourPreTokens = preTokenBalances.filter((balance) => {
434
911
  if (!isTokenBalance(balance)) return false;
435
- let accountKey = accountKeys[balance.accountIndex];
436
- return accountKey && accountKey.trim() === walletAddress.trim() || balance.owner && balance.owner.trim() === walletAddress.trim();
912
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
913
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
437
914
  }), ourPostTokens = postTokenBalances.filter((balance) => {
438
915
  if (!isTokenBalance(balance)) return false;
439
- let accountKey = accountKeys[balance.accountIndex];
440
- return accountKey && accountKey.trim() === walletAddress.trim() || balance.owner && balance.owner.trim() === walletAddress.trim();
916
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
917
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
441
918
  }), allMints = /* @__PURE__ */ new Set();
442
919
  for (let token of ourPreTokens)
443
920
  isTokenBalance(token) && allMints.add(token.mint);
@@ -481,6 +958,52 @@ function parseTokenTransfers(meta, accountKeys, walletAddress) {
481
958
  }
482
959
  return null;
483
960
  }
961
+ function parseTokenAccountClosure(meta, accountKeys, walletAddress) {
962
+ if (!isTransactionMeta(meta))
963
+ return null;
964
+ let preTokenBalances = Array.isArray(meta.preTokenBalances) ? meta.preTokenBalances : [], postTokenBalances = Array.isArray(meta.postTokenBalances) ? meta.postTokenBalances : [], ourPreTokens = preTokenBalances.filter((balance) => {
965
+ if (!isTokenBalance(balance)) return false;
966
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
967
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
968
+ }), ourPostTokens = postTokenBalances.filter((balance) => {
969
+ if (!isTokenBalance(balance)) return false;
970
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
971
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
972
+ }), postKeys = /* @__PURE__ */ new Set();
973
+ for (let token of ourPostTokens)
974
+ isTokenBalance(token) && postKeys.add(`${token.accountIndex}:${token.mint}`);
975
+ for (let token of ourPreTokens) {
976
+ if (!isTokenBalance(token)) continue;
977
+ let key = `${token.accountIndex}:${token.mint}`;
978
+ if (!postKeys.has(key))
979
+ return { tokenMint: token.mint };
980
+ }
981
+ return null;
982
+ }
983
+ function parseSwapTokens(meta, accountKeys, walletAddress, solChange) {
984
+ if (!isTransactionMeta(meta))
985
+ return {};
986
+ let preTokenBalances = Array.isArray(meta.preTokenBalances) ? meta.preTokenBalances : [], postTokenBalances = Array.isArray(meta.postTokenBalances) ? meta.postTokenBalances : [], ourPreTokens = preTokenBalances.filter((balance) => {
987
+ if (!isTokenBalance(balance)) return false;
988
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
989
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
990
+ }), ourPostTokens = postTokenBalances.filter((balance) => {
991
+ if (!isTokenBalance(balance)) return false;
992
+ let accountKey = accountKeys[balance.accountIndex], owner = coerceMaybeAddressString(balance.owner);
993
+ return accountKey && accountKey.trim() === walletAddress.trim() || owner && owner.trim() === walletAddress.trim();
994
+ }), allMints = /* @__PURE__ */ new Set();
995
+ for (let token of ourPreTokens)
996
+ isTokenBalance(token) && allMints.add(token.mint);
997
+ for (let token of ourPostTokens)
998
+ isTokenBalance(token) && allMints.add(token.mint);
999
+ let fromToken, toToken;
1000
+ for (let mint of allMints) {
1001
+ let preBal = ourPreTokens.find((b) => isTokenBalance(b) && b.mint === mint), postBal = ourPostTokens.find((b) => isTokenBalance(b) && b.mint === mint), preAmount = isTokenBalance(preBal) && isUiTokenAmount(preBal.uiTokenAmount) ? Number(preBal.uiTokenAmount.amount) : 0, change = (isTokenBalance(postBal) && isUiTokenAmount(postBal.uiTokenAmount) ? Number(postBal.uiTokenAmount.amount) : 0) - preAmount;
1002
+ change < 0 && !fromToken ? fromToken = { mint } : change > 0 && !toToken && (toToken = { mint });
1003
+ }
1004
+ let WRAPPED_SOL_MINT = "So11111111111111111111111111111111111111112";
1005
+ return solChange < -1e-3 && !fromToken ? fromToken = { mint: WRAPPED_SOL_MINT } : solChange > 1e-3 && !toToken && (toToken = { mint: WRAPPED_SOL_MINT }), { fromToken, toToken };
1006
+ }
484
1007
  function formatAmount(tokenAmount, tokenDecimals, direction, solChange) {
485
1008
  if (tokenAmount !== void 0 && tokenDecimals !== void 0 && direction !== void 0) {
486
1009
  let sign = direction === "in" ? "+" : "-", maxDecimals = Math.min(tokenDecimals, 6);
@@ -489,52 +1012,59 @@ function formatAmount(tokenAmount, tokenDecimals, direction, solChange) {
489
1012
  if (solChange !== 0)
490
1013
  return `${solChange > 0 ? "+" : ""}${solChange.toFixed(4)} SOL`;
491
1014
  }
492
- var tokenMetadataCache = /* @__PURE__ */ new Map();
493
- function transformImageUrl(url, imageProxy) {
494
- if (url)
495
- return imageProxy ? `${imageProxy}${encodeURIComponent(url)}` : url;
1015
+ function throwIfAborted(signal) {
1016
+ if (signal?.aborted)
1017
+ throw new DOMException("Query aborted", "AbortError");
496
1018
  }
497
- async function fetchTokenMetadata(mints) {
498
- let results = /* @__PURE__ */ new Map();
499
- if (mints.length === 0) return results;
500
- let uncachedMints = [];
501
- for (let mint of mints) {
502
- let cached = tokenMetadataCache.get(mint);
503
- cached ? results.set(mint, cached) : uncachedMints.push(mint);
504
- }
505
- if (uncachedMints.length === 0) return results;
506
- try {
507
- let response = await fetch("https://token-list-api.solana.cloud/v1/mints?chainId=101", {
508
- method: "POST",
509
- headers: { "Content-Type": "application/json" },
510
- body: JSON.stringify({ addresses: uncachedMints }),
511
- signal: AbortSignal.timeout(5e3)
512
- });
513
- if (!response.ok) return results;
514
- let data = await response.json();
515
- for (let item of data.content) {
516
- let metadata = { symbol: item.symbol, icon: item.logoURI };
517
- results.set(item.address, metadata), tokenMetadataCache.set(item.address, metadata);
1019
+ function clampInt(value, min, max) {
1020
+ return Number.isFinite(value) ? Math.min(max, Math.max(min, Math.floor(value))) : min;
1021
+ }
1022
+ async function mapWithConcurrency(inputs, worker, options) {
1023
+ let concurrency = clampInt(options.concurrency, 1, 32), results = new Array(inputs.length), nextIndex = 0;
1024
+ async function run() {
1025
+ for (; ; ) {
1026
+ throwIfAborted(options.signal);
1027
+ let index = nextIndex;
1028
+ if (nextIndex += 1, index >= inputs.length) return;
1029
+ results[index] = await worker(inputs[index], index);
518
1030
  }
519
- } catch (error) {
520
- console.warn("[useTransactions] Failed to fetch token metadata:", error);
521
1031
  }
1032
+ let runners = Array.from({ length: Math.min(concurrency, inputs.length) }, () => run());
1033
+ return await Promise.all(runners), results;
1034
+ }
1035
+ async function fetchTransactionTokenMetadata(mints, options = {}) {
1036
+ let results = /* @__PURE__ */ new Map();
1037
+ if (!mints.length) return results;
1038
+ let tokenList = await fetchSolanaTokenListMetadata(mints, {
1039
+ timeoutMs: 5e3,
1040
+ signal: options.signal,
1041
+ cluster: options.cluster
1042
+ });
1043
+ for (let [mint, meta] of tokenList)
1044
+ results.set(mint, { symbol: meta.symbol, icon: meta.logoURI });
522
1045
  return results;
523
1046
  }
524
1047
  function useTransactions(options = {}) {
525
- let { limit = 10, autoRefresh = false, refreshInterval = 6e4, fetchDetails = true } = options, { address: address$1, connected } = useAccount(), { cluster } = useCluster(), client = useSolanaClient(), connectorClient = useConnectorClient(), [transactions, setTransactions] = useState([]), [isLoading, setIsLoading] = useState(false), [error, setError] = useState(null), [hasMore, setHasMore] = useState(true), [lastUpdated, setLastUpdated] = useState(null), beforeSignatureRef = useRef(void 0), prevDepsRef = useRef(
526
- null
527
- ), rpcClient = client?.client ?? null, imageProxy = connectorClient?.getConfig().imageProxy, parseTransaction = useCallback(
1048
+ let {
1049
+ enabled = true,
1050
+ limit = 10,
1051
+ autoRefresh = false,
1052
+ refreshInterval = 6e4,
1053
+ fetchDetails = true,
1054
+ detailsConcurrency = 6,
1055
+ staleTimeMs = 0,
1056
+ cacheTimeMs = 300 * 1e3,
1057
+ // 5 minutes
1058
+ refetchOnMount = "stale",
1059
+ client: clientOverride
1060
+ } = options, { address: address$1, connected } = useAccount(), { cluster } = useCluster(), { client: providerClient } = useSolanaClient(), connectorClient = useConnectorClient(), [paginatedTransactions, setPaginatedTransactions] = useState([]), [isPaginationLoading, setIsPaginationLoading] = useState(false), [hasMore, setHasMore] = useState(true), beforeSignatureRef = useRef(void 0), rpcClient = clientOverride ?? providerClient, connectorConfig = connectorClient?.getConfig(), imageProxy = connectorConfig?.imageProxy, programLabels = connectorConfig?.programLabels, parseTransaction = useCallback(
528
1061
  (tx, walletAddress, sig, blockTime, slot, err, explorerUrl) => {
529
1062
  let { date, time } = formatDate(blockTime), baseInfo = {
530
1063
  signature: sig,
531
1064
  blockTime,
532
1065
  slot,
533
1066
  status: err ? "failed" : "success",
534
- error: err ? JSON.stringify(
535
- err,
536
- (_key, value) => typeof value == "bigint" ? value.toString() : value
537
- ) : void 0,
1067
+ error: err ? JSON.stringify(err, (_key, value) => typeof value == "bigint" ? value.toString() : value) : void 0,
538
1068
  type: "unknown",
539
1069
  formattedDate: date,
540
1070
  formattedTime: time,
@@ -549,24 +1079,13 @@ function useTransactions(options = {}) {
549
1079
  let { message } = transaction;
550
1080
  if (!isTransactionMessage(message))
551
1081
  return baseInfo;
552
- let accountKeys = getAccountKeys(message), walletIndex = accountKeys.findIndex((key) => key.trim() === walletAddress.trim());
1082
+ let accountKeys = getAccountKeys(message), walletIndex = accountKeys.findIndex((key2) => key2.trim() === walletAddress.trim());
553
1083
  if (walletIndex === -1)
554
1084
  return baseInfo;
555
- let { balanceChange, solChange } = parseSolChange(meta, walletIndex), programIds = detectProgramIds(message, accountKeys), hasJupiter = programIds.has("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"), hasOrca = programIds.has("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"), hasRaydium = programIds.has("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"), hasStake = programIds.has("Stake11111111111111111111111111111111111111"), hasMetaplex = programIds.has("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), hasSystemProgram = programIds.has("11111111111111111111111111111111"), hasTokenProgram = programIds.has("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), type = "unknown", direction, counterparty, tokenMint, tokenAmount, tokenDecimals;
556
- if (hasJupiter || hasOrca || hasRaydium)
557
- type = "swap";
558
- else if (hasStake)
559
- type = "stake";
560
- else if (hasMetaplex)
561
- type = "nft";
562
- else if (hasSystemProgram && Math.abs(balanceChange) > 0)
563
- type = balanceChange > 0 ? "received" : "sent", direction = balanceChange > 0 ? "in" : "out", tokenMint = "So11111111111111111111111111111111111111112", accountKeys.length >= 2 && (counterparty = accountKeys.find(
564
- (key, idx) => idx !== walletIndex && key !== "11111111111111111111111111111111"
565
- ));
566
- else if (hasTokenProgram) {
567
- let tokenTransfer = parseTokenTransfers(meta, accountKeys, walletAddress);
568
- tokenTransfer && (type = tokenTransfer.type, direction = tokenTransfer.direction, tokenMint = tokenTransfer.tokenMint, tokenAmount = tokenTransfer.tokenAmount, tokenDecimals = tokenTransfer.tokenDecimals);
569
- } else programIds.size > 0 && (type = "program");
1085
+ let { balanceChange, solChange } = parseSolChange(meta, walletIndex), programIds = detectProgramIds(message, accountKeys), hasJupiter = programIds.has("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"), hasOrca = programIds.has("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"), hasRaydium = programIds.has("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"), hasStake = programIds.has("Stake11111111111111111111111111111111111111"), hasMetaplex = programIds.has("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), hasSystemProgram = programIds.has("11111111111111111111111111111111"), hasTokenProgram = programIds.has("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), tokenTransfer = hasTokenProgram ? parseTokenTransfers(meta, accountKeys, walletAddress) : null, tokenAccountClosure = hasTokenProgram ? parseTokenAccountClosure(meta, accountKeys, walletAddress) : null, inferredSwapTokens = parseSwapTokens(meta, accountKeys, walletAddress, solChange), inferredSwapFromMint = inferredSwapTokens.fromToken?.mint, inferredSwapToMint = inferredSwapTokens.toToken?.mint, hasNonTrivialProgram = [...programIds].some((id) => !DEFAULT_IGNORED_PROGRAM_IDS.has(id)), hasInferredSwap = !!(inferredSwapFromMint && inferredSwapToMint) && inferredSwapFromMint !== inferredSwapToMint && hasNonTrivialProgram && !tokenAccountClosure, programId = pickPrimaryProgramId(programIds), programName = programId ? resolveProgramName(programId, programLabels) : void 0, programIdsArray = [...programIds], instructionTypes = getParsedInstructionTypes(message), instructionCount = Array.isArray(message.instructions) ? message.instructions.length : void 0, type = "unknown", direction, counterparty, tokenMint, tokenAmount, tokenDecimals, swapFromToken, swapToToken;
1086
+ hasJupiter || hasOrca || hasRaydium ? (type = "swap", inferredSwapTokens.fromToken && (swapFromToken = { mint: inferredSwapTokens.fromToken.mint }), inferredSwapTokens.toToken && (swapToToken = { mint: inferredSwapTokens.toToken.mint })) : hasStake ? type = "stake" : hasMetaplex ? type = "nft" : hasInferredSwap ? (type = "swap", swapFromToken = { mint: inferredSwapFromMint }, swapToToken = { mint: inferredSwapToMint }) : tokenTransfer ? (type = tokenTransfer.type, direction = tokenTransfer.direction, tokenMint = tokenTransfer.tokenMint, tokenAmount = tokenTransfer.tokenAmount, tokenDecimals = tokenTransfer.tokenDecimals) : tokenAccountClosure ? (type = "tokenAccountClosed", tokenMint = tokenAccountClosure.tokenMint, direction = solChange > 0 ? "in" : void 0) : hasSystemProgram && Math.abs(balanceChange) > 0 ? (type = balanceChange > 0 ? "received" : "sent", direction = balanceChange > 0 ? "in" : "out", tokenMint = "So11111111111111111111111111111111111111112", accountKeys.length >= 2 && (counterparty = accountKeys.find(
1087
+ (key2, idx) => idx !== walletIndex && key2 !== "11111111111111111111111111111111"
1088
+ ))) : programIds.size > 0 && (type = "program");
570
1089
  let formattedAmount = formatAmount(tokenAmount, tokenDecimals, direction, solChange);
571
1090
  return {
572
1091
  ...baseInfo,
@@ -575,131 +1094,192 @@ function useTransactions(options = {}) {
575
1094
  amount: tokenAmount ?? Math.abs(solChange),
576
1095
  formattedAmount,
577
1096
  tokenMint,
578
- counterparty: counterparty ? `${counterparty.slice(0, 4)}...${counterparty.slice(-4)}` : void 0
1097
+ counterparty: counterparty ? `${counterparty.slice(0, 4)}...${counterparty.slice(-4)}` : void 0,
1098
+ swapFromToken,
1099
+ swapToToken,
1100
+ programId,
1101
+ programName,
1102
+ programIds: programIdsArray.length ? programIdsArray : void 0,
1103
+ instructionTypes,
1104
+ instructionCount
579
1105
  };
580
1106
  } catch (parseError) {
581
1107
  return console.warn("Failed to parse transaction:", parseError), baseInfo;
582
1108
  }
583
1109
  },
584
- []
585
- ), fetchTransactions = useCallback(
586
- async (loadMore = false) => {
587
- if (!connected || !address$1 || !rpcClient || !cluster) {
588
- setTransactions([]);
589
- return;
590
- }
591
- setIsLoading(true), setError(null);
592
- try {
593
- let rpc = rpcClient.rpc, walletAddress = address(address$1), signaturesResult = await rpc.getSignaturesForAddress(walletAddress, {
594
- limit,
595
- ...loadMore && beforeSignatureRef.current ? { before: signature(beforeSignatureRef.current) } : {}
596
- }).send(), newTransactions;
597
- if (fetchDetails && signaturesResult.length > 0) {
598
- let txPromises = signaturesResult.map(
599
- (s) => rpc.getTransaction(signature(String(s.signature)), {
600
- encoding: "jsonParsed",
601
- maxSupportedTransactionVersion: 0
602
- }).send().catch(() => null)
603
- ), txDetails = await Promise.all(txPromises);
604
- newTransactions = signaturesResult.map((sig, idx) => {
605
- let blockTimeNum = sig.blockTime ? Number(sig.blockTime) : null, tx = txDetails[idx];
606
- return parseTransaction(
607
- tx,
608
- address$1,
609
- String(sig.signature),
610
- blockTimeNum,
611
- Number(sig.slot),
612
- sig.err,
613
- getTransactionUrl(String(sig.signature), cluster)
614
- );
615
- });
616
- } else
617
- newTransactions = signaturesResult.map((sig) => {
618
- let blockTimeNum = sig.blockTime ? Number(sig.blockTime) : null, { date, time } = formatDate(blockTimeNum);
619
- return {
620
- signature: String(sig.signature),
621
- blockTime: blockTimeNum,
622
- slot: Number(sig.slot),
623
- status: sig.err ? "failed" : "success",
624
- error: sig.err ? JSON.stringify(sig.err) : void 0,
625
- type: "unknown",
626
- formattedDate: date,
627
- formattedTime: time,
628
- explorerUrl: getTransactionUrl(String(sig.signature), cluster)
1110
+ [programLabels]
1111
+ ), key = useMemo(() => {
1112
+ if (!enabled || !connected || !address$1 || !rpcClient || !cluster) return null;
1113
+ let rpcUrl = rpcClient.urlOrMoniker instanceof URL ? rpcClient.urlOrMoniker.toString() : String(rpcClient.urlOrMoniker);
1114
+ return getTransactionsQueryKey({ rpcUrl, address: address$1, clusterId: cluster.id, limit, fetchDetails });
1115
+ }, [enabled, connected, address$1, rpcClient, cluster, limit, fetchDetails]);
1116
+ useEffect(() => {
1117
+ beforeSignatureRef.current = void 0, setPaginatedTransactions([]), setIsPaginationLoading(false), setHasMore(true);
1118
+ }, [key]);
1119
+ let fetchAndEnrichTransactions = useCallback(
1120
+ async (beforeSignature, currentCluster, signal) => {
1121
+ if (!rpcClient || !address$1)
1122
+ return { transactions: [], hasMore: false };
1123
+ throwIfAborted(signal);
1124
+ let rpc = rpcClient.rpc, walletAddress = address(address$1), signaturesResult = await rpc.getSignaturesForAddress(walletAddress, {
1125
+ limit,
1126
+ ...beforeSignature ? { before: signature(beforeSignature) } : {}
1127
+ }).send();
1128
+ throwIfAborted(signal);
1129
+ let newTransactions;
1130
+ if (fetchDetails && signaturesResult.length > 0) {
1131
+ let txDetails = await mapWithConcurrency(
1132
+ signaturesResult,
1133
+ async (sig) => rpc.getTransaction(signature(String(sig.signature)), {
1134
+ encoding: "jsonParsed",
1135
+ maxSupportedTransactionVersion: 0
1136
+ }).send().catch(() => null),
1137
+ { concurrency: detailsConcurrency, signal }
1138
+ );
1139
+ throwIfAborted(signal), newTransactions = signaturesResult.map((sig, idx) => {
1140
+ let blockTimeNum = sig.blockTime ? Number(sig.blockTime) : null, tx = txDetails[idx];
1141
+ return parseTransaction(
1142
+ tx,
1143
+ address$1,
1144
+ String(sig.signature),
1145
+ blockTimeNum,
1146
+ Number(sig.slot),
1147
+ sig.err,
1148
+ getTransactionUrl(String(sig.signature), currentCluster)
1149
+ );
1150
+ });
1151
+ } else
1152
+ newTransactions = signaturesResult.map((sig) => {
1153
+ let blockTimeNum = sig.blockTime ? Number(sig.blockTime) : null, { date, time } = formatDate(blockTimeNum);
1154
+ return {
1155
+ signature: String(sig.signature),
1156
+ blockTime: blockTimeNum,
1157
+ slot: Number(sig.slot),
1158
+ status: sig.err ? "failed" : "success",
1159
+ error: sig.err ? JSON.stringify(sig.err) : void 0,
1160
+ type: "unknown",
1161
+ formattedDate: date,
1162
+ formattedTime: time,
1163
+ explorerUrl: getTransactionUrl(String(sig.signature), currentCluster)
1164
+ };
1165
+ });
1166
+ let mintsToFetch = [
1167
+ .../* @__PURE__ */ new Set([
1168
+ ...newTransactions.filter((tx) => tx.tokenMint).map((tx) => tx.tokenMint),
1169
+ ...newTransactions.filter((tx) => tx.swapFromToken?.mint).map((tx) => tx.swapFromToken.mint),
1170
+ ...newTransactions.filter((tx) => tx.swapToToken?.mint).map((tx) => tx.swapToToken.mint)
1171
+ ])
1172
+ ];
1173
+ if (mintsToFetch.length > 0) {
1174
+ throwIfAborted(signal);
1175
+ let tokenMetadata = await fetchTransactionTokenMetadata(mintsToFetch, {
1176
+ signal,
1177
+ cluster: getClusterType(currentCluster)
1178
+ });
1179
+ tokenMetadata.size > 0 && (newTransactions = newTransactions.map((tx) => {
1180
+ let enrichedTx = { ...tx };
1181
+ if (tx.tokenMint && tokenMetadata.has(tx.tokenMint)) {
1182
+ let meta = tokenMetadata.get(tx.tokenMint);
1183
+ enrichedTx = {
1184
+ ...enrichedTx,
1185
+ tokenSymbol: meta.symbol,
1186
+ tokenIcon: transformImageUrl(meta.icon, imageProxy),
1187
+ formattedAmount: tx.formattedAmount ? `${tx.formattedAmount} ${meta.symbol}` : tx.formattedAmount
629
1188
  };
630
- });
631
- setTransactions(loadMore ? (prev) => [...prev, ...newTransactions] : newTransactions);
632
- let mintsToFetch = [...new Set(newTransactions.filter((tx) => tx.tokenMint).map((tx) => tx.tokenMint))];
633
- if (mintsToFetch.length > 0) {
634
- let tokenMetadata = await fetchTokenMetadata(mintsToFetch);
635
- if (tokenMetadata.size > 0) {
636
- let enrichedTransactions = newTransactions.map((tx) => {
637
- if (tx.tokenMint && tokenMetadata.has(tx.tokenMint)) {
638
- let meta = tokenMetadata.get(tx.tokenMint);
639
- return {
640
- ...tx,
641
- tokenSymbol: meta.symbol,
642
- tokenIcon: transformImageUrl(meta.icon, imageProxy),
643
- // Update formatted amount with symbol
644
- formattedAmount: tx.formattedAmount ? `${tx.formattedAmount} ${meta.symbol}` : tx.formattedAmount
645
- };
1189
+ }
1190
+ if (tx.swapFromToken?.mint && tokenMetadata.has(tx.swapFromToken.mint)) {
1191
+ let meta = tokenMetadata.get(tx.swapFromToken.mint);
1192
+ enrichedTx = {
1193
+ ...enrichedTx,
1194
+ swapFromToken: {
1195
+ ...tx.swapFromToken,
1196
+ symbol: meta.symbol,
1197
+ icon: transformImageUrl(meta.icon, imageProxy)
646
1198
  }
647
- return tx;
648
- });
649
- setTransactions(loadMore ? (prev) => [...prev.slice(0, -newTransactions.length), ...enrichedTransactions] : enrichedTransactions);
1199
+ };
650
1200
  }
651
- }
652
- if (typeof newTransactions < "u" && Array.isArray(newTransactions)) {
653
- if (newTransactions.length > 0) {
654
- let newBeforeSignature = newTransactions[newTransactions.length - 1].signature;
655
- beforeSignatureRef.current = newBeforeSignature;
1201
+ if (tx.swapToToken?.mint && tokenMetadata.has(tx.swapToToken.mint)) {
1202
+ let meta = tokenMetadata.get(tx.swapToToken.mint);
1203
+ enrichedTx = {
1204
+ ...enrichedTx,
1205
+ swapToToken: {
1206
+ ...tx.swapToToken,
1207
+ symbol: meta.symbol,
1208
+ icon: transformImageUrl(meta.icon, imageProxy)
1209
+ }
1210
+ };
656
1211
  }
657
- setHasMore(newTransactions.length === limit);
658
- }
659
- setLastUpdated(/* @__PURE__ */ new Date());
1212
+ return enrichedTx;
1213
+ }));
1214
+ }
1215
+ return {
1216
+ transactions: newTransactions,
1217
+ hasMore: newTransactions.length === limit
1218
+ };
1219
+ },
1220
+ [rpcClient, address$1, limit, fetchDetails, detailsConcurrency, parseTransaction, imageProxy]
1221
+ ), queryFn = useCallback(
1222
+ async (signal) => {
1223
+ if (!connected || !address$1 || !rpcClient || !cluster)
1224
+ return [];
1225
+ throwIfAborted(signal);
1226
+ let result = await fetchAndEnrichTransactions(void 0, cluster, signal);
1227
+ return throwIfAborted(signal), result.transactions;
1228
+ },
1229
+ [connected, address$1, rpcClient, cluster, fetchAndEnrichTransactions]
1230
+ ), {
1231
+ data: initialTransactions,
1232
+ error,
1233
+ isFetching: isInitialLoading,
1234
+ updatedAt,
1235
+ refetch: sharedRefetch,
1236
+ abort
1237
+ } = useSharedQuery(key, queryFn, {
1238
+ enabled,
1239
+ staleTimeMs,
1240
+ cacheTimeMs,
1241
+ refetchOnMount,
1242
+ refetchIntervalMs: autoRefresh ? refreshInterval : false
1243
+ });
1244
+ useEffect(() => {
1245
+ initialTransactions && (beforeSignatureRef.current = initialTransactions.length ? initialTransactions[initialTransactions.length - 1].signature : void 0, setHasMore(initialTransactions.length === limit), setPaginatedTransactions((prev) => prev.length ? [] : prev));
1246
+ }, [initialTransactions, limit]);
1247
+ let loadMoreFn = useCallback(async () => {
1248
+ if (!(!hasMore || isPaginationLoading || !cluster)) {
1249
+ setIsPaginationLoading(true);
1250
+ try {
1251
+ let result = await fetchAndEnrichTransactions(beforeSignatureRef.current, cluster);
1252
+ result.transactions.length > 0 && (beforeSignatureRef.current = result.transactions[result.transactions.length - 1].signature, setPaginatedTransactions((prev) => [...prev, ...result.transactions])), setHasMore(result.hasMore);
660
1253
  } catch (err) {
661
- setError(err), console.error("Failed to fetch transactions:", err);
1254
+ console.error("Failed to load more transactions:", err);
662
1255
  } finally {
663
- setIsLoading(false);
1256
+ setIsPaginationLoading(false);
664
1257
  }
1258
+ }
1259
+ }, [hasMore, isPaginationLoading, cluster, fetchAndEnrichTransactions]), refetch = useCallback(
1260
+ async (opts) => {
1261
+ beforeSignatureRef.current = void 0, setPaginatedTransactions([]), setHasMore(true), await sharedRefetch(opts);
665
1262
  },
666
- [connected, address$1, rpcClient, cluster, limit, fetchDetails, parseTransaction, imageProxy]
667
- ), refetch = useCallback(async () => {
668
- beforeSignatureRef.current = void 0, await fetchTransactions(false);
669
- }, [fetchTransactions]), loadMoreFn = useCallback(async () => {
670
- hasMore && !isLoading && await fetchTransactions(true);
671
- }, [hasMore, isLoading, fetchTransactions]);
672
- return useEffect(() => {
673
- let prevDeps = prevDepsRef.current, currentDeps = { connected, address: address$1, cluster };
674
- (!prevDeps || prevDeps.connected !== connected || prevDeps.address !== address$1 || prevDeps.cluster !== cluster) && (prevDepsRef.current = currentDeps, beforeSignatureRef.current = void 0, fetchTransactions(false));
675
- }, [connected, address$1, cluster, fetchTransactions]), useEffect(() => {
676
- if (!connected || !autoRefresh) return;
677
- let interval = setInterval(refetch, refreshInterval);
678
- return () => clearInterval(interval);
679
- }, [connected, autoRefresh, refreshInterval, refetch]), useMemo(
1263
+ [sharedRefetch]
1264
+ ), transactions = useMemo(() => [...initialTransactions ?? [], ...paginatedTransactions], [initialTransactions, paginatedTransactions]), isLoading = isInitialLoading || isPaginationLoading, visibleError = updatedAt ? null : error;
1265
+ return useMemo(
680
1266
  () => ({
681
1267
  transactions,
682
1268
  isLoading,
683
- error,
1269
+ error: visibleError,
684
1270
  hasMore,
685
1271
  loadMore: loadMoreFn,
686
1272
  refetch,
687
- lastUpdated
1273
+ abort,
1274
+ lastUpdated: updatedAt ? new Date(updatedAt) : null
688
1275
  }),
689
- [transactions, isLoading, error, hasMore, loadMoreFn, refetch, lastUpdated]
1276
+ [transactions, isLoading, visibleError, hasMore, loadMoreFn, refetch, abort, updatedAt]
690
1277
  );
691
1278
  }
692
- function createTimeoutSignal(ms) {
693
- if (typeof AbortSignal.timeout == "function")
694
- return { signal: AbortSignal.timeout(ms), cleanup: () => {
695
- } };
696
- let controller = new AbortController(), timeoutId = setTimeout(() => controller.abort(), ms);
697
- return {
698
- signal: controller.signal,
699
- cleanup: () => clearTimeout(timeoutId)
700
- };
1279
+ function getTokensQueryKey(rpcUrl, address) {
1280
+ return getWalletAssetsQueryKey(rpcUrl, address);
701
1281
  }
702
- var NATIVE_MINT = "So11111111111111111111111111111111111111112", CACHE_MAX_SIZE = 500, PRICE_CACHE_TTL = 6e4, STALE_CLEANUP_INTERVAL = 12e4, COINGECKO_DEFAULT_MAX_RETRIES = 3, COINGECKO_DEFAULT_BASE_DELAY = 1e3, COINGECKO_DEFAULT_MAX_TIMEOUT = 3e4, COINGECKO_API_BASE_URL = "https://api.coingecko.com/api/v3", LRUCache = class {
1282
+ var CACHE_MAX_SIZE = 500, PRICE_CACHE_TTL = 6e4, STALE_CLEANUP_INTERVAL = 12e4, COINGECKO_DEFAULT_MAX_RETRIES = 3, COINGECKO_DEFAULT_BASE_DELAY = 1e3, COINGECKO_DEFAULT_MAX_TIMEOUT = 3e4, COINGECKO_API_BASE_URL = "https://api.coingecko.com/api/v3", LRUCache = class {
703
1283
  constructor(maxSize, options) {
704
1284
  __publicField(this, "cache", /* @__PURE__ */ new Map());
705
1285
  __publicField(this, "maxSize");
@@ -739,10 +1319,6 @@ var NATIVE_MINT = "So11111111111111111111111111111111111111112", CACHE_MAX_SIZE
739
1319
  get size() {
740
1320
  return this.cache.size;
741
1321
  }
742
- /**
743
- * Prune stale entries based on TTL.
744
- * Only works if getTtl and getTimestamp are provided.
745
- */
746
1322
  pruneStale() {
747
1323
  if (!this.getTtl || !this.getTimestamp) return 0;
748
1324
  let now = Date.now(), pruned = 0;
@@ -767,27 +1343,6 @@ function stopCacheCleanup() {
767
1343
  function clearTokenCaches() {
768
1344
  metadataCache.clear(), priceCache.clear();
769
1345
  }
770
- async function fetchSolanaTokenMetadata(mints) {
771
- let results = /* @__PURE__ */ new Map();
772
- if (mints.length === 0) return results;
773
- let { signal, cleanup } = createTimeoutSignal(1e4);
774
- try {
775
- let response = await fetch("https://token-list-api.solana.cloud/v1/mints?chainId=101", {
776
- method: "POST",
777
- headers: { "Content-Type": "application/json" },
778
- body: JSON.stringify({ addresses: mints }),
779
- signal
780
- });
781
- if (cleanup(), !response.ok)
782
- throw new Error(`Solana Token List API error: ${response.status}`);
783
- let data = await response.json();
784
- for (let item of data.content)
785
- results.set(item.address, item);
786
- } catch (error) {
787
- cleanup(), console.warn("[useTokens] Solana Token List API failed:", error);
788
- }
789
- return results;
790
- }
791
1346
  function calculateBackoffDelay(attempt, baseDelay, retryAfter) {
792
1347
  if (retryAfter !== void 0 && retryAfter > 0) {
793
1348
  let jitter2 = Math.random() * 500;
@@ -823,26 +1378,17 @@ async function fetchCoinGeckoPrices(coingeckoIds, config) {
823
1378
  let elapsedTime = Date.now() - startTime;
824
1379
  if (elapsedTime >= maxTimeout) {
825
1380
  console.warn(
826
- `[useTokens] CoinGecko API: Total timeout (${maxTimeout}ms) exceeded after ${attempt} attempts. Returning cached/partial results.`
1381
+ `[useTokens] CoinGecko API: Total timeout (${maxTimeout}ms) exceeded after ${attempt} attempts.`
827
1382
  );
828
1383
  break;
829
1384
  }
830
1385
  let remainingTimeout = maxTimeout - elapsedTime, requestTimeout = Math.min(1e4, remainingTimeout), { signal, cleanup } = createTimeoutSignal(requestTimeout);
831
1386
  try {
832
- let response = await fetch(url, {
833
- headers,
834
- signal
835
- });
1387
+ let response = await fetch(url, { headers, signal });
836
1388
  if (cleanup(), response.status === 429) {
837
1389
  let retryAfter = parseRetryAfter(response.headers.get("Retry-After")), delay = calculateBackoffDelay(attempt, baseDelay, retryAfter);
838
- if (console.warn(
839
- `[useTokens] CoinGecko API rate limited (429). Attempt ${attempt + 1}/${maxRetries + 1}. Retry-After: ${retryAfter ?? "not specified"}s. Waiting ${Math.round(delay)}ms before retry. Consider adding an API key for higher limits: https://www.coingecko.com/en/api/pricing`
840
- ), Date.now() - startTime + delay >= maxTimeout) {
841
- console.warn(
842
- `[useTokens] CoinGecko API: Skipping retry - would exceed total timeout (${maxTimeout}ms). Returning cached/partial results.`
843
- );
1390
+ if (console.warn(`[useTokens] CoinGecko API rate limited (429). Waiting ${Math.round(delay)}ms.`), Date.now() - startTime + delay >= maxTimeout)
844
1391
  break;
845
- }
846
1392
  await new Promise((resolve) => setTimeout(resolve, delay)), attempt++;
847
1393
  continue;
848
1394
  }
@@ -853,174 +1399,195 @@ async function fetchCoinGeckoPrices(coingeckoIds, config) {
853
1399
  priceData?.usd !== void 0 && (results.set(id, priceData.usd), priceCache.set(id, { price: priceData.usd, timestamp: fetchTime }));
854
1400
  return results;
855
1401
  } catch (error) {
856
- if (cleanup(), lastError = error, error instanceof DOMException && error.name === "AbortError" ? console.warn(
857
- `[useTokens] CoinGecko API request timed out. Attempt ${attempt + 1}/${maxRetries + 1}.`
858
- ) : console.warn(
859
- `[useTokens] CoinGecko API request failed. Attempt ${attempt + 1}/${maxRetries + 1}:`,
860
- error
861
- ), attempt < maxRetries) {
1402
+ if (cleanup(), lastError = error, attempt < maxRetries) {
862
1403
  let delay = calculateBackoffDelay(attempt, baseDelay);
863
1404
  Date.now() - startTime + delay < maxTimeout && await new Promise((resolve) => setTimeout(resolve, delay));
864
1405
  }
865
1406
  attempt++;
866
1407
  }
867
1408
  }
868
- return attempt > maxRetries && console.warn(
869
- `[useTokens] CoinGecko API: All ${maxRetries + 1} attempts failed. Returning cached/partial results. Last error: ${lastError?.message ?? "Unknown error"}. If you are frequently rate limited, consider adding an API key: https://www.coingecko.com/en/api/pricing`
870
- ), results;
1409
+ return attempt > maxRetries && lastError && console.warn(`[useTokens] CoinGecko API: All attempts failed. Last error: ${lastError.message}`), results;
871
1410
  }
872
- async function fetchTokenMetadataHybrid(mints, coingeckoConfig) {
873
- let results = /* @__PURE__ */ new Map();
874
- if (mints.length === 0) return results;
875
- let uncachedMints = [], now = Date.now();
876
- for (let mint of mints) {
877
- let cached = metadataCache.get(mint);
878
- cached ? (cached.coingeckoId && (!priceCache.get(cached.coingeckoId) || now - (priceCache.get(cached.coingeckoId)?.timestamp ?? 0) >= PRICE_CACHE_TTL) && cached.coingeckoId && uncachedMints.push(mint), results.set(mint, cached)) : uncachedMints.push(mint);
879
- }
880
- if (uncachedMints.length === 0) return results;
881
- let solanaMetadata = await fetchSolanaTokenMetadata(uncachedMints), coingeckoIdToMint = /* @__PURE__ */ new Map();
882
- for (let [mint, meta] of solanaMetadata)
883
- meta.extensions?.coingeckoId && coingeckoIdToMint.set(meta.extensions.coingeckoId, mint);
1411
+ async function fetchTokenMetadataHybrid(mints, coingeckoConfig, options) {
1412
+ if (mints.length === 0) return false;
1413
+ let now = Date.now(), mintsNeedingTokenList = [], staleCoingeckoIdToMint = /* @__PURE__ */ new Map();
884
1414
  for (let mint of mints) {
885
1415
  let cached = metadataCache.get(mint);
886
- cached?.coingeckoId && !coingeckoIdToMint.has(cached.coingeckoId) && coingeckoIdToMint.set(cached.coingeckoId, mint);
1416
+ if (!cached) {
1417
+ mintsNeedingTokenList.push(mint);
1418
+ continue;
1419
+ }
1420
+ if (!cached.coingeckoId) continue;
1421
+ let priceEntry = priceCache.get(cached.coingeckoId);
1422
+ !!(priceEntry && now - priceEntry.timestamp < PRICE_CACHE_TTL) || staleCoingeckoIdToMint.set(cached.coingeckoId, mint);
887
1423
  }
888
- let prices = await fetchCoinGeckoPrices([...coingeckoIdToMint.keys()], coingeckoConfig);
889
- for (let [mint, meta] of solanaMetadata) {
890
- let coingeckoId = meta.extensions?.coingeckoId, usdPrice = coingeckoId ? prices.get(coingeckoId) : void 0, combined = {
1424
+ let didUpdate = false, tokenListMetadata = await fetchSolanaTokenListMetadata(mintsNeedingTokenList, {
1425
+ timeoutMs: 1e4,
1426
+ cluster: options?.cluster
1427
+ });
1428
+ for (let [mint, meta] of tokenListMetadata) {
1429
+ let coingeckoId = meta.extensions?.coingeckoId, usdPrice = (coingeckoId ? priceCache.get(coingeckoId) : void 0)?.price, combined = {
891
1430
  address: meta.address,
892
- name: meta.address === NATIVE_MINT ? "Solana" : meta.name,
1431
+ name: meta.address === NATIVE_SOL_MINT ? "Solana" : meta.name,
893
1432
  symbol: meta.symbol,
894
1433
  decimals: meta.decimals,
895
1434
  logoURI: meta.logoURI,
896
1435
  coingeckoId,
897
1436
  usdPrice
898
- };
899
- results.set(mint, combined), metadataCache.set(mint, combined);
1437
+ }, existing = metadataCache.get(mint);
1438
+ (!existing || existing.name !== combined.name || existing.symbol !== combined.symbol || existing.decimals !== combined.decimals || existing.logoURI !== combined.logoURI || existing.coingeckoId !== combined.coingeckoId || existing.usdPrice !== combined.usdPrice) && (didUpdate = true, metadataCache.set(mint, combined));
900
1439
  }
1440
+ didUpdate && options?.onUpdate?.();
1441
+ let coingeckoIdToMint = new Map(staleCoingeckoIdToMint);
1442
+ for (let [mint, meta] of tokenListMetadata)
1443
+ meta.extensions?.coingeckoId && coingeckoIdToMint.set(meta.extensions.coingeckoId, mint);
1444
+ if (coingeckoIdToMint.size === 0)
1445
+ return didUpdate;
1446
+ let prices = await fetchCoinGeckoPrices([...coingeckoIdToMint.keys()], coingeckoConfig), didUpdatePrices = false;
901
1447
  for (let [coingeckoId, mint] of coingeckoIdToMint) {
902
- let cached = results.get(mint) ?? metadataCache.get(mint);
1448
+ let cached = metadataCache.get(mint);
903
1449
  if (cached) {
904
1450
  let usdPrice = prices.get(coingeckoId);
905
- usdPrice !== void 0 && (cached.usdPrice = usdPrice, results.set(mint, cached), metadataCache.set(mint, cached));
1451
+ usdPrice !== void 0 && cached.usdPrice !== usdPrice && (didUpdate = true, didUpdatePrices = true, cached.usdPrice = usdPrice, metadataCache.set(mint, cached));
906
1452
  }
907
1453
  }
908
- return results;
1454
+ return didUpdatePrices && options?.onUpdate?.(), didUpdate;
909
1455
  }
910
1456
  function formatBalance(amount, decimals) {
911
- return (Number(amount) / Math.pow(10, decimals)).toLocaleString(void 0, {
912
- minimumFractionDigits: 0,
913
- maximumFractionDigits: Math.min(decimals, 6)
1457
+ return formatBigIntBalance(amount, decimals, {
1458
+ maxDecimals: Math.min(decimals, 6)
914
1459
  });
915
1460
  }
916
1461
  function formatUsd(amount, decimals, usdPrice) {
917
- return (Number(amount) / Math.pow(10, decimals) * usdPrice).toLocaleString(void 0, {
918
- style: "currency",
919
- currency: "USD",
920
- minimumFractionDigits: 2,
921
- maximumFractionDigits: 2
922
- });
1462
+ return formatBigIntUsd(amount, decimals, usdPrice);
923
1463
  }
924
- function transformImageUrl2(url, imageProxy) {
925
- if (!url) return;
926
- if (!imageProxy) return url;
927
- let encodedUrl = encodeURIComponent(url);
928
- return imageProxy.endsWith("/") ? imageProxy + encodedUrl : imageProxy + "/" + encodedUrl;
1464
+ function selectTokens(assets, address) {
1465
+ return {
1466
+ lamports: assets?.lamports ?? 0n,
1467
+ tokenAccounts: assets?.tokenAccounts ?? [],
1468
+ address
1469
+ };
1470
+ }
1471
+ function sortByValueDesc(a, b) {
1472
+ let metadataSort = (b.logo ? 1 : 0) - (a.logo ? 1 : 0);
1473
+ if (metadataSort !== 0) return metadataSort;
1474
+ let aValue = Number(a.amount) / Math.pow(10, a.decimals) * (a.usdPrice ?? 0);
1475
+ return Number(b.amount) / Math.pow(10, b.decimals) * (b.usdPrice ?? 0) - aValue;
929
1476
  }
930
1477
  function useTokens(options = {}) {
931
1478
  let {
1479
+ enabled = true,
932
1480
  includeZeroBalance = false,
933
1481
  autoRefresh = false,
934
1482
  refreshInterval = 6e4,
935
1483
  fetchMetadata = true,
936
- includeNativeSol = true
937
- } = options, { address: address$1, connected } = useAccount(), client = useSolanaClient(), connectorClient = useConnectorClient(), [tokens, setTokens] = useState([]), [isLoading, setIsLoading] = useState(false), [error, setError] = useState(null), [lastUpdated, setLastUpdated] = useState(null), [totalAccounts, setTotalAccounts] = useState(0), rpcClient = client?.client ?? null, connectorConfig = connectorClient?.getConfig(), imageProxy = connectorConfig?.imageProxy, coingeckoConfig = connectorConfig?.coingecko, fetchTokens = useCallback(async () => {
938
- if (!connected || !address$1 || !rpcClient) {
939
- setTokens([]), setTotalAccounts(0);
940
- return;
941
- }
942
- setIsLoading(true), setError(null);
943
- try {
944
- let rpc = rpcClient.rpc, walletAddress = address(address$1), tokenProgramId = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), [balanceResult, tokenAccountsResult] = await Promise.all([
945
- includeNativeSol ? rpc.getBalance(walletAddress).send() : Promise.resolve(null),
946
- rpc.getTokenAccountsByOwner(walletAddress, { programId: tokenProgramId }, { encoding: "jsonParsed" }).send()
947
- ]), tokenList = [], mints = [];
948
- if (includeNativeSol && balanceResult !== null) {
949
- let solBalance = balanceResult.value;
950
- (includeZeroBalance || solBalance > 0n) && (tokenList.push({
951
- mint: NATIVE_MINT,
952
- tokenAccount: address$1,
953
- // SOL uses wallet address
954
- amount: solBalance,
955
- decimals: 9,
956
- formatted: formatBalance(solBalance, 9),
957
- isFrozen: false,
958
- owner: address$1
959
- }), mints.push(NATIVE_MINT));
960
- }
961
- for (let account of tokenAccountsResult.value) {
962
- let parsed = account.account.data;
963
- if (parsed?.parsed?.info) {
964
- let info = parsed.parsed.info, amount = BigInt(info.tokenAmount?.amount || "0"), decimals = info.tokenAmount?.decimals || 0;
965
- if (!includeZeroBalance && amount === 0n)
966
- continue;
967
- tokenList.push({
968
- mint: info.mint,
969
- tokenAccount: account.pubkey,
970
- amount,
971
- decimals,
972
- formatted: formatBalance(amount, decimals),
973
- isFrozen: info.state === "frozen",
974
- owner: info.owner
975
- }), mints.push(info.mint);
976
- }
977
- }
978
- if (setTokens([...tokenList]), setTotalAccounts(tokenAccountsResult.value.length + (includeNativeSol ? 1 : 0)), setLastUpdated(/* @__PURE__ */ new Date()), fetchMetadata && mints.length > 0) {
979
- let metadata = await fetchTokenMetadataHybrid(mints, coingeckoConfig);
980
- for (let i = 0; i < tokenList.length; i++) {
981
- let meta = metadata.get(tokenList[i].mint);
982
- meta && (tokenList[i] = {
983
- ...tokenList[i],
984
- name: meta.name,
985
- symbol: meta.symbol,
986
- logo: transformImageUrl2(meta.logoURI, imageProxy),
987
- usdPrice: meta.usdPrice,
988
- formattedUsd: meta.usdPrice ? formatUsd(tokenList[i].amount, tokenList[i].decimals, meta.usdPrice) : void 0
989
- });
990
- }
991
- tokenList.sort((a, b) => {
992
- let metadataSort = (b.logo ? 1 : 0) - (a.logo ? 1 : 0);
993
- if (metadataSort !== 0) return metadataSort;
994
- let aValue = Number(a.amount) / Math.pow(10, a.decimals) * (a.usdPrice ?? 0);
995
- return Number(b.amount) / Math.pow(10, b.decimals) * (b.usdPrice ?? 0) - aValue;
996
- }), setTokens([...tokenList]);
997
- }
998
- } catch (err) {
999
- setError(err), console.error("[useTokens] Failed to fetch tokens:", err);
1000
- } finally {
1001
- setIsLoading(false);
1002
- }
1003
- }, [connected, address$1, rpcClient, includeZeroBalance, fetchMetadata, includeNativeSol, imageProxy, coingeckoConfig]);
1484
+ includeNativeSol = true,
1485
+ staleTimeMs = 0,
1486
+ cacheTimeMs = 300 * 1e3,
1487
+ // 5 minutes
1488
+ refetchOnMount = "stale",
1489
+ client: clientOverride
1490
+ } = options, { address, connected } = useAccount(), { type: clusterType } = useCluster(), connectorConfig = useConnectorClient()?.getConfig(), imageProxy = connectorConfig?.imageProxy, coingeckoConfig = connectorConfig?.coingecko, selectFn = useCallback(
1491
+ (assets) => selectTokens(assets, address ?? ""),
1492
+ [address]
1493
+ ), {
1494
+ data: selection,
1495
+ error,
1496
+ isFetching,
1497
+ updatedAt,
1498
+ refetch: sharedRefetch,
1499
+ abort
1500
+ } = useWalletAssets({
1501
+ enabled,
1502
+ staleTimeMs,
1503
+ cacheTimeMs,
1504
+ refetchOnMount,
1505
+ refetchIntervalMs: autoRefresh ? refreshInterval : false,
1506
+ client: clientOverride,
1507
+ select: selectFn
1508
+ }), lamports = selection?.lamports ?? 0n, tokenAccounts = selection?.tokenAccounts ?? [], walletAddress = selection?.address ?? "", baseTokens = useMemo(() => {
1509
+ let result = [];
1510
+ includeNativeSol && walletAddress && (includeZeroBalance || lamports > 0n) && result.push({
1511
+ mint: NATIVE_SOL_MINT,
1512
+ tokenAccount: walletAddress,
1513
+ amount: lamports,
1514
+ decimals: 9,
1515
+ formatted: formatBalance(lamports, 9),
1516
+ isFrozen: false,
1517
+ owner: walletAddress
1518
+ });
1519
+ for (let account of tokenAccounts)
1520
+ !includeZeroBalance && account.amount === 0n || result.push({
1521
+ mint: account.mint,
1522
+ tokenAccount: account.pubkey,
1523
+ amount: account.amount,
1524
+ decimals: account.decimals,
1525
+ formatted: formatBalance(account.amount, account.decimals),
1526
+ isFrozen: account.isFrozen,
1527
+ owner: account.owner,
1528
+ programId: account.programId
1529
+ });
1530
+ return result;
1531
+ }, [lamports, tokenAccounts, walletAddress, includeNativeSol, includeZeroBalance]), mints = useMemo(() => {
1532
+ let unique = /* @__PURE__ */ new Set();
1533
+ for (let token of baseTokens)
1534
+ unique.add(token.mint);
1535
+ return [...unique].sort();
1536
+ }, [baseTokens]), mintsKey = useMemo(() => mints.join(","), [mints]), [metadataVersion, setMetadataVersion] = useState(0);
1004
1537
  useEffect(() => {
1005
- fetchTokens();
1006
- }, [fetchTokens]), useEffect(() => {
1007
- if (!connected || !autoRefresh) return;
1008
- let interval = setInterval(fetchTokens, refreshInterval);
1009
- return () => clearInterval(interval);
1010
- }, [connected, autoRefresh, refreshInterval, fetchTokens]), useEffect(() => (startCacheCleanup(), () => stopCacheCleanup()), []);
1538
+ if (!fetchMetadata || !mintsKey) return;
1539
+ let isMounted = true;
1540
+ return (async () => {
1541
+ try {
1542
+ let mintList = mintsKey.split(",");
1543
+ await fetchTokenMetadataHybrid(mintList, coingeckoConfig, {
1544
+ onUpdate: () => {
1545
+ isMounted && setMetadataVersion((v) => v + 1);
1546
+ },
1547
+ cluster: clusterType ?? void 0
1548
+ }), isMounted && setMetadataVersion((v) => v + 1);
1549
+ } catch (err) {
1550
+ console.error("[useTokens] Failed to fetch metadata:", err);
1551
+ }
1552
+ })(), () => {
1553
+ isMounted = false;
1554
+ };
1555
+ }, [mintsKey, fetchMetadata, coingeckoConfig, clusterType]);
1556
+ let tokens = useMemo(() => fetchMetadata ? baseTokens.map((token) => {
1557
+ let meta = metadataCache.get(token.mint);
1558
+ if (!meta) return token;
1559
+ let usdPrice = (meta.coingeckoId ? priceCache.get(meta.coingeckoId) : void 0)?.price ?? meta.usdPrice;
1560
+ return {
1561
+ ...token,
1562
+ name: meta.name,
1563
+ symbol: meta.symbol,
1564
+ logo: transformImageUrl(meta.logoURI, imageProxy),
1565
+ usdPrice,
1566
+ formattedUsd: usdPrice ? formatUsd(token.amount, token.decimals, usdPrice) : void 0
1567
+ };
1568
+ }).sort(sortByValueDesc) : baseTokens.slice().sort(sortByValueDesc), [baseTokens, fetchMetadata, imageProxy, metadataVersion]), totalAccounts = tokenAccounts.length + (includeNativeSol ? 1 : 0);
1569
+ useEffect(() => (startCacheCleanup(), () => stopCacheCleanup()), []);
1011
1570
  let wasConnectedRef = useRef(connected);
1012
- return useEffect(() => {
1571
+ useEffect(() => {
1013
1572
  wasConnectedRef.current && !connected && clearTokenCaches(), wasConnectedRef.current = connected;
1014
- }, [connected]), useMemo(
1573
+ }, [connected]);
1574
+ let visibleError = updatedAt ? null : error, refetch = useCallback(
1575
+ async (opts) => {
1576
+ await sharedRefetch(opts);
1577
+ },
1578
+ [sharedRefetch]
1579
+ );
1580
+ return useMemo(
1015
1581
  () => ({
1016
1582
  tokens,
1017
- isLoading,
1018
- error,
1019
- refetch: fetchTokens,
1020
- lastUpdated,
1583
+ isLoading: isFetching,
1584
+ error: visibleError,
1585
+ refetch,
1586
+ abort,
1587
+ lastUpdated: updatedAt ? new Date(updatedAt) : null,
1021
1588
  totalAccounts
1022
1589
  }),
1023
- [tokens, isLoading, error, fetchTokens, lastUpdated, totalAccounts]
1590
+ [tokens, isFetching, visibleError, refetch, abort, updatedAt, totalAccounts]
1024
1591
  );
1025
1592
  }
1026
1593
  function DisconnectElement({
@@ -1657,6 +2224,43 @@ function WalletListElement({
1657
2224
  );
1658
2225
  }
1659
2226
  WalletListElement.displayName = "WalletListElement";
2227
+ var shineStyles = `
2228
+ .ck-skeleton {
2229
+ position: relative;
2230
+ overflow: hidden;
2231
+ }
2232
+ .ck-skeleton-shine {
2233
+ position: absolute;
2234
+ top: 0;
2235
+ left: 0;
2236
+ width: 500%;
2237
+ height: 500%;
2238
+ background: linear-gradient(135deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.1) 50%, rgba(255,255,255,0) 100%);
2239
+ animation: ck-skeleton-slide 0.5s infinite;
2240
+ z-index: 1;
2241
+ pointer-events: none;
2242
+ }
2243
+ @keyframes ck-skeleton-slide {
2244
+ 0% { transform: translate(-100%, -100%); }
2245
+ 100% { transform: translate(100%, 100%); }
2246
+ }
2247
+ `, stylesInjected = false;
2248
+ function injectStyles() {
2249
+ if (!stylesInjected && typeof document < "u") {
2250
+ let styleId = "ck-skeleton-shine-styles";
2251
+ if (!document.getElementById(styleId)) {
2252
+ let style = document.createElement("style");
2253
+ style.id = styleId, style.textContent = shineStyles, document.head.appendChild(style);
2254
+ }
2255
+ stylesInjected = true;
2256
+ }
2257
+ }
2258
+ function SkeletonShine() {
2259
+ return React.useEffect(() => {
2260
+ injectStyles();
2261
+ }, []), /* @__PURE__ */ jsx("div", { className: "ck-skeleton-shine", "data-slot": "skeleton-shine" });
2262
+ }
2263
+ SkeletonShine.displayName = "SkeletonShine";
1660
2264
  function BalanceElement({
1661
2265
  showSol = true,
1662
2266
  showTokens = false,
@@ -1699,10 +2303,10 @@ function BalanceElement({
1699
2303
  "data-variant": variant,
1700
2304
  "data-loading": "true",
1701
2305
  children: /* @__PURE__ */ jsxs("div", { className: "ck-balance-block-skeleton", "data-slot": "balance-element-skeleton", children: [
1702
- /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text" }),
2306
+ /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text", children: /* @__PURE__ */ jsx(SkeletonShine, {}) }),
1703
2307
  showTokens && /* @__PURE__ */ jsxs(Fragment, { children: [
1704
- /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text ck-skeleton--short" }),
1705
- /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text ck-skeleton--short" })
2308
+ /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text ck-skeleton--short", children: /* @__PURE__ */ jsx(SkeletonShine, {}) }),
2309
+ /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--text ck-skeleton--short", children: /* @__PURE__ */ jsx(SkeletonShine, {}) })
1706
2310
  ] })
1707
2311
  ] })
1708
2312
  }
@@ -1864,6 +2468,8 @@ function BalanceElement({
1864
2468
  BalanceElement.displayName = "BalanceElement";
1865
2469
  function TransactionHistoryElement({
1866
2470
  limit = 5,
2471
+ fetchDetails = true,
2472
+ detailsConcurrency,
1867
2473
  showStatus = true,
1868
2474
  showTime = true,
1869
2475
  className,
@@ -1873,7 +2479,11 @@ function TransactionHistoryElement({
1873
2479
  render,
1874
2480
  renderItem
1875
2481
  }) {
1876
- let { transactions, isLoading, error, hasMore, loadMore, refetch } = useTransactions({ limit });
2482
+ let { transactions, isLoading, error, hasMore, loadMore, refetch } = useTransactions({
2483
+ limit,
2484
+ fetchDetails,
2485
+ detailsConcurrency
2486
+ });
1877
2487
  if (render)
1878
2488
  return /* @__PURE__ */ jsx(Fragment, { children: render({ transactions, isLoading, error, hasMore, loadMore, refetch }) });
1879
2489
  let statusIcon = (status) => /* @__PURE__ */ jsx(
@@ -1923,7 +2533,7 @@ function TransactionHistoryElement({
1923
2533
  "data-slot": "tx-history-element",
1924
2534
  "data-variant": variant,
1925
2535
  "data-loading": "true",
1926
- children: /* @__PURE__ */ jsx("div", { className: "ck-tx-history-skeleton", "data-slot": "tx-history-skeleton", children: Array.from({ length: Math.min(limit, 3) }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--tx" }, i)) })
2536
+ children: /* @__PURE__ */ jsx("div", { className: "ck-tx-history-skeleton", "data-slot": "tx-history-skeleton", children: Array.from({ length: Math.min(limit, 3) }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--tx", children: /* @__PURE__ */ jsx(SkeletonShine, {}) }, i)) })
1927
2537
  }
1928
2538
  );
1929
2539
  if (error)
@@ -2087,7 +2697,7 @@ function TokenListElement({
2087
2697
  "data-slot": "token-list-element",
2088
2698
  "data-variant": variant,
2089
2699
  "data-loading": "true",
2090
- children: /* @__PURE__ */ jsx("div", { className: "ck-token-list-skeleton", "data-slot": "token-list-skeleton", children: Array.from({ length: Math.min(limit, 3) }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--token" }, i)) })
2700
+ children: /* @__PURE__ */ jsx("div", { className: "ck-token-list-skeleton", "data-slot": "token-list-skeleton", children: Array.from({ length: Math.min(limit, 3) }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "ck-skeleton ck-skeleton--token", children: /* @__PURE__ */ jsx(SkeletonShine, {}) }, i)) })
2091
2701
  }
2092
2702
  );
2093
2703
  if (error)
@@ -2180,6 +2790,6 @@ function TokenListElement({
2180
2790
  }
2181
2791
  TokenListElement.displayName = "TokenListElement";
2182
2792
 
2183
- export { AccountElement, BalanceElement, ClusterElement, ConnectorProvider, DisconnectElement, TokenListElement, TransactionHistoryElement, UnifiedProvider, WalletListElement, useAccount, useBalance, useCluster, useConnector, useConnectorClient, useGillSolanaClient, useGillTransactionSigner, useKitTransactionSigner, useSolanaClient, useTokens, useTransactionPreparer, useTransactionSigner, useTransactions, useWalletInfo };
2184
- //# sourceMappingURL=chunk-3STZXVXD.mjs.map
2185
- //# sourceMappingURL=chunk-3STZXVXD.mjs.map
2793
+ export { AccountElement, AppProvider, BalanceElement, ClusterElement, ConnectorProvider, DisconnectElement, SkeletonShine, TokenListElement, TransactionHistoryElement, UnifiedProvider, WalletListElement, clearSharedQueryCache, clearTokenCaches, getBalanceQueryKey, getTokensQueryKey, getTransactionsQueryKey, getWalletAssetsQueryKey, invalidateSharedQuery, useAccount, useBalance, useCluster, useConnector, useConnectorClient, useGillSolanaClient, useGillTransactionSigner, useKitTransactionSigner, useSolanaClient, useTokens, useTransactionPreparer, useTransactionSigner, useTransactions, useWalletInfo };
2794
+ //# sourceMappingURL=chunk-TTOKQAPX.mjs.map
2795
+ //# sourceMappingURL=chunk-TTOKQAPX.mjs.map