@naculus/connect-appkit-react 0.1.8 → 0.2.0

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 (94) hide show
  1. package/README.md +4 -4
  2. package/dist/client.cjs +1 -1
  3. package/dist/client.cjs.map +1 -1
  4. package/dist/client.d.cts +144 -0
  5. package/dist/client.d.ts +144 -0
  6. package/dist/client.js +1 -1
  7. package/dist/client.js.map +1 -1
  8. package/dist/index.cjs +2 -1
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +2270 -0
  11. package/dist/index.d.ts +2270 -0
  12. package/dist/index.js +2 -1
  13. package/dist/index.js.map +1 -1
  14. package/package.json +30 -12
  15. package/api-extractor.json +0 -21
  16. package/src/chain-abstraction/index.ts +0 -8
  17. package/src/chain-abstraction/useCompareCosts.ts +0 -137
  18. package/src/chain-abstraction/useExecuteRoute.ts +0 -129
  19. package/src/chain-abstraction/useRouteQuote.ts +0 -168
  20. package/src/client.test.ts +0 -242
  21. package/src/client.ts +0 -265
  22. package/src/hooks/useAccount.test.ts +0 -67
  23. package/src/hooks/useAccount.ts +0 -37
  24. package/src/hooks/useBalance.test.ts +0 -459
  25. package/src/hooks/useBalance.ts +0 -170
  26. package/src/hooks/useChain.test.ts +0 -160
  27. package/src/hooks/useChain.ts +0 -53
  28. package/src/hooks/useConnect.test.ts +0 -112
  29. package/src/hooks/useConnect.ts +0 -29
  30. package/src/hooks/useDisconnect.test.ts +0 -75
  31. package/src/hooks/useDisconnect.ts +0 -32
  32. package/src/hooks/useERC20Allowance.ts +0 -93
  33. package/src/hooks/useERC20Approve.ts +0 -220
  34. package/src/hooks/useERC20Transfer.ts +0 -134
  35. package/src/hooks/useERC20TransferSimulation.ts +0 -147
  36. package/src/hooks/useEmbeddedWallet.test.ts +0 -266
  37. package/src/hooks/useEmbeddedWallet.ts +0 -181
  38. package/src/hooks/useLastTx.test.ts +0 -117
  39. package/src/hooks/useLastTx.ts +0 -73
  40. package/src/hooks/useLookupAddress.ts +0 -131
  41. package/src/hooks/useNotification.ts +0 -308
  42. package/src/hooks/useResolveName.ts +0 -128
  43. package/src/hooks/useSIWxLogin.test.ts +0 -307
  44. package/src/hooks/useSIWxLogin.ts +0 -163
  45. package/src/hooks/useSIWxSession.test.ts +0 -481
  46. package/src/hooks/useSIWxSession.ts +0 -290
  47. package/src/hooks/useSendCalls.ts +0 -103
  48. package/src/hooks/useSendTransaction.test.ts +0 -175
  49. package/src/hooks/useSendTransaction.ts +0 -80
  50. package/src/hooks/useSendUserOperation.ts +0 -313
  51. package/src/hooks/useSession.ts +0 -96
  52. package/src/hooks/useSessionKeys.ts +0 -356
  53. package/src/hooks/useSignInWithEthereum.test.ts +0 -224
  54. package/src/hooks/useSignInWithEthereum.ts +0 -46
  55. package/src/hooks/useSignInWithX.test.ts +0 -210
  56. package/src/hooks/useSignInWithX.ts +0 -141
  57. package/src/hooks/useSignMessage.test.ts +0 -151
  58. package/src/hooks/useSignMessage.ts +0 -65
  59. package/src/hooks/useSimulateTransfer.ts +0 -165
  60. package/src/hooks/useSiwxAuthSession.test.ts +0 -456
  61. package/src/hooks/useSiwxAuthSession.ts +0 -163
  62. package/src/hooks/useSmartAccount.ts +0 -194
  63. package/src/hooks/useSwitchChain.ts +0 -68
  64. package/src/hooks/useTokenBalance.test.ts +0 -337
  65. package/src/hooks/useTokenBalance.ts +0 -210
  66. package/src/hooks/useTransactionSimulation.ts +0 -401
  67. package/src/hooks/useTxHistory.test.ts +0 -108
  68. package/src/hooks/useTxHistory.ts +0 -83
  69. package/src/hooks/useTxMonitor.test.ts +0 -127
  70. package/src/hooks/useTxMonitor.ts +0 -178
  71. package/src/hooks/useUserOpStatus.ts +0 -245
  72. package/src/hooks/useValidateDestination.test.ts +0 -96
  73. package/src/hooks/useValidateDestination.ts +0 -72
  74. package/src/hooks/useViemClient.test.ts +0 -155
  75. package/src/hooks/useViemClient.ts +0 -65
  76. package/src/hooks/useWallet.test.ts +0 -133
  77. package/src/hooks/useWallet.ts +0 -39
  78. package/src/hooks/useWeb3ErrorHandler.ts +0 -102
  79. package/src/index.ts +0 -186
  80. package/src/provider/Web3ConnectProvider.test.tsx +0 -178
  81. package/src/provider/Web3ConnectProvider.tsx +0 -530
  82. package/src/token-list/TokenSelector.tsx +0 -351
  83. package/src/token-list/defaults.ts +0 -39
  84. package/src/token-list/index.ts +0 -14
  85. package/src/token-list/useTokenList.ts +0 -119
  86. package/src/token-list/useTokenSearch.ts +0 -99
  87. package/src/types/index.ts +0 -99
  88. package/src/utils/chains.ts +0 -115
  89. package/src/utils/errorMessages.ts +0 -142
  90. package/src/utils/i18n.ts +0 -90
  91. package/src/wc-generated/components.ts +0 -377
  92. package/test/setup.ts +0 -17
  93. package/test/test-utils.tsx +0 -78
  94. package/tsup.config.ts +0 -18
@@ -1,351 +0,0 @@
1
- /**
2
- * TokenSelector — chain-aware, searchable token selection component.
3
- *
4
- * Features:
5
- * - Loads token list for the given chain
6
- * - Search by symbol, name, or address
7
- * - Displays token logo, symbol, name, and decimals
8
- * - Calls onSelect when a token is chosen
9
- */
10
-
11
- import React, { useCallback } from "react";
12
- import type { TokenListEntry } from "@naculus/connect-core";
13
- import { useTokenList } from "./useTokenList";
14
- import { useTokenSearch } from "./useTokenSearch";
15
-
16
- export interface TokenSelectorProps {
17
- /** Chain ID in CAIP-2 format (e.g. "eip155:1") */
18
- chainId: string;
19
- /** Called when a token is selected */
20
- onSelect: (token: TokenListEntry) => void;
21
- /** Called to close/dismiss the selector */
22
- onClose: () => void;
23
- /** Optional configuration */
24
- options?: {
25
- /** Show popular/recommended tokens (default: true) */
26
- showRecommendations?: boolean;
27
- /** Custom filter for tokens */
28
- filter?: (token: TokenListEntry) => boolean;
29
- };
30
- }
31
-
32
- /**
33
- * TokenSelector — a dropdown/modal component for selecting tokens.
34
- *
35
- * @example
36
- * <TokenSelector
37
- * chainId="eip155:1"
38
- * onSelect={(token) => setSelectedToken(token)}
39
- * onClose={() => setOpen(false)}
40
- * />
41
- */
42
- export function TokenSelector({
43
- chainId,
44
- onSelect,
45
- onClose,
46
- options,
47
- }: TokenSelectorProps) {
48
- const [searchQuery, setSearchQuery] = React.useState("");
49
-
50
- const {
51
- tokens: allTokens,
52
- isLoading,
53
- error,
54
- } = useTokenList(chainId);
55
-
56
- const { results, isSearching } = useTokenSearch(
57
- searchQuery,
58
- searchQuery ? chainId : undefined,
59
- );
60
-
61
- const showRecommendations = options?.showRecommendations ?? true;
62
-
63
- // Display tokens: when searching, show results; otherwise show the full list
64
- const displayTokens = React.useMemo(() => {
65
- if (searchQuery.trim()) {
66
- const combined = [...results.exact, ...results.fuzzy].map((m) => m.token);
67
- return options?.filter
68
- ? combined.filter(options.filter)
69
- : combined;
70
- }
71
-
72
- const tokens = allTokens;
73
- return options?.filter
74
- ? tokens.filter(options.filter)
75
- : tokens;
76
- }, [searchQuery, results, allTokens, options?.filter]);
77
-
78
- // Popular tokens for recommendation section
79
- const popularTokens = React.useMemo(() => {
80
- if (!showRecommendations || searchQuery.trim()) return [];
81
- return allTokens.slice(0, 6); // Top 6
82
- }, [allTokens, showRecommendations, searchQuery]);
83
-
84
- const handleSelect = useCallback(
85
- (token: TokenListEntry) => {
86
- onSelect(token);
87
- onClose();
88
- },
89
- [onSelect, onClose],
90
- );
91
-
92
- return (
93
- <div
94
- style={{
95
- background: "hsl(var(--background))",
96
- borderRadius: "12px",
97
- padding: "16px",
98
- color: "hsl(var(--foreground))",
99
- minWidth: "320px",
100
- maxWidth: "400px",
101
- fontFamily: "system-ui, sans-serif",
102
- }}
103
- >
104
- {/* Header */}
105
- <div
106
- style={{
107
- display: "flex",
108
- justifyContent: "space-between",
109
- alignItems: "center",
110
- marginBottom: "12px",
111
- }}
112
- >
113
- <h3 style={{ margin: 0, fontSize: "16px", fontWeight: 600 }}>
114
- Select Token
115
- </h3>
116
- <button
117
- onClick={onClose}
118
- style={{
119
- background: "none",
120
- border: "none",
121
- color: "hsl(var(--muted-foreground))",
122
- cursor: "pointer",
123
- fontSize: "18px",
124
- padding: "4px 8px",
125
- }}
126
- >
127
- ✕
128
- </button>
129
- </div>
130
-
131
- {/* Search Input */}
132
- <input
133
- type="text"
134
- placeholder="Search token name or paste address"
135
- value={searchQuery}
136
- onChange={(e) => setSearchQuery(e.target.value)}
137
- style={{
138
- width: "100%",
139
- padding: "10px 12px",
140
- borderRadius: "8px",
141
- border: "1px solid #333",
142
- background: "hsl(var(--card))",
143
- color: "hsl(var(--foreground))",
144
- fontSize: "14px",
145
- outline: "none",
146
- boxSizing: "border-box",
147
- marginBottom: "12px",
148
- }}
149
- autoFocus
150
- />
151
-
152
- {/* Loading / Error States */}
153
- {isLoading && !isSearching && (
154
- <div style={{ textAlign: "center", padding: "20px", color: "hsl(var(--muted-foreground))" }}>
155
- Loading tokens...
156
- </div>
157
- )}
158
- {error && (
159
- <div
160
- style={{
161
- textAlign: "center",
162
- padding: "12px",
163
- color: "hsl(var(--destructive))",
164
- fontSize: "13px",
165
- }}
166
- >
167
- Error: {error.message}
168
- </div>
169
- )}
170
-
171
- {/* Popular / Recommended Section */}
172
- {showRecommendations && popularTokens.length > 0 && !searchQuery.trim() && (
173
- <div style={{ marginBottom: "12px" }}>
174
- <div
175
- style={{
176
- fontSize: "11px",
177
- color: "hsl(var(--muted-foreground))",
178
- textTransform: "uppercase",
179
- letterSpacing: "0.5px",
180
- marginBottom: "8px",
181
- }}
182
- >
183
- Popular
184
- </div>
185
- <div style={{ display: "flex", gap: "6px", flexWrap: "wrap" }}>
186
- {popularTokens.map((token) => (
187
- <button
188
- key={`${token.chainId}:${token.address}`}
189
- onClick={() => handleSelect(token)}
190
- style={{
191
- background: "hsl(var(--card))",
192
- border: "1px solid #333",
193
- borderRadius: "20px",
194
- padding: "6px 14px",
195
- color: "hsl(var(--foreground))",
196
- cursor: "pointer",
197
- fontSize: "13px",
198
- display: "flex",
199
- alignItems: "center",
200
- gap: "4px",
201
- }}
202
- >
203
- {token.logoURI ? (
204
- <img
205
- src={token.logoURI}
206
- alt=""
207
- style={{ width: "16px", height: "16px", borderRadius: "50%" }}
208
- onError={(e) => {
209
- (e.target as HTMLImageElement).style.display = "none";
210
- }}
211
- />
212
- ) : null}
213
- {token.symbol}
214
- </button>
215
- ))}
216
- </div>
217
- </div>
218
- )}
219
-
220
- {/* Search Result Count */}
221
- {searchQuery.trim() && !isLoading && (
222
- <div
223
- style={{
224
- fontSize: "11px",
225
- color: "hsl(var(--muted-foreground))",
226
- marginBottom: "8px",
227
- }}
228
- >
229
- {displayTokens.length > 0 ? (
230
- <>
231
- {results.exact.length} exact, {results.fuzzy.length} fuzzy
232
- {results.hasMore ? " (more available)" : ""}
233
- </>
234
- ) : (
235
- "No results found"
236
- )}
237
- </div>
238
- )}
239
-
240
- {/* Token List */}
241
- <div
242
- style={{
243
- maxHeight: "320px",
244
- overflowY: "auto",
245
- display: "flex",
246
- flexDirection: "column",
247
- gap: "2px",
248
- }}
249
- >
250
- {displayTokens.map((token) => (
251
- <button
252
- key={`${token.chainId}:${token.address}`}
253
- onClick={() => handleSelect(token)}
254
- style={{
255
- display: "flex",
256
- alignItems: "center",
257
- gap: "10px",
258
- padding: "10px 8px",
259
- background: "transparent",
260
- border: "none",
261
- borderRadius: "8px",
262
- color: "hsl(var(--foreground))",
263
- cursor: "pointer",
264
- textAlign: "left",
265
- width: "100%",
266
- fontSize: "14px",
267
- }}
268
- onMouseEnter={(e) => {
269
- (e.target as HTMLElement).style.background = "hsl(var(--card))";
270
- }}
271
- onMouseLeave={(e) => {
272
- (e.target as HTMLElement).style.background = "transparent";
273
- }}
274
- >
275
- {/* Logo */}
276
- <div
277
- style={{
278
- width: "32px",
279
- height: "32px",
280
- borderRadius: "50%",
281
- background: "hsl(var(--border))",
282
- display: "flex",
283
- alignItems: "center",
284
- justifyContent: "center",
285
- fontSize: "12px",
286
- fontWeight: 600,
287
- flexShrink: 0,
288
- overflow: "hidden",
289
- }}
290
- >
291
- {token.logoURI ? (
292
- <img
293
- src={token.logoURI}
294
- alt={token.symbol}
295
- style={{ width: "100%", height: "100%" }}
296
- onError={(e) => {
297
- const img = e.target as HTMLImageElement;
298
- img.style.display = "none";
299
- (img.parentElement as HTMLElement).textContent =
300
- token.symbol.slice(0, 2);
301
- }}
302
- />
303
- ) : (
304
- token.symbol.slice(0, 2)
305
- )}
306
- </div>
307
-
308
- {/* Token info */}
309
- <div style={{ flex: 1, minWidth: 0 }}>
310
- <div
311
- style={{
312
- fontWeight: 600,
313
- fontSize: "14px",
314
- overflow: "hidden",
315
- textOverflow: "ellipsis",
316
- whiteSpace: "nowrap",
317
- }}
318
- >
319
- {token.symbol}
320
- </div>
321
- <div
322
- style={{
323
- fontSize: "12px",
324
- color: "hsl(var(--muted-foreground))",
325
- overflow: "hidden",
326
- textOverflow: "ellipsis",
327
- whiteSpace: "nowrap",
328
- }}
329
- >
330
- {token.name}
331
- </div>
332
- </div>
333
-
334
- {/* Decimals & Chain Info */}
335
- <div
336
- style={{
337
- fontSize: "11px",
338
- color: "#666",
339
- textAlign: "right",
340
- flexShrink: 0,
341
- }}
342
- >
343
- <div>{token.decimals} decimals</div>
344
- <div>Chain {token.chainId}</div>
345
- </div>
346
- </button>
347
- ))}
348
- </div>
349
- </div>
350
- );
351
- }
@@ -1,39 +0,0 @@
1
- /**
2
- * Default token list sources for React hooks.
3
- */
4
-
5
- import type { TokenListSource } from "@naculus/connect-core";
6
- import {
7
- DEFAULT_SOURCES,
8
- ETHEREUM_MAINNET_TOKENS,
9
- POLYGON_TOKENS,
10
- OPTIMISM_TOKENS,
11
- ARBITRUM_TOKENS,
12
- BASE_TOKENS,
13
- } from "@naculus/connect-core";
14
-
15
- /**
16
- * Get default sources for useTokenList / useTokenSearch hooks.
17
- * Includes built-in tokens and remote source URLs.
18
- */
19
- export function getDefaultSources(): TokenListSource[] {
20
- return [
21
- {
22
- name: "built-in",
23
- tokens: [
24
- ...ETHEREUM_MAINNET_TOKENS,
25
- ...POLYGON_TOKENS,
26
- ...OPTIMISM_TOKENS,
27
- ...ARBITRUM_TOKENS,
28
- ...BASE_TOKENS,
29
- ],
30
- enabled: true,
31
- },
32
- {
33
- name: "uniswap-default",
34
- url: "https://tokens.uniswap.org",
35
- enabled: true,
36
- refreshInterval: 24 * 60 * 60 * 1000,
37
- },
38
- ];
39
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Token Discovery — React hooks and components.
3
- *
4
- * @see docs/features/token-discovery.md
5
- */
6
-
7
- export { useTokenList } from "./useTokenList";
8
- export type { UseTokenListOptions, UseTokenListReturn } from "./useTokenList";
9
-
10
- export { useTokenSearch } from "./useTokenSearch";
11
- export type { UseTokenSearchReturn } from "./useTokenSearch";
12
-
13
- export { TokenSelector } from "./TokenSelector";
14
- export type { TokenSelectorProps } from "./TokenSelector";
@@ -1,119 +0,0 @@
1
- /**
2
- * useTokenList — React hook for loading token lists.
3
- *
4
- * Manages TokenListManager lifecycle and provides reactive
5
- * token data scoped to a specific chain.
6
- */
7
-
8
- import { useState, useEffect, useCallback, useRef } from "react";
9
- import { TokenListManager } from "@naculus/connect-core";
10
- import type { TokenListEntry, TokenListSource, TokenListManagerConfig } from "@naculus/connect-core";
11
-
12
- export interface UseTokenListOptions {
13
- /** Auto-load on mount (default: true) */
14
- autoLoad?: boolean;
15
- /** Custom sources (overrides defaults) */
16
- sources?: TokenListSource[];
17
- }
18
-
19
- export interface UseTokenListReturn {
20
- /** Loaded tokens for the specified chain */
21
- tokens: TokenListEntry[];
22
- /** Whether the initial load is in progress */
23
- isLoading: boolean;
24
- /** Whether tokens have been loaded at least once */
25
- isLoaded: boolean;
26
- /** Last error, if any */
27
- error: Error | null;
28
- /** Force a full refresh from all sources */
29
- refetch: () => Promise<void>;
30
- }
31
-
32
- /**
33
- * React hook for accessing token lists for a specific chain.
34
- *
35
- * @example
36
- * const { tokens, isLoading } = useTokenList("eip155:1");
37
- * // tokens → [WETH, USDC, USDT, DAI, ...]
38
- */
39
- export function useTokenList(
40
- chainId?: string,
41
- options?: UseTokenListOptions,
42
- ): UseTokenListReturn {
43
- const managerRef = useRef<TokenListManager | null>(null);
44
- const [tokens, setTokens] = useState<TokenListEntry[]>([]);
45
- const [isLoading, setIsLoading] = useState(options?.autoLoad !== false);
46
- const [isLoaded, setIsLoaded] = useState(false);
47
- const [error, setError] = useState<Error | null>(null);
48
-
49
- // Initialize manager once
50
- if (!managerRef.current) {
51
- const config: Partial<TokenListManagerConfig> = {};
52
- if (options?.sources) {
53
- config.sources = options.sources;
54
- }
55
- managerRef.current = new TokenListManager(config);
56
- }
57
-
58
- const load = useCallback(async () => {
59
- const manager = managerRef.current;
60
- if (!manager) return;
61
-
62
- setIsLoading(true);
63
- setError(null);
64
-
65
- try {
66
- await manager.load();
67
- const numericChainId = chainId ? parseChainId(chainId) : undefined;
68
- const loaded = manager.getTokens(
69
- numericChainId !== undefined ? { chainId: numericChainId } : undefined,
70
- );
71
- setTokens(loaded);
72
- setIsLoaded(true);
73
- } catch (err) {
74
- setError(err instanceof Error ? err : new Error(String(err)));
75
- } finally {
76
- setIsLoading(false);
77
- }
78
- }, [chainId]);
79
-
80
- const refetch = useCallback(async () => {
81
- const manager = managerRef.current;
82
- if (!manager) return;
83
-
84
- setIsLoading(true);
85
- setError(null);
86
-
87
- try {
88
- await manager.refresh();
89
- const numericChainId = chainId ? parseChainId(chainId) : undefined;
90
- const loaded = manager.getTokens(
91
- numericChainId !== undefined ? { chainId: numericChainId } : undefined,
92
- );
93
- setTokens(loaded);
94
- setIsLoaded(true);
95
- } catch (err) {
96
- setError(err instanceof Error ? err : new Error(String(err)));
97
- } finally {
98
- setIsLoading(false);
99
- }
100
- }, [chainId]);
101
-
102
- useEffect(() => {
103
- if (options?.autoLoad !== false) {
104
- load();
105
- }
106
- }, [load, options?.autoLoad]);
107
-
108
- return { tokens, isLoading, isLoaded, error, refetch };
109
- }
110
-
111
- /**
112
- * Parse a CAIP-2 chain ID like "eip155:1" into a numeric chain ID (1).
113
- * Returns undefined for non-numeric chains.
114
- */
115
- function parseChainId(chainId: string): number | undefined {
116
- const parts = chainId.split(":");
117
- const numeric = parseInt(parts[parts.length - 1], 10);
118
- return isNaN(numeric) ? undefined : numeric;
119
- }
@@ -1,99 +0,0 @@
1
- /**
2
- * useTokenSearch — React hook for searching tokens.
3
- *
4
- * Debounced search over loaded token lists.
5
- * Returns structured results (exact + fuzzy matches).
6
- */
7
-
8
- import { useState, useEffect, useRef, useMemo } from "react";
9
- import { TokenListManager } from "@naculus/connect-core";
10
- import type { TokenSearchResult, TokenListSource, TokenListManagerConfig } from "@naculus/connect-core";
11
- import { getDefaultSources } from "./defaults";
12
-
13
- const DEBOUNCE_MS = 300;
14
-
15
- export interface UseTokenSearchReturn {
16
- /** Structured search results */
17
- results: TokenSearchResult;
18
- /** Whether a search is currently running */
19
- isSearching: boolean;
20
- }
21
-
22
- // Shared manager instance across hooks
23
- let sharedManager: TokenListManager | null = null;
24
-
25
- function getManager(): TokenListManager {
26
- if (!sharedManager) {
27
- sharedManager = new TokenListManager({
28
- sources: getDefaultSources(),
29
- });
30
- // Start loading in background
31
- sharedManager.load().catch(() => {});
32
- }
33
- return sharedManager;
34
- }
35
-
36
- /**
37
- * React hook for searching tokens by symbol, name, or address.
38
- *
39
- * Debounced 300ms to avoid excessive re-renders during typing.
40
- *
41
- * @example
42
- * const { results, isSearching } = useTokenSearch("USDC");
43
- * // results.exact → [USDC on ETH]
44
- * // results.fuzzy → [USDC.e on AVAX]
45
- */
46
- export function useTokenSearch(
47
- query: string,
48
- chainId?: string,
49
- ): UseTokenSearchReturn {
50
- const [results, setResults] = useState<TokenSearchResult>({
51
- exact: [],
52
- fuzzy: [],
53
- hasMore: false,
54
- });
55
- const [isSearching, setIsSearching] = useState(false);
56
- const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
57
-
58
- const trimmedQuery = query.trim();
59
-
60
- useEffect(() => {
61
- if (!trimmedQuery) {
62
- setResults({ exact: [], fuzzy: [], hasMore: false });
63
- setIsSearching(false);
64
- return;
65
- }
66
-
67
- // Debounce
68
- if (debounceRef.current) {
69
- clearTimeout(debounceRef.current);
70
- }
71
-
72
- setIsSearching(true);
73
-
74
- debounceRef.current = setTimeout(() => {
75
- const manager = getManager();
76
- const numericChainId = chainId ? parseChainId(chainId) : undefined;
77
- const searchResults = manager.search(trimmedQuery, {
78
- chainId: numericChainId,
79
- limit: 20,
80
- });
81
- setResults(searchResults);
82
- setIsSearching(false);
83
- }, DEBOUNCE_MS);
84
-
85
- return () => {
86
- if (debounceRef.current) {
87
- clearTimeout(debounceRef.current);
88
- }
89
- };
90
- }, [trimmedQuery, chainId]);
91
-
92
- return { results, isSearching };
93
- }
94
-
95
- function parseChainId(chainId: string): number | undefined {
96
- const parts = chainId.split(":");
97
- const numeric = parseInt(parts[parts.length - 1], 10);
98
- return isNaN(numeric) ? undefined : numeric;
99
- }