@naculus/connect-appkit-core 0.2.3 → 0.2.5

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.
package/dist/index.js CHANGED
@@ -1,3 +1,46 @@
1
+ // src/capabilities.ts
2
+ import {
3
+ caip2ToHexChain,
4
+ readAtomicSupport
5
+ } from "@naculus/connect-core";
6
+ function normalizeCapabilities(raw) {
7
+ const out = {};
8
+ for (const [key, entry] of Object.entries(raw ?? {})) {
9
+ if (!entry || typeof entry !== "object") continue;
10
+ const { supported, maxBatchSize } = readAtomicSupport(
11
+ entry
12
+ );
13
+ out[key] = {
14
+ chainId: key,
15
+ atomic: supported ? "supported" : "unsupported",
16
+ ...maxBatchSize === void 0 ? {} : { maxBatchSize },
17
+ raw: entry
18
+ };
19
+ }
20
+ return out;
21
+ }
22
+ function selectChainCapabilities(capabilities, chainId) {
23
+ if (!capabilities || !chainId) return null;
24
+ const hex = caip2ToHexChain(chainId);
25
+ return capabilities[chainId] ?? (hex ? capabilities[hex] : void 0) ?? null;
26
+ }
27
+ function atomicSupportFor(capabilities, chainId) {
28
+ return selectChainCapabilities(capabilities, chainId)?.atomic ?? "unknown";
29
+ }
30
+
31
+ // src/solana-roles.ts
32
+ function readSolanaRoles(connector, session) {
33
+ if (!session) return { roles: null, absence: "no_session" };
34
+ if (session.walletType !== "solana") {
35
+ return { roles: null, absence: "not_solana" };
36
+ }
37
+ if (typeof connector?.getRoles !== "function") {
38
+ return { roles: null, absence: "unreported" };
39
+ }
40
+ const roles = connector.getRoles(session);
41
+ return roles ? { roles, absence: null } : { roles: null, absence: "no_session" };
42
+ }
43
+
1
44
  // src/chain-selection.ts
2
45
  import { eip155Reference, namespaceOf } from "@naculus/connect-core";
3
46
  function chainNumber(chain) {
@@ -183,6 +226,172 @@ function __resetResolverForTests() {
183
226
  sharedResolver = null;
184
227
  }
185
228
 
229
+ // src/notification.ts
230
+ import {
231
+ createStorageAdapter
232
+ } from "@naculus/connect-core";
233
+ var NOTIFICATION_SETTINGS_KEY = "naculus_notif_react_settings";
234
+ var DEFAULT_NOTIFICATION_SETTINGS = {
235
+ telegram: false,
236
+ webpush: false,
237
+ inapp: true,
238
+ frequency: "final-only",
239
+ mutedChains: [],
240
+ mutedTypes: []
241
+ };
242
+ function createNotificationSettingsStorage() {
243
+ const storage = createStorageAdapter("local", "naculus_notif_");
244
+ return {
245
+ getItem: async (key) => {
246
+ try {
247
+ return await storage.get(key);
248
+ } catch (cause) {
249
+ console.warn("useNotification: failed to read from storage:", cause);
250
+ return null;
251
+ }
252
+ },
253
+ setItem: async (key, value) => {
254
+ try {
255
+ await storage.set(key, value);
256
+ } catch (cause) {
257
+ console.warn("useNotification: failed to write to storage:", cause);
258
+ }
259
+ },
260
+ removeItem: async (key) => {
261
+ try {
262
+ await storage.remove(key);
263
+ } catch (cause) {
264
+ console.warn("useNotification: failed to remove from storage:", cause);
265
+ }
266
+ }
267
+ };
268
+ }
269
+ async function loadNotificationSettings(storage) {
270
+ return storage.getItem(NOTIFICATION_SETTINGS_KEY);
271
+ }
272
+ function saveNotificationSettings(storage, settings) {
273
+ storage.setItem(NOTIFICATION_SETTINGS_KEY, settings).catch(() => {
274
+ });
275
+ }
276
+ function updateNotificationSettings(current, update) {
277
+ return { ...current, ...update };
278
+ }
279
+ function channelsUpdate(channels) {
280
+ const update = {};
281
+ if (channels.includes("telegram")) update.telegram = true;
282
+ if (channels.includes("webpush")) update.webpush = true;
283
+ if (channels.includes("inapp")) update.inapp = true;
284
+ return update;
285
+ }
286
+ function mutedChainSettings(settings, chainId, muted) {
287
+ return {
288
+ mutedChains: muted ? [.../* @__PURE__ */ new Set([...settings.mutedChains, chainId])] : settings.mutedChains.filter((id) => id !== chainId)
289
+ };
290
+ }
291
+ function createNotification(item) {
292
+ return {
293
+ ...item,
294
+ id: `notif_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
295
+ timestamp: Date.now()
296
+ };
297
+ }
298
+ function sendItem(channel, item) {
299
+ return channel.send({
300
+ id: item.id,
301
+ userId: "",
302
+ txHash: item.txHash,
303
+ chainId: "",
304
+ chainName: item.chainName,
305
+ status: item.status,
306
+ title: item.title,
307
+ body: item.body,
308
+ valueFormatted: item.valueFormatted,
309
+ explorerUrl: item.explorerUrl,
310
+ timestamp: item.timestamp
311
+ });
312
+ }
313
+ function addInAppNotification(channel, item) {
314
+ void sendItem(channel, createNotification(item));
315
+ }
316
+ async function clearOneInAppNotification(channel, id) {
317
+ const remaining = channel.getHistory().filter((item) => item.id !== id);
318
+ channel.clear();
319
+ await Promise.all(remaining.map((item) => sendItem(channel, item)));
320
+ return channel.getHistory();
321
+ }
322
+
323
+ // src/destination.ts
324
+ import { isEvmAddress, isZeroAddress } from "@naculus/connect-core";
325
+ var DEAD_ADDRESSES = /* @__PURE__ */ new Set([
326
+ "0x0000000000000000000000000000000000000001",
327
+ "0x000000000000000000000000000000000000dead"
328
+ ]);
329
+ function isBurnDestination(address) {
330
+ const lower = address.toLowerCase();
331
+ return DEAD_ADDRESSES.has(lower) || lower.includes("dead");
332
+ }
333
+ function validateDestination(address) {
334
+ if (!address) return { isValid: false, level: "blocked", issue: "empty" };
335
+ if (!isEvmAddress(address)) {
336
+ return { isValid: false, level: "blocked", issue: "invalid_format" };
337
+ }
338
+ if (isZeroAddress(address)) {
339
+ return { isValid: false, level: "blocked", issue: "zero_address" };
340
+ }
341
+ if (isBurnDestination(address)) {
342
+ return { isValid: false, level: "blocked", issue: "burn_address" };
343
+ }
344
+ return { isValid: true, level: "ok", issue: null };
345
+ }
346
+ function bareEvmAddress(account) {
347
+ if (!account) return null;
348
+ const value = account.includes(":") ? account.split(":").pop() : account;
349
+ return value && isEvmAddress(value) ? value : null;
350
+ }
351
+
352
+ // src/routing.ts
353
+ import { isValidAddress } from "@naculus/connect-core";
354
+ function isPositiveIntegerAmount(amount) {
355
+ return /^(?:0|[1-9][0-9]*)$/.test(amount) && BigInt(amount) > 0n;
356
+ }
357
+ function isQuotableInput(input) {
358
+ return Boolean(
359
+ input.fromChain && input.toChain && input.fromToken && isPositiveIntegerAmount(input.amount)
360
+ );
361
+ }
362
+ function compareCostsKey(input) {
363
+ const options = input.options;
364
+ const optionsKey = options ? Object.keys(options).sort().map((key) => {
365
+ const value = options[key];
366
+ return `${key}=${typeof value === "object" && value !== null ? "[object]" : String(value)}`;
367
+ }).join("&") : "";
368
+ return `${input.operation}|${(input.chains ?? []).join(",")}|${optionsKey}`;
369
+ }
370
+ function validateRouteRecipient(quote, recipient) {
371
+ if (typeof recipient !== "string" || recipient.trim() === "") {
372
+ return {
373
+ code: "invalid_recipient",
374
+ message: "A recipient address is required"
375
+ };
376
+ }
377
+ const namespace = quote?.toChain?.split(":")[0];
378
+ if (namespace && !isValidAddress(recipient, namespace)) {
379
+ return {
380
+ code: "invalid_recipient",
381
+ message: `"${recipient}" is not a valid ${namespace} address for the destination chain ${quote?.toChain}`
382
+ };
383
+ }
384
+ return null;
385
+ }
386
+ function toExecuteRouteError(cause) {
387
+ const error = cause;
388
+ return {
389
+ code: error?.code ?? "execution_failed",
390
+ message: error?.message ?? String(cause),
391
+ details: error?.details
392
+ };
393
+ }
394
+
186
395
  // src/web3-store.ts
187
396
  import { logger } from "@naculus/connect-core";
188
397
  function withTimeout(promise, ms, label = "Operation") {
@@ -272,27 +481,50 @@ function web3Reducer(state, action) {
272
481
  }
273
482
  }
274
483
  export {
484
+ DEFAULT_NOTIFICATION_SETTINGS,
485
+ NOTIFICATION_SETTINGS_KEY,
275
486
  __resetResolverForTests,
487
+ addInAppNotification,
488
+ atomicSupportFor,
489
+ bareEvmAddress,
276
490
  chainNamespace,
277
491
  chainNumber,
278
492
  chainsForNamespace,
493
+ channelsUpdate,
494
+ clearOneInAppNotification,
495
+ compareCostsKey,
279
496
  createDecimalsCache,
497
+ createNotification,
498
+ createNotificationSettingsStorage,
280
499
  decodeRevertReason,
281
500
  describeChain,
282
501
  eip155ChainIdToNumber,
283
502
  getResolver,
284
503
  initialWeb3State,
504
+ isBurnDestination,
285
505
  isEvmChainId,
506
+ isPositiveIntegerAmount,
507
+ isQuotableInput,
508
+ loadNotificationSettings,
509
+ mutedChainSettings,
510
+ normalizeCapabilities,
286
511
  normalizeEip155ChainId,
287
512
  readDecimals,
513
+ readSolanaRoles,
288
514
  resolveChain,
289
515
  resolveEvmChainId,
290
516
  resolveRevertReason,
517
+ saveNotificationSettings,
518
+ selectChainCapabilities,
291
519
  toChainSwitchError,
292
520
  toEip155Accounts,
521
+ toExecuteRouteError,
293
522
  toViemChain,
294
523
  tokenChainMismatch,
295
524
  tokenKey,
525
+ updateNotificationSettings,
526
+ validateDestination,
527
+ validateRouteRecipient,
296
528
  web3Reducer,
297
529
  withRetry,
298
530
  withTimeout,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chain-selection.ts","../src/viem-chain.ts","../src/aa-chain.ts","../src/token-chain.ts","../src/token-decimals.ts","../src/provider-errors.ts","../src/revert-reason.ts","../src/name-resolver.ts","../src/web3-store.ts"],"sourcesContent":["/**\n * Which chains a session can actually switch to, and what the current one is.\n *\n * Framework-neutral, and deliberately built on `parseChainId` from\n * `@naculus/connect-core` rather than on another `startsWith(\"eip155:\")`.\n */\n\nimport { eip155Reference, namespaceOf } from \"@naculus/connect-core\";\nimport type { ChainInfo, WalletChain } from \"./types\";\n\n/**\n * The EIP-155 chain number, or null when this is not an EVM chain.\n *\n * Callers that need a number — a viem client, an ERC-20 read — must handle\n * the null rather than defaulting, because there is no number that means\n * \"Solana\" and any stand-in would address a real EVM chain.\n */\nexport function chainNumber(chain: WalletChain): number | null {\n return eip155Reference(chain.caip2);\n}\n\n/** The namespace a chain belongs to, read from its CAIP-2 id. */\nexport function chainNamespace(chain: WalletChain): string | null {\n return namespaceOf(chain.caip2);\n}\n\n/**\n * The configured chain for a CAIP-2 id, or null.\n *\n * A string comparison now that `WalletChain` is keyed by CAIP-2. It used to\n * pull an integer out of the id and compare that, which no non-EVM chain\n * could ever match.\n */\nexport function resolveChain(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): WalletChain | null {\n if (!chainId) return null;\n return chains.find((c) => c.caip2 === chainId) ?? null;\n}\n\n/**\n * The chains worth offering for the session's namespace.\n *\n * This used to return every configured chain regardless of what was\n * connected, so a Solana wallet was offered Ethereum and Polygon in the\n * switcher. Clicking one asked a wallet that has no concept of EIP-155 to\n * switch to chain 137 — an action the interface presented as available and\n * that could never work.\n *\n * With no connected chain, everything is offered: nothing has been ruled out\n * yet, and hiding the list would leave a user unable to pick.\n */\nexport function chainsForNamespace(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): WalletChain[] {\n if (!chainId) return chains;\n const namespace = namespaceOf(chainId);\n if (!namespace) return chains;\n return chains.filter((c) => chainNamespace(c) === namespace);\n}\n\n/**\n * A description of the connected chain for display.\n *\n * Returns something for every namespace. It used to return null for anything\n * that was not EIP-155, so a connected Solana wallet had no chain to show at\n * all and the interface fell back to \"Unknown Chain\".\n */\nexport function describeChain(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): ChainInfo | null {\n if (!chainId) return null;\n const namespace = namespaceOf(chainId);\n if (!namespace) return null;\n\n const known = resolveChain(chains, chainId);\n if (known) {\n return { namespace, chainId, name: known.name, selected: true };\n }\n\n // No configured entry. Name it by its CAIP-2 reference rather than\n // inventing one: \"Chain 5\" for a Solana cluster would be a fabrication, and\n // the reference is at least something a user can look up.\n const reference = chainId.slice(namespace.length + 1);\n return {\n namespace,\n chainId,\n name: namespace === \"eip155\" ? `Chain ${reference}` : reference,\n selected: true,\n };\n}\n","/**\n * The viem `Chain` shape for a configured EVM chain.\n *\n * Three hooks each built this inline — `useBalance`, `useTokenBalance` and\n * `useViemClient` — with identical bodies and the same `?? \"ETH\"` in each.\n * Three copies of a fallback is three places for it to be wrong.\n *\n * Returns a plain object rather than importing viem, so this package stays\n * free of a dependency only the React bindings need. The shape satisfies\n * viem's `Chain` structurally.\n */\n\nimport { CHAINS } from \"@naculus/connect-core\";\nimport type { WalletChain } from \"./types\";\n\nexport interface ViemChainShape {\n id: number;\n name: string;\n nativeCurrency: { name: string; symbol: string; decimals: number };\n rpcUrls: {\n default: { http: string[] };\n public: { http: string[] };\n };\n}\n\n/**\n * Build the chain descriptor a viem client needs, or null when this chain is\n * not one viem can talk to.\n *\n * Null for a non-EVM chain rather than a stand-in number: viem speaks to EVM\n * nodes, and any number chosen to fill the gap addresses a real chain that is\n * not the one connected.\n *\n * The native symbol comes from the chain's own `token`, then from the shared\n * chain registry. For an unlisted chain it stays empty rather than claiming\n * the asset is ETH. viem requires a string here; the user-facing symbol comes\n * from `useBalance().symbol`, which stays null when it is unknown.\n */\nexport function toViemChain(\n chain: WalletChain,\n chainNumber: number | null,\n): ViemChainShape | null {\n if (chainNumber === null || !chain.rpcUrl) return null;\n\n const symbol =\n chain.token ?? CHAINS[chainNumber]?.nativeCurrency?.symbol ?? \"\";\n\n return {\n id: chainNumber,\n name: chain.name,\n // Every EIP-155 native currency is 18 decimals by protocol, so this is a\n // constant rather than an assumption. ERC-20 decimals are per-token and\n // are never guessed anywhere in this codebase.\n nativeCurrency: { name: symbol, symbol, decimals: 18 },\n rpcUrls: {\n default: { http: [chain.rpcUrl] },\n public: { http: [chain.rpcUrl] },\n },\n };\n}\n","/**\n * Which EVM chain does an account-abstraction operation belong to?\n *\n * The smart-account hooks each resolved this as\n * `options.chainId ?? connectedChainId ?? \"eip155:1\"`, and each then guarded\n * with `if (connectedChainId && connectedChainId !== target)`. The guard is\n * skipped precisely when the mainnet default is used, so the one branch that\n * assumes a chain is the one branch that never checks it.\n *\n * That branch is reachable: a caller passing its own `signer` needs no\n * session, so `connectedChainId` is legitimately absent. It would then build\n * and sign a UserOperation for chain 1 — with chain 1's EntryPoint — while its\n * configured RPC and bundler point somewhere else. The chain ID is part of the\n * UserOperation hash, so the user approves a commitment to a chain they were\n * never shown.\n *\n * Resolving to undefined instead lets each caller refuse in whatever way suits\n * it. Framework-agnostic on purpose: it must not import React.\n */\n\n/** CAIP-2 for an EVM chain. The reference is a decimal chain ID with no leading zero. */\nconst EVM_CHAIN_ID = /^eip155:[1-9][0-9]*$/;\n\nexport function isEvmChainId(value: string | null | undefined): boolean {\n return typeof value === \"string\" && EVM_CHAIN_ID.test(value);\n}\n\n/**\n * The chain to operate on, or undefined when it cannot be known.\n *\n * An explicitly requested chain wins over the connected one; a malformed value\n * from either source resolves to undefined rather than being replaced by a\n * guess, because the caller asked for something specific and got it wrong.\n */\nexport function resolveEvmChainId(\n requested: string | null | undefined,\n connected: string | null | undefined,\n): string | undefined {\n const candidate = requested ?? connected ?? undefined;\n return isEvmChainId(candidate) ? candidate : undefined;\n}\n","/**\n * Does this token belong to the chain the wallet is pointed at?\n *\n * `TokenConfig.chainId` existed but was only ever read as a display label and\n * a cache key. Nothing compared it to the connected chain, so a hook handed a\n * mainnet token config while the wallet sat on Polygon would read — or\n * approve — whatever contract happens to occupy that address on Polygon.\n * Usually that address holds nothing and the call reverts, which is merely\n * confusing. When it holds a different token, `allowance()` returns a\n * plausible number for the wrong asset, and `approve()` grants a spender\n * rights over something the user never looked at.\n *\n * Framework-agnostic on purpose: it must not import React.\n */\n\nimport type { TokenConfig } from \"@naculus/connect-core\";\nimport { WalletError } from \"@naculus/connect-core\";\n\n/**\n * A mismatch, or undefined when the token is on the active chain — or when\n * there is not enough information to tell.\n *\n * Silence on \"cannot tell\" is deliberate. A token config without a chainId, or\n * a session with no resolved chain, is a gap in what the caller supplied; a\n * guess in either direction would be worse than letting the call proceed and\n * fail on its own terms.\n */\nexport function tokenChainMismatch(\n token: Pick<TokenConfig, \"address\" | \"chainId\">,\n activeChainId: number | undefined,\n): WalletError | undefined {\n if (typeof token?.chainId !== \"number\" || !Number.isFinite(token.chainId)) {\n return undefined;\n }\n if (typeof activeChainId !== \"number\" || !Number.isFinite(activeChainId)) {\n return undefined;\n }\n if (token.chainId === activeChainId) return undefined;\n\n return new WalletError(\n \"chain_mismatch\",\n `Token ${token.address} is configured for chain ${token.chainId}, but the ` +\n `wallet is on chain ${activeChainId}. Switch chains before continuing — ` +\n `the same address holds a different contract on each chain.`,\n );\n}\n","/**\n * Decimals cache keyed by the token it belongs to.\n *\n * The ERC-20 hooks cached decimals in a bare `useRef` seeded from the token\n * prop. `useRef` only takes its initial value on mount, and nothing reset it,\n * so a component that lets the user switch tokens kept the first token's\n * precision: picking USDC (6) and then DAI (18) converted \"100\" with 6\n * decimals and transferred 0.0000000001 DAI. The transaction succeeded, so\n * nothing surfaced the mistake.\n *\n * Keying the cache by the token makes a stale hit impossible without needing\n * an effect to fire first.\n */\n\nexport interface TokenIdentity {\n chainId?: number;\n address: string;\n}\n\nexport interface DecimalsCache {\n key: string;\n value: number | undefined;\n}\n\nexport function tokenKey(token: TokenIdentity): string {\n return `${token.chainId ?? \"?\"}:${token.address.toLowerCase()}`;\n}\n\nexport function createDecimalsCache(\n token: TokenIdentity,\n known?: number,\n): DecimalsCache {\n return { key: tokenKey(token), value: known };\n}\n\n/**\n * Cached decimals for `token`, or undefined when the cache holds another\n * token's value. Resets the cache as a side effect so the next write lands\n * against the right key.\n */\nexport function readDecimals(\n cache: DecimalsCache,\n token: TokenIdentity,\n known?: number,\n): number | undefined {\n const key = tokenKey(token);\n if (cache.key !== key) {\n cache.key = key;\n cache.value = known;\n }\n return cache.value;\n}\n\nexport function writeDecimals(\n cache: DecimalsCache,\n token: TokenIdentity,\n value: number,\n): void {\n cache.key = tokenKey(token);\n cache.value = value;\n}\n","/**\n * Normalize an EIP-1193 provider error into a WalletError.\n *\n * Hooks were collapsing every non-WalletError into one code — `useSwitchChain`\n * labelled a user pressing Cancel as `chain_unsupported`, so the UI told them\n * the chain was unavailable when they had simply declined. The codes below are\n * the ones wallets actually send, and they mean different things to a caller:\n * one is retryable by asking again, the other is not retryable at all until\n * the chain is added.\n *\n * Framework-agnostic on purpose: it must not import React.\n */\n\nimport type { WalletErrorCode } from \"@naculus/connect-core\";\nimport { WalletError } from \"@naculus/connect-core\";\n\n/** EIP-1193: the user rejected the request. */\nconst USER_REJECTED = 4001;\n/** EIP-3326: the wallet does not recognize the chain and must add it first. */\nconst UNRECOGNIZED_CHAIN = 4902;\n\nfunction providerCode(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"number\") return code;\n // Some wallets stringify it, and some nest the original under `data`.\n if (typeof code === \"string\" && /^-?\\d+$/.test(code)) return Number(code);\n const nested = (error as { data?: { originalError?: unknown } }).data\n ?.originalError;\n return nested ? providerCode(nested) : undefined;\n}\n\n/**\n * Map a chain-switch failure onto the code that describes it.\n *\n * `fallback` is used when the wallet gave nothing to go on, so the caller\n * still decides what an unexplained failure means in its own context.\n */\nexport function toChainSwitchError(\n error: unknown,\n fallback: WalletErrorCode = \"chain_unsupported\",\n): WalletError {\n if (error instanceof WalletError) return error;\n\n const message =\n error instanceof Error\n ? error.message\n : \"Unknown error during chain switch\";\n\n switch (providerCode(error)) {\n case USER_REJECTED:\n return new WalletError(\"chain_switch_rejected\", message, error);\n case UNRECOGNIZED_CHAIN:\n return new WalletError(\n \"chain_unsupported\",\n `${message} (the wallet does not have this chain configured)`,\n error,\n );\n default:\n return new WalletError(fallback, message, error);\n }\n}\n","/**\n * Decode an EVM revert payload into something a user can act on.\n *\n * This text is what a wallet shows when a simulation says a transaction would\n * fail, so it is worth getting right and worth sharing: the logic is pure and\n * every binding needs the same answer.\n *\n * Two things are handled that the inlined version did not. The ABI length\n * word is attacker-controlled, so a payload can declare a longer string than\n * it carries; decoding that produced a run of NUL bytes rather than an honest\n * \"could not decode\". And a Panic was reported as \"Built-in failure\" with the\n * code discarded, which is the one piece of information that distinguishes an\n * overflow from a division by zero.\n */\n\n/** keccak256(\"Error(string)\")[0:4] */\nconst ERROR_STRING_SELECTOR = \"08c379a0\";\n/** keccak256(\"Panic(uint256)\")[0:4] */\nconst PANIC_SELECTOR = \"4e487b71\";\n\n/**\n * Panic codes defined by Solidity. Unlisted codes still surface their raw\n * value rather than being flattened into a generic message.\n */\nconst PANIC_REASONS: Record<number, string> = {\n 0x01: \"assertion failed\",\n 0x11: \"arithmetic overflow or underflow\",\n 0x12: \"division or modulo by zero\",\n 0x21: \"invalid enum value\",\n 0x31: \"pop() on an empty array\",\n 0x32: \"array index out of bounds\",\n};\n\nfunction hexToUtf8(hex: string): string {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * @param data Raw revert data from the node, with or without the 0x prefix.\n * @returns The decoded reason, or undefined when the payload is not a shape\n * this understands — callers should fall back to the RPC error text rather\n * than presenting a guess.\n */\nexport function decodeRevertReason(data: unknown): string | undefined {\n if (typeof data !== \"string\" || data.length === 0) return undefined;\n const clean = data.startsWith(\"0x\") ? data.slice(2) : data;\n if (!/^[0-9a-fA-F]*$/.test(clean)) return undefined;\n\n if (clean.startsWith(ERROR_STRING_SELECTOR)) {\n // selector(8) + offset(64) + length(64) + payload\n if (clean.length < 136) return undefined;\n const declared = Number.parseInt(clean.slice(72, 136), 16);\n if (!Number.isSafeInteger(declared) || declared <= 0) return undefined;\n const available = (clean.length - 136) / 2;\n // Refuse a payload that promises more than it carries instead of padding\n // the difference with NULs and calling it a revert reason.\n if (declared > available) return undefined;\n return hexToUtf8(clean.slice(136, 136 + declared * 2));\n }\n\n if (clean.startsWith(PANIC_SELECTOR)) {\n if (clean.length < 8 + 64) return \"Panic\";\n const code = Number.parseInt(clean.slice(8, 72), 16);\n if (!Number.isSafeInteger(code)) return \"Panic\";\n const known = PANIC_REASONS[code];\n const hex = `0x${code.toString(16).padStart(2, \"0\")}`;\n return known ? `Panic ${hex}: ${known}` : `Panic ${hex}`;\n }\n\n return undefined;\n}\n\n/** Decoded reason if available, otherwise the node's own message. */\nexport function resolveRevertReason(\n data: unknown,\n fallbackMessage: string,\n): string {\n return (\n decodeRevertReason(data) ??\n (fallbackMessage.includes(\"revert\")\n ? fallbackMessage\n : \"Transaction reverted\")\n );\n}\n","/**\n * Shared NameResolver instance for the name-resolution hooks.\n *\n * useResolveName and useLookupAddress each carried an identical getResolver\n * with its own module-level singleton, so the two directions of the same\n * lookup ran against separate resolvers and separate caches. One definition,\n * one instance.\n */\nimport { NameResolver } from \"@naculus/connect-core\";\nimport type { NameResolverConfig } from \"@naculus/connect-core\";\n\nlet sharedResolver: NameResolver | null = null;\n\n/**\n * @param config When supplied, returns a dedicated resolver rather than the\n * shared one — a caller asking for specific settings must not be handed an\n * instance configured by someone else.\n */\nexport function getResolver(config?: NameResolverConfig): NameResolver {\n if (config) return new NameResolver(config);\n if (!sharedResolver) sharedResolver = new NameResolver();\n return sharedResolver;\n}\n\n/** Test seam: drop the shared instance. */\nexport function __resetResolverForTests(): void {\n sharedResolver = null;\n}\n","/**\n * Framework-agnostic core of the connect provider.\n *\n * appkit's stated shape is \"write the Stencil components once, generate the\n * React and Vue wrappers\". In practice `react/` carries ~8.5k hand-written\n * lines against `vue/`'s ~280, and only two files in `react/src` contain JSX —\n * the rest is logic wearing React clothes. Web Components also cannot serve\n * React Native or Flutter, so the presentation layer was never going to be the\n * thing worth sharing.\n *\n * This module is the first piece moved out: the state machine and the pure\n * helpers around it, with no React import. Everything here is consumable from\n * Vue, Svelte, or a native binding as-is, and `web3-store.test.ts` asserts the\n * absence of a framework import so the boundary cannot rot back.\n */\nimport { logger } from \"@naculus/connect-core\";\nimport type { ConnectionStatus, Web3State } from \"./types\";\n\n// ── Async helpers ──────────────────────────────────────────────────────\n\nexport function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n label = \"Operation\",\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`${label} timed out after ${ms}ms`)),\n ms,\n );\n });\n return Promise.race([promise, timeout]).finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n}\n\nexport interface RetryOptions {\n maxRetries: number;\n baseDelay: number;\n label?: string;\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= options.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n if (attempt < options.maxRetries) {\n const delay = options.baseDelay * 2 ** attempt;\n logger.warn(\n \"appkit/core\",\n `Retry attempt ${attempt + 1}/${options.maxRetries} after ${delay}ms`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n throw lastError;\n}\n\n// ── CAIP normalisation ─────────────────────────────────────────────────\n\n/** Convert EIP-1193 chainChanged values into the CAIP-2 form used by sessions. */\nexport function normalizeEip155ChainId(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n try {\n if (value.startsWith(\"eip155:\")) {\n const reference = value.slice(\"eip155:\".length);\n if (!/^\\d+$/.test(reference)) return undefined;\n return `eip155:${BigInt(reference).toString(10)}`;\n }\n if (/^0x[0-9a-f]+$/i.test(value) || /^\\d+$/.test(value)) {\n return `eip155:${BigInt(value).toString(10)}`;\n }\n } catch {\n // Ignore malformed wallet events rather than corrupting session state.\n }\n return undefined;\n}\n\nexport function toEip155Accounts(\n accounts: unknown[],\n chainId: string,\n): string[] {\n const reference = chainId.split(\":\")[1];\n if (!reference) return [];\n return accounts\n .filter(\n (account): account is string =>\n typeof account === \"string\" && account.length > 0,\n )\n .map((account) =>\n account.includes(\":\") ? (account.split(\":\").pop() as string) : account,\n )\n .map((account) => `eip155:${reference}:${account}`);\n}\n\n/**\n * Numeric EIP-155 chain id, or undefined for any other namespace.\n *\n * The previous implementation took the last CAIP segment and ran parseInt over\n * it, which stops at the first non-digit: `solana:5eykt4Us…` became 5 — an\n * EVM chain id — so asking for Solana mainnet's token list returned Goerli's,\n * and `xrpl:0` became chain 0. Anchor the whole reference instead.\n */\nexport function eip155ChainIdToNumber(chainId: string): number | undefined {\n const match = /^eip155:([1-9][0-9]*)$/.exec(chainId);\n if (!match) return undefined;\n const numeric = Number(match[1]);\n return Number.isSafeInteger(numeric) ? numeric : undefined;\n}\n\n// ── State machine ──────────────────────────────────────────────────────\n\nexport type Web3Action =\n | { type: \"SET_STATUS\"; payload: ConnectionStatus }\n | { type: \"SET_SESSION\"; payload: Web3State[\"session\"] }\n | { type: \"SET_ACCOUNTS\"; payload: string[] }\n | { type: \"SET_CHAIN\"; payload: string | null }\n | { type: \"SET_ERROR\"; payload: Error | null }\n | { type: \"RESET\" };\n\nexport const initialWeb3State: Web3State = {\n status: \"disconnected\",\n session: null,\n accounts: [],\n chainId: null,\n error: null,\n};\n\nexport function web3Reducer(state: Web3State, action: Web3Action): Web3State {\n switch (action.type) {\n case \"SET_STATUS\":\n return { ...state, status: action.payload };\n case \"SET_SESSION\":\n return { ...state, session: action.payload };\n case \"SET_ACCOUNTS\":\n return { ...state, accounts: action.payload };\n case \"SET_CHAIN\":\n return { ...state, chainId: action.payload };\n case \"SET_ERROR\":\n return { ...state, error: action.payload };\n case \"RESET\":\n return initialWeb3State;\n default:\n return state;\n }\n}\n"],"mappings":"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","names":["chainNumber","WalletError"]}
1
+ {"version":3,"sources":["../src/capabilities.ts","../src/solana-roles.ts","../src/chain-selection.ts","../src/viem-chain.ts","../src/aa-chain.ts","../src/token-chain.ts","../src/token-decimals.ts","../src/provider-errors.ts","../src/revert-reason.ts","../src/name-resolver.ts","../src/notification.ts","../src/destination.ts","../src/routing.ts","../src/web3-store.ts"],"sourcesContent":["/**\n * EIP-5792 capability answers, normalised once.\n *\n * Deciding how to execute before sending is the whole point of\n * `wallet_getCapabilities`, and the reasoning that turns a raw answer into\n * that decision is not React's. It lived inside the React hook, which meant a\n * Vue composable could only get it by copying — and a second copy of \"absence\n * is not a denial\" is exactly the copy that drifts back into `false`.\n */\n\nimport {\n caip2ToHexChain,\n readAtomicSupport,\n type WalletCapabilities,\n} from \"@naculus/connect-core\";\n\n/**\n * Whether a wallet can execute a batch atomically.\n *\n * Three values, not two. A wallet that does not implement\n * `wallet_getCapabilities` has not said no — it has said nothing, and EIP-5792\n * is explicit that absence is not a denial. Collapsing that into `false` sends\n * every such wallet down the sequential path, which is the one where an\n * approve can land and the swap it was for can fail.\n */\nexport type AtomicSupport = \"supported\" | \"unsupported\" | \"unknown\";\n\nexport interface ChainCapabilities {\n /** CAIP-2 chain this describes. */\n chainId: string;\n atomic: AtomicSupport;\n /** Largest batch the wallet will accept, when it says. */\n maxBatchSize?: number;\n /** The raw entry, for capabilities this SDK does not model. */\n raw: WalletCapabilities;\n}\n\n/**\n * Turn a wallet's raw answer into per-chain entries.\n *\n * Entries that are not objects are dropped rather than coerced: a wallet\n * answering `null` for a chain has said nothing about it, and inventing an\n * `\"unsupported\"` there would be the same mistake as reading absence as denial.\n */\nexport function normalizeCapabilities(\n raw: Record<string, unknown> | null | undefined,\n): Record<string, ChainCapabilities> {\n const out: Record<string, ChainCapabilities> = {};\n for (const [key, entry] of Object.entries(raw ?? {})) {\n if (!entry || typeof entry !== \"object\") continue;\n const { supported, maxBatchSize } = readAtomicSupport(\n entry as Record<string, unknown>,\n );\n out[key] = {\n chainId: key,\n atomic: supported ? \"supported\" : \"unsupported\",\n ...(maxBatchSize === undefined ? {} : { maxBatchSize }),\n raw: entry as WalletCapabilities,\n };\n }\n return out;\n}\n\n/**\n * Find the entry for a chain, by CAIP-2 or by the hex form.\n *\n * Connectors normalise keys to CAIP-2, but a wallet answering in raw hex\n * through a custom connector should still be found rather than reported as\n * \"unknown\" next to an entry that is sitting right there.\n */\nexport function selectChainCapabilities(\n capabilities: Record<string, ChainCapabilities> | null | undefined,\n chainId: string | null | undefined,\n): ChainCapabilities | null {\n if (!capabilities || !chainId) return null;\n const hex = caip2ToHexChain(chainId);\n return capabilities[chainId] ?? (hex ? capabilities[hex] : undefined) ?? null;\n}\n\n/**\n * Atomic support for a chain.\n *\n * `\"unknown\"` covers every case where we have not been told: no query yet, a\n * wallet without the method, a failed query, one still in flight. A caller\n * choosing between an atomic and a sequential path needs that distinction, not\n * a confident `false`.\n */\nexport function atomicSupportFor(\n capabilities: Record<string, ChainCapabilities> | null | undefined,\n chainId: string | null | undefined,\n): AtomicSupport {\n return selectChainCapabilities(capabilities, chainId)?.atomic ?? \"unknown\";\n}\n","/**\n * Solana signer roles, read once for both frameworks.\n *\n * `@naculus/connector-solana` splits a connected wallet into `identity`,\n * `signer` and `payer`, each `null` when the wallet declared it cannot fill\n * that job. That answer is the connector's. What this module owns is the part\n * a UI has to decide before it can show anything: *why* there is no answer —\n * because nothing is connected, because the connected wallet is not Solana,\n * or because the connector predates the roles API and has not said. Those are\n * three different screens, and a hook that hands back one `null` for all of\n * them makes every consumer rediscover the distinction.\n */\n\n/** Which account this is, with no ability to sign for it. */\nexport interface SolanaIdentity {\n /** Base58 public key. */\n readonly address: string;\n /** CAIP-2 chain this account is being used on. */\n readonly chain: string;\n}\n\n/** An account that will contribute a signature without submitting anything. */\nexport interface SolanaSigner extends SolanaIdentity {\n signMessage?: (message: Uint8Array) => Promise<Uint8Array>;\n signTransaction: (transaction: Uint8Array) => Promise<Uint8Array>;\n /** Absent, not throwing, when the wallet has no batch feature. */\n signAllTransactions?: (transactions: Uint8Array[]) => Promise<Uint8Array[]>;\n}\n\n/** An account that signs and broadcasts through the wallet's own RPC. */\nexport interface SolanaPayer extends SolanaIdentity {\n /** Returns the base58 transaction signature the cluster accepted. */\n signAndSendTransaction: (transaction: Uint8Array) => Promise<string>;\n}\n\n/** What the wallet declared, recorded at discovery. Two-state on purpose. */\nexport interface SolanaWalletFeatures {\n signMessage: boolean;\n signTransaction: boolean;\n signAllTransactions: boolean;\n signAndSendTransaction: boolean;\n}\n\n/**\n * The roles a connected account can fill.\n *\n * Structurally identical to `SolanaRoles` in `@naculus/connector-solana`,\n * declared here rather than imported because this package depends only on\n * `@naculus/connect-core`, and the connector's own declaration is what its\n * `getRoles` returns — a value of that type satisfies this one unchanged.\n */\nexport interface SolanaRoles {\n identity: SolanaIdentity;\n signer: SolanaSigner | null;\n payer: SolanaPayer | null;\n features: SolanaWalletFeatures;\n}\n\n/** The part of a connector this needs. `getRoles` is optional on purpose. */\nexport interface SolanaRolesSource {\n getRoles?: (session: unknown) => SolanaRoles | null;\n}\n\n/** The part of a session this needs. */\nexport interface SessionLike {\n walletType?: string;\n}\n\n/**\n * Why `roles` is null.\n *\n * - `no_session` — nothing connected, or the connector has no live session\n * for the one the app holds (a restored session before reconnect).\n * - `not_solana` — the connected wallet is on another namespace. Not an\n * error; a Solana panel simply does not apply.\n * - `unreported` — the connector does not expose `getRoles`. An older\n * `@naculus/connector-solana`, or a custom one. The wallet has not said\n * what it can do, which is not the same as saying it can do nothing.\n */\nexport type SolanaRolesAbsence = \"no_session\" | \"not_solana\" | \"unreported\";\n\nexport type SolanaRolesState =\n | { roles: SolanaRoles; absence: null }\n | { roles: null; absence: SolanaRolesAbsence };\n\n/**\n * Read the roles for the session the app holds.\n *\n * Synchronous and pure: the connector already recorded what the wallet\n * declared, so there is nothing to await and no prompt to trigger. Reading\n * roles must never cost the user an approval dialog.\n */\nexport function readSolanaRoles(\n connector: SolanaRolesSource | null | undefined,\n session: SessionLike | null | undefined,\n): SolanaRolesState {\n if (!session) return { roles: null, absence: \"no_session\" };\n if (session.walletType !== \"solana\") {\n return { roles: null, absence: \"not_solana\" };\n }\n if (typeof connector?.getRoles !== \"function\") {\n return { roles: null, absence: \"unreported\" };\n }\n const roles = connector.getRoles(session);\n return roles\n ? { roles, absence: null }\n : { roles: null, absence: \"no_session\" };\n}\n","/**\n * Which chains a session can actually switch to, and what the current one is.\n *\n * Framework-neutral, and deliberately built on `parseChainId` from\n * `@naculus/connect-core` rather than on another `startsWith(\"eip155:\")`.\n */\n\nimport { eip155Reference, namespaceOf } from \"@naculus/connect-core\";\nimport type { ChainInfo, WalletChain } from \"./types\";\n\n/**\n * The EIP-155 chain number, or null when this is not an EVM chain.\n *\n * Callers that need a number — a viem client, an ERC-20 read — must handle\n * the null rather than defaulting, because there is no number that means\n * \"Solana\" and any stand-in would address a real EVM chain.\n */\nexport function chainNumber(chain: WalletChain): number | null {\n return eip155Reference(chain.caip2);\n}\n\n/** The namespace a chain belongs to, read from its CAIP-2 id. */\nexport function chainNamespace(chain: WalletChain): string | null {\n return namespaceOf(chain.caip2);\n}\n\n/**\n * The configured chain for a CAIP-2 id, or null.\n *\n * A string comparison now that `WalletChain` is keyed by CAIP-2. It used to\n * pull an integer out of the id and compare that, which no non-EVM chain\n * could ever match.\n */\nexport function resolveChain(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): WalletChain | null {\n if (!chainId) return null;\n return chains.find((c) => c.caip2 === chainId) ?? null;\n}\n\n/**\n * The chains worth offering for the session's namespace.\n *\n * This used to return every configured chain regardless of what was\n * connected, so a Solana wallet was offered Ethereum and Polygon in the\n * switcher. Clicking one asked a wallet that has no concept of EIP-155 to\n * switch to chain 137 — an action the interface presented as available and\n * that could never work.\n *\n * With no connected chain, everything is offered: nothing has been ruled out\n * yet, and hiding the list would leave a user unable to pick.\n */\nexport function chainsForNamespace(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): WalletChain[] {\n if (!chainId) return chains;\n const namespace = namespaceOf(chainId);\n if (!namespace) return chains;\n return chains.filter((c) => chainNamespace(c) === namespace);\n}\n\n/**\n * A description of the connected chain for display.\n *\n * Returns something for every namespace. It used to return null for anything\n * that was not EIP-155, so a connected Solana wallet had no chain to show at\n * all and the interface fell back to \"Unknown Chain\".\n */\nexport function describeChain(\n chains: WalletChain[],\n chainId: string | null | undefined,\n): ChainInfo | null {\n if (!chainId) return null;\n const namespace = namespaceOf(chainId);\n if (!namespace) return null;\n\n const known = resolveChain(chains, chainId);\n if (known) {\n return { namespace, chainId, name: known.name, selected: true };\n }\n\n // No configured entry. Name it by its CAIP-2 reference rather than\n // inventing one: \"Chain 5\" for a Solana cluster would be a fabrication, and\n // the reference is at least something a user can look up.\n const reference = chainId.slice(namespace.length + 1);\n return {\n namespace,\n chainId,\n name: namespace === \"eip155\" ? `Chain ${reference}` : reference,\n selected: true,\n };\n}\n","/**\n * The viem `Chain` shape for a configured EVM chain.\n *\n * Three hooks each built this inline — `useBalance`, `useTokenBalance` and\n * `useViemClient` — with identical bodies and the same `?? \"ETH\"` in each.\n * Three copies of a fallback is three places for it to be wrong.\n *\n * Returns a plain object rather than importing viem, so this package stays\n * free of a dependency only the React bindings need. The shape satisfies\n * viem's `Chain` structurally.\n */\n\nimport { CHAINS } from \"@naculus/connect-core\";\nimport type { WalletChain } from \"./types\";\n\nexport interface ViemChainShape {\n id: number;\n name: string;\n nativeCurrency: { name: string; symbol: string; decimals: number };\n rpcUrls: {\n default: { http: string[] };\n public: { http: string[] };\n };\n}\n\n/**\n * Build the chain descriptor a viem client needs, or null when this chain is\n * not one viem can talk to.\n *\n * Null for a non-EVM chain rather than a stand-in number: viem speaks to EVM\n * nodes, and any number chosen to fill the gap addresses a real chain that is\n * not the one connected.\n *\n * The native symbol comes from the chain's own `token`, then from the shared\n * chain registry. For an unlisted chain it stays empty rather than claiming\n * the asset is ETH. viem requires a string here; the user-facing symbol comes\n * from `useBalance().symbol`, which stays null when it is unknown.\n */\nexport function toViemChain(\n chain: WalletChain,\n chainNumber: number | null,\n): ViemChainShape | null {\n if (chainNumber === null || !chain.rpcUrl) return null;\n\n const symbol =\n chain.token ?? CHAINS[chainNumber]?.nativeCurrency?.symbol ?? \"\";\n\n return {\n id: chainNumber,\n name: chain.name,\n // Every EIP-155 native currency is 18 decimals by protocol, so this is a\n // constant rather than an assumption. ERC-20 decimals are per-token and\n // are never guessed anywhere in this codebase.\n nativeCurrency: { name: symbol, symbol, decimals: 18 },\n rpcUrls: {\n default: { http: [chain.rpcUrl] },\n public: { http: [chain.rpcUrl] },\n },\n };\n}\n","/**\n * Which EVM chain does an account-abstraction operation belong to?\n *\n * The smart-account hooks each resolved this as\n * `options.chainId ?? connectedChainId ?? \"eip155:1\"`, and each then guarded\n * with `if (connectedChainId && connectedChainId !== target)`. The guard is\n * skipped precisely when the mainnet default is used, so the one branch that\n * assumes a chain is the one branch that never checks it.\n *\n * That branch is reachable: a caller passing its own `signer` needs no\n * session, so `connectedChainId` is legitimately absent. It would then build\n * and sign a UserOperation for chain 1 — with chain 1's EntryPoint — while its\n * configured RPC and bundler point somewhere else. The chain ID is part of the\n * UserOperation hash, so the user approves a commitment to a chain they were\n * never shown.\n *\n * Resolving to undefined instead lets each caller refuse in whatever way suits\n * it. Framework-agnostic on purpose: it must not import React.\n */\n\n/** CAIP-2 for an EVM chain. The reference is a decimal chain ID with no leading zero. */\nconst EVM_CHAIN_ID = /^eip155:[1-9][0-9]*$/;\n\nexport function isEvmChainId(value: string | null | undefined): boolean {\n return typeof value === \"string\" && EVM_CHAIN_ID.test(value);\n}\n\n/**\n * The chain to operate on, or undefined when it cannot be known.\n *\n * An explicitly requested chain wins over the connected one; a malformed value\n * from either source resolves to undefined rather than being replaced by a\n * guess, because the caller asked for something specific and got it wrong.\n */\nexport function resolveEvmChainId(\n requested: string | null | undefined,\n connected: string | null | undefined,\n): string | undefined {\n const candidate = requested ?? connected ?? undefined;\n return isEvmChainId(candidate) ? candidate : undefined;\n}\n","/**\n * Does this token belong to the chain the wallet is pointed at?\n *\n * `TokenConfig.chainId` existed but was only ever read as a display label and\n * a cache key. Nothing compared it to the connected chain, so a hook handed a\n * mainnet token config while the wallet sat on Polygon would read — or\n * approve — whatever contract happens to occupy that address on Polygon.\n * Usually that address holds nothing and the call reverts, which is merely\n * confusing. When it holds a different token, `allowance()` returns a\n * plausible number for the wrong asset, and `approve()` grants a spender\n * rights over something the user never looked at.\n *\n * Framework-agnostic on purpose: it must not import React.\n */\n\nimport type { TokenConfig } from \"@naculus/connect-core\";\nimport { WalletError } from \"@naculus/connect-core\";\n\n/**\n * A mismatch, or undefined when the token is on the active chain — or when\n * there is not enough information to tell.\n *\n * Silence on \"cannot tell\" is deliberate. A token config without a chainId, or\n * a session with no resolved chain, is a gap in what the caller supplied; a\n * guess in either direction would be worse than letting the call proceed and\n * fail on its own terms.\n */\nexport function tokenChainMismatch(\n token: Pick<TokenConfig, \"address\" | \"chainId\">,\n activeChainId: number | undefined,\n): WalletError | undefined {\n if (typeof token?.chainId !== \"number\" || !Number.isFinite(token.chainId)) {\n return undefined;\n }\n if (typeof activeChainId !== \"number\" || !Number.isFinite(activeChainId)) {\n return undefined;\n }\n if (token.chainId === activeChainId) return undefined;\n\n return new WalletError(\n \"chain_mismatch\",\n `Token ${token.address} is configured for chain ${token.chainId}, but the ` +\n `wallet is on chain ${activeChainId}. Switch chains before continuing — ` +\n `the same address holds a different contract on each chain.`,\n );\n}\n","/**\n * Decimals cache keyed by the token it belongs to.\n *\n * The ERC-20 hooks cached decimals in a bare `useRef` seeded from the token\n * prop. `useRef` only takes its initial value on mount, and nothing reset it,\n * so a component that lets the user switch tokens kept the first token's\n * precision: picking USDC (6) and then DAI (18) converted \"100\" with 6\n * decimals and transferred 0.0000000001 DAI. The transaction succeeded, so\n * nothing surfaced the mistake.\n *\n * Keying the cache by the token makes a stale hit impossible without needing\n * an effect to fire first.\n */\n\nexport interface TokenIdentity {\n chainId?: number;\n address: string;\n}\n\nexport interface DecimalsCache {\n key: string;\n value: number | undefined;\n}\n\nexport function tokenKey(token: TokenIdentity): string {\n return `${token.chainId ?? \"?\"}:${token.address.toLowerCase()}`;\n}\n\nexport function createDecimalsCache(\n token: TokenIdentity,\n known?: number,\n): DecimalsCache {\n return { key: tokenKey(token), value: known };\n}\n\n/**\n * Cached decimals for `token`, or undefined when the cache holds another\n * token's value. Resets the cache as a side effect so the next write lands\n * against the right key.\n */\nexport function readDecimals(\n cache: DecimalsCache,\n token: TokenIdentity,\n known?: number,\n): number | undefined {\n const key = tokenKey(token);\n if (cache.key !== key) {\n cache.key = key;\n cache.value = known;\n }\n return cache.value;\n}\n\nexport function writeDecimals(\n cache: DecimalsCache,\n token: TokenIdentity,\n value: number,\n): void {\n cache.key = tokenKey(token);\n cache.value = value;\n}\n","/**\n * Normalize an EIP-1193 provider error into a WalletError.\n *\n * Hooks were collapsing every non-WalletError into one code — `useSwitchChain`\n * labelled a user pressing Cancel as `chain_unsupported`, so the UI told them\n * the chain was unavailable when they had simply declined. The codes below are\n * the ones wallets actually send, and they mean different things to a caller:\n * one is retryable by asking again, the other is not retryable at all until\n * the chain is added.\n *\n * Framework-agnostic on purpose: it must not import React.\n */\n\nimport type { WalletErrorCode } from \"@naculus/connect-core\";\nimport { WalletError } from \"@naculus/connect-core\";\n\n/** EIP-1193: the user rejected the request. */\nconst USER_REJECTED = 4001;\n/** EIP-3326: the wallet does not recognize the chain and must add it first. */\nconst UNRECOGNIZED_CHAIN = 4902;\n\nfunction providerCode(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\") return undefined;\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"number\") return code;\n // Some wallets stringify it, and some nest the original under `data`.\n if (typeof code === \"string\" && /^-?\\d+$/.test(code)) return Number(code);\n const nested = (error as { data?: { originalError?: unknown } }).data\n ?.originalError;\n return nested ? providerCode(nested) : undefined;\n}\n\n/**\n * Map a chain-switch failure onto the code that describes it.\n *\n * `fallback` is used when the wallet gave nothing to go on, so the caller\n * still decides what an unexplained failure means in its own context.\n */\nexport function toChainSwitchError(\n error: unknown,\n fallback: WalletErrorCode = \"chain_unsupported\",\n): WalletError {\n if (error instanceof WalletError) return error;\n\n const message =\n error instanceof Error\n ? error.message\n : \"Unknown error during chain switch\";\n\n switch (providerCode(error)) {\n case USER_REJECTED:\n return new WalletError(\"chain_switch_rejected\", message, error);\n case UNRECOGNIZED_CHAIN:\n return new WalletError(\n \"chain_unsupported\",\n `${message} (the wallet does not have this chain configured)`,\n error,\n );\n default:\n return new WalletError(fallback, message, error);\n }\n}\n","/**\n * Decode an EVM revert payload into something a user can act on.\n *\n * This text is what a wallet shows when a simulation says a transaction would\n * fail, so it is worth getting right and worth sharing: the logic is pure and\n * every binding needs the same answer.\n *\n * Two things are handled that the inlined version did not. The ABI length\n * word is attacker-controlled, so a payload can declare a longer string than\n * it carries; decoding that produced a run of NUL bytes rather than an honest\n * \"could not decode\". And a Panic was reported as \"Built-in failure\" with the\n * code discarded, which is the one piece of information that distinguishes an\n * overflow from a division by zero.\n */\n\n/** keccak256(\"Error(string)\")[0:4] */\nconst ERROR_STRING_SELECTOR = \"08c379a0\";\n/** keccak256(\"Panic(uint256)\")[0:4] */\nconst PANIC_SELECTOR = \"4e487b71\";\n\n/**\n * Panic codes defined by Solidity. Unlisted codes still surface their raw\n * value rather than being flattened into a generic message.\n */\nconst PANIC_REASONS: Record<number, string> = {\n 0x01: \"assertion failed\",\n 0x11: \"arithmetic overflow or underflow\",\n 0x12: \"division or modulo by zero\",\n 0x21: \"invalid enum value\",\n 0x31: \"pop() on an empty array\",\n 0x32: \"array index out of bounds\",\n};\n\nfunction hexToUtf8(hex: string): string {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * @param data Raw revert data from the node, with or without the 0x prefix.\n * @returns The decoded reason, or undefined when the payload is not a shape\n * this understands — callers should fall back to the RPC error text rather\n * than presenting a guess.\n */\nexport function decodeRevertReason(data: unknown): string | undefined {\n if (typeof data !== \"string\" || data.length === 0) return undefined;\n const clean = data.startsWith(\"0x\") ? data.slice(2) : data;\n if (!/^[0-9a-fA-F]*$/.test(clean)) return undefined;\n\n if (clean.startsWith(ERROR_STRING_SELECTOR)) {\n // selector(8) + offset(64) + length(64) + payload\n if (clean.length < 136) return undefined;\n const declared = Number.parseInt(clean.slice(72, 136), 16);\n if (!Number.isSafeInteger(declared) || declared <= 0) return undefined;\n const available = (clean.length - 136) / 2;\n // Refuse a payload that promises more than it carries instead of padding\n // the difference with NULs and calling it a revert reason.\n if (declared > available) return undefined;\n return hexToUtf8(clean.slice(136, 136 + declared * 2));\n }\n\n if (clean.startsWith(PANIC_SELECTOR)) {\n if (clean.length < 8 + 64) return \"Panic\";\n const code = Number.parseInt(clean.slice(8, 72), 16);\n if (!Number.isSafeInteger(code)) return \"Panic\";\n const known = PANIC_REASONS[code];\n const hex = `0x${code.toString(16).padStart(2, \"0\")}`;\n return known ? `Panic ${hex}: ${known}` : `Panic ${hex}`;\n }\n\n return undefined;\n}\n\n/** Decoded reason if available, otherwise the node's own message. */\nexport function resolveRevertReason(\n data: unknown,\n fallbackMessage: string,\n): string {\n return (\n decodeRevertReason(data) ??\n (fallbackMessage.includes(\"revert\")\n ? fallbackMessage\n : \"Transaction reverted\")\n );\n}\n","/**\n * Shared NameResolver instance for the name-resolution hooks.\n *\n * useResolveName and useLookupAddress each carried an identical getResolver\n * with its own module-level singleton, so the two directions of the same\n * lookup ran against separate resolvers and separate caches. One definition,\n * one instance.\n */\nimport { NameResolver } from \"@naculus/connect-core\";\nimport type { NameResolverConfig } from \"@naculus/connect-core\";\n\nlet sharedResolver: NameResolver | null = null;\n\n/**\n * @param config When supplied, returns a dedicated resolver rather than the\n * shared one — a caller asking for specific settings must not be handed an\n * instance configured by someone else.\n */\nexport function getResolver(config?: NameResolverConfig): NameResolver {\n if (config) return new NameResolver(config);\n if (!sharedResolver) sharedResolver = new NameResolver();\n return sharedResolver;\n}\n\n/** Test seam: drop the shared instance. */\nexport function __resetResolverForTests(): void {\n sharedResolver = null;\n}\n","/** Framework-neutral in-app notification actions and settings persistence. */\n\nimport {\n createStorageAdapter,\n type InAppChannel,\n type NotificationFrequency,\n type NotificationItem,\n type NotificationSettings,\n} from \"@naculus/connect-core\";\n\nexport interface NotificationSettingsStorage {\n getItem<T>(key: string): Promise<T | null>;\n setItem<T>(key: string, value: T): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nexport const NOTIFICATION_SETTINGS_KEY = \"naculus_notif_react_settings\";\n\nexport const DEFAULT_NOTIFICATION_SETTINGS: NotificationSettings = {\n telegram: false,\n webpush: false,\n inapp: true,\n frequency: \"final-only\" as NotificationFrequency,\n mutedChains: [],\n mutedTypes: [],\n};\n\n/** Preserve the existing localStorage adapter and fail-open UI fallback. */\nexport function createNotificationSettingsStorage(): NotificationSettingsStorage {\n const storage = createStorageAdapter(\"local\", \"naculus_notif_\");\n return {\n getItem: async <T>(key: string): Promise<T | null> => {\n try {\n return await storage.get<T>(key);\n } catch (cause) {\n console.warn(\"useNotification: failed to read from storage:\", cause);\n return null;\n }\n },\n setItem: async <T>(key: string, value: T): Promise<void> => {\n try {\n await storage.set(key, value);\n } catch (cause) {\n console.warn(\"useNotification: failed to write to storage:\", cause);\n }\n },\n removeItem: async (key: string): Promise<void> => {\n try {\n await storage.remove(key);\n } catch (cause) {\n console.warn(\"useNotification: failed to remove from storage:\", cause);\n }\n },\n };\n}\n\nexport async function loadNotificationSettings(\n storage: NotificationSettingsStorage,\n): Promise<NotificationSettings | null> {\n return storage.getItem<NotificationSettings>(NOTIFICATION_SETTINGS_KEY);\n}\n\nexport function saveNotificationSettings(\n storage: NotificationSettingsStorage,\n settings: NotificationSettings,\n): void {\n storage.setItem(NOTIFICATION_SETTINGS_KEY, settings).catch(() => {});\n}\n\nexport function updateNotificationSettings(\n current: NotificationSettings,\n update: Partial<NotificationSettings>,\n): NotificationSettings {\n return { ...current, ...update };\n}\n\nexport function channelsUpdate(\n channels: string[],\n): Partial<NotificationSettings> {\n const update: Partial<NotificationSettings> = {};\n if (channels.includes(\"telegram\")) update.telegram = true;\n if (channels.includes(\"webpush\")) update.webpush = true;\n if (channels.includes(\"inapp\")) update.inapp = true;\n return update;\n}\n\nexport function mutedChainSettings(\n settings: NotificationSettings,\n chainId: string,\n muted: boolean,\n): Partial<NotificationSettings> {\n return {\n mutedChains: muted\n ? [...new Set([...settings.mutedChains, chainId])]\n : settings.mutedChains.filter((id) => id !== chainId),\n };\n}\n\nexport function createNotification(\n item: Omit<NotificationItem, \"id\" | \"timestamp\">,\n): NotificationItem {\n return {\n ...item,\n id: `notif_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n timestamp: Date.now(),\n };\n}\n\nfunction sendItem(\n channel: InAppChannel,\n item: NotificationItem,\n): Promise<void> {\n return channel.send({\n id: item.id,\n userId: \"\",\n txHash: item.txHash,\n chainId: \"\",\n chainName: item.chainName,\n status: item.status,\n title: item.title,\n body: item.body,\n valueFormatted: item.valueFormatted,\n explorerUrl: item.explorerUrl,\n timestamp: item.timestamp,\n });\n}\n\nexport function addInAppNotification(\n channel: InAppChannel,\n item: Omit<NotificationItem, \"id\" | \"timestamp\">,\n): void {\n void sendItem(channel, createNotification(item));\n}\n\n/** InAppChannel has no remove-one method; preserve its clear/replay behavior. */\nexport async function clearOneInAppNotification(\n channel: InAppChannel,\n id: string,\n): Promise<NotificationItem[]> {\n const remaining = channel.getHistory().filter((item) => item.id !== id);\n channel.clear();\n await Promise.all(remaining.map((item) => sendItem(channel, item)));\n return channel.getHistory();\n}\n","import { isEvmAddress, isZeroAddress } from \"@naculus/connect-core\";\n\n/**\n * Destination-address checks shared by the React hook and the Vue composable.\n *\n * Syntactic only. `\"ok\"` means none of these checks found anything wrong —\n * not that the recipient is who the user thinks. This cannot see an\n * address-poisoning lookalike, a contract that will not release the funds,\n * or a known-malicious destination. Labelling that \"safe\" once put a green\n * tick beside a scammer's address; the level is deliberately not \"safe\".\n */\n\nexport type AddressValidationLevel = \"ok\" | \"warning\" | \"blocked\";\n\n/** Machine-readable reason; the framework layer maps it to a user string. */\nexport type DestinationIssue =\n | \"empty\"\n | \"invalid_format\"\n | \"zero_address\"\n | \"burn_address\";\n\nexport interface DestinationValidation {\n isValid: boolean;\n level: AddressValidationLevel;\n issue: DestinationIssue | null;\n}\n\nconst DEAD_ADDRESSES = new Set([\n \"0x0000000000000000000000000000000000000001\",\n \"0x000000000000000000000000000000000000dead\",\n]);\n\n/**\n * Burn heuristic kept from the React hook: known sinks plus any well-formed\n * address containing \"dead\". Stricter than connect-core's prefix-only\n * `isBurnAddress`; unifying the two is a connect-lib change.\n */\nexport function isBurnDestination(address: string): boolean {\n const lower = address.toLowerCase();\n return DEAD_ADDRESSES.has(lower) || lower.includes(\"dead\");\n}\n\nexport function validateDestination(address: string): DestinationValidation {\n if (!address) return { isValid: false, level: \"blocked\", issue: \"empty\" };\n if (!isEvmAddress(address)) {\n return { isValid: false, level: \"blocked\", issue: \"invalid_format\" };\n }\n if (isZeroAddress(address)) {\n return { isValid: false, level: \"blocked\", issue: \"zero_address\" };\n }\n if (isBurnDestination(address)) {\n return { isValid: false, level: \"blocked\", issue: \"burn_address\" };\n }\n // Nothing wrong was found. That is not the same as safe.\n return { isValid: true, level: \"ok\", issue: null };\n}\n\n/**\n * The bare `0x…` account from either a CAIP-10 id (`eip155:1:0x…`) or a plain\n * hex address; null when it is neither. Hooks and composables should not each\n * carry their own copy of this split.\n */\nexport function bareEvmAddress(\n account: string | null | undefined,\n): `0x${string}` | null {\n if (!account) return null;\n const value = account.includes(\":\") ? account.split(\":\").pop() : account;\n return value && isEvmAddress(value) ? (value as `0x${string}`) : null;\n}\n","import { isValidAddress } from \"@naculus/connect-core\";\n\n/**\n * Chain-abstraction (routing) domain types and the few decisions the React\n * hooks and Vue composables share. The hooks inject the actual quote /\n * compare / execute functions; nothing here talks to a network.\n */\n\n// ── Quotes ────────────────────────────────────────────────────────\n\nexport interface RouteQuote {\n routeId: string;\n provider: string;\n netReceiveFormatted: string;\n toTokenSymbol: string;\n}\n\nexport interface RouteQuoteOptions {\n sortBy?: \"cost\" | \"time\" | \"balance\";\n maxSlippage?: number;\n}\n\nexport interface RouteQuoteInput {\n fromChain: string;\n toChain: string;\n fromToken: string;\n toToken?: string;\n amount: string;\n options?: RouteQuoteOptions;\n}\n\nexport type GetRouteQuotes = (\n from: string,\n to: string,\n token: string,\n amount: string,\n options?: RouteQuoteOptions,\n toToken?: string,\n) => Promise<RouteQuote[]>;\n\n/**\n * `BigInt()` throws on anything that is not a decimal integer — \"1.5\", \"abc\",\n * \"1e18\" — so a user typing into an amount field must not reach it. Only a\n * positive base-unit integer is a quotable amount.\n */\nexport function isPositiveIntegerAmount(amount: string): boolean {\n return /^(?:0|[1-9][0-9]*)$/.test(amount) && BigInt(amount) > 0n;\n}\n\n/** True when every field a quote needs is present and the amount is quotable. */\nexport function isQuotableInput(input: RouteQuoteInput): boolean {\n return Boolean(\n input.fromChain &&\n input.toChain &&\n input.fromToken &&\n isPositiveIntegerAmount(input.amount),\n );\n}\n\n// ── Cost comparison ───────────────────────────────────────────────\n\nexport interface CostComparison {\n chain: string;\n chainName: string;\n totalCost: string;\n gasCost: string;\n estimatedTimeMs: number;\n}\n\nexport type CostComparisonOperation =\n | \"send_erc20\"\n | \"swap\"\n | \"bridge\"\n | (string & {});\n\nexport interface CostComparisonOptions {\n amount?: string;\n token?: string;\n [key: string]: unknown;\n}\n\nexport interface CompareCostsInput {\n operation: CostComparisonOperation;\n chains: string[];\n options?: CostComparisonOptions;\n}\n\nexport type CompareCosts = (\n operation: string,\n chains: string[],\n options?: CostComparisonOptions,\n) => Promise<CostComparison[]>;\n\n/**\n * Value key for a compare-costs input. `chains` is an array and `options` an\n * object; keying on identity re-fired the fetch on every render/reactive tick\n * for a caller writing the natural inline literal — an unbounded loop against\n * a cost API.\n */\nexport function compareCostsKey(input: CompareCostsInput): string {\n const options = input.options;\n const optionsKey = options\n ? Object.keys(options)\n .sort()\n .map((key) => {\n const value = options[key];\n return `${key}=${\n typeof value === \"object\" && value !== null\n ? \"[object]\"\n : String(value)\n }`;\n })\n .join(\"&\")\n : \"\";\n return `${input.operation}|${(input.chains ?? []).join(\",\")}|${optionsKey}`;\n}\n\n// ── Execution ─────────────────────────────────────────────────────\n\nexport interface ExecutableQuote {\n routeId: string;\n provider: string;\n totalCost?: bigint;\n estimatedTimeMs?: number;\n toChain?: string;\n}\n\nexport interface ExecuteRouteResult {\n fromTxHash: string;\n toTxHash?: string;\n}\n\nexport interface ExecuteRouteOptions {\n timeoutMs?: number;\n gasLimit?: bigint;\n}\n\nexport interface ExecuteRouteError {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type ExecuteRoute = (\n quote: ExecutableQuote,\n recipient: string,\n options?: ExecuteRouteOptions,\n) => Promise<ExecuteRouteResult>;\n\n/**\n * Recipient check before a cross-chain execution. An empty recipient is wrong\n * on every chain. Beyond that the destination namespace decides: when the\n * quote names `toChain`, the address is validated for that namespace; when it\n * does not, this returns null and the executor decides, rather than rejecting\n * a valid non-EVM address on a guess.\n */\nexport function validateRouteRecipient(\n quote: ExecutableQuote | null | undefined,\n recipient: string,\n): ExecuteRouteError | null {\n if (typeof recipient !== \"string\" || recipient.trim() === \"\") {\n return {\n code: \"invalid_recipient\",\n message: \"A recipient address is required\",\n };\n }\n const namespace = quote?.toChain?.split(\":\")[0];\n if (namespace && !isValidAddress(recipient, namespace)) {\n return {\n code: \"invalid_recipient\",\n message: `\"${recipient}\" is not a valid ${namespace} address for the destination chain ${quote?.toChain}`,\n };\n }\n return null;\n}\n\n/** Normalise whatever an executor threw into the hook's error shape. */\nexport function toExecuteRouteError(cause: unknown): ExecuteRouteError {\n const error = cause as {\n code?: string;\n message?: string;\n details?: Record<string, unknown>;\n };\n return {\n code: error?.code ?? \"execution_failed\",\n message: error?.message ?? String(cause),\n details: error?.details,\n };\n}\n","/**\n * Framework-agnostic core of the connect provider.\n *\n * appkit's stated shape is \"write the Stencil components once, generate the\n * React and Vue wrappers\". In practice `react/` carries ~8.5k hand-written\n * lines against `vue/`'s ~280, and only two files in `react/src` contain JSX —\n * the rest is logic wearing React clothes. Web Components also cannot serve\n * React Native or Flutter, so the presentation layer was never going to be the\n * thing worth sharing.\n *\n * This module is the first piece moved out: the state machine and the pure\n * helpers around it, with no React import. Everything here is consumable from\n * Vue, Svelte, or a native binding as-is, and `web3-store.test.ts` asserts the\n * absence of a framework import so the boundary cannot rot back.\n */\nimport { logger } from \"@naculus/connect-core\";\nimport type { ConnectionStatus, Web3State } from \"./types\";\n\n// ── Async helpers ──────────────────────────────────────────────────────\n\nexport function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n label = \"Operation\",\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`${label} timed out after ${ms}ms`)),\n ms,\n );\n });\n return Promise.race([promise, timeout]).finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n}\n\nexport interface RetryOptions {\n maxRetries: number;\n baseDelay: number;\n label?: string;\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n options: RetryOptions,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= options.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n if (attempt < options.maxRetries) {\n const delay = options.baseDelay * 2 ** attempt;\n logger.warn(\n \"appkit/core\",\n `Retry attempt ${attempt + 1}/${options.maxRetries} after ${delay}ms`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n throw lastError;\n}\n\n// ── CAIP normalisation ─────────────────────────────────────────────────\n\n/** Convert EIP-1193 chainChanged values into the CAIP-2 form used by sessions. */\nexport function normalizeEip155ChainId(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n try {\n if (value.startsWith(\"eip155:\")) {\n const reference = value.slice(\"eip155:\".length);\n if (!/^\\d+$/.test(reference)) return undefined;\n return `eip155:${BigInt(reference).toString(10)}`;\n }\n if (/^0x[0-9a-f]+$/i.test(value) || /^\\d+$/.test(value)) {\n return `eip155:${BigInt(value).toString(10)}`;\n }\n } catch {\n // Ignore malformed wallet events rather than corrupting session state.\n }\n return undefined;\n}\n\nexport function toEip155Accounts(\n accounts: unknown[],\n chainId: string,\n): string[] {\n const reference = chainId.split(\":\")[1];\n if (!reference) return [];\n return accounts\n .filter(\n (account): account is string =>\n typeof account === \"string\" && account.length > 0,\n )\n .map((account) =>\n account.includes(\":\") ? (account.split(\":\").pop() as string) : account,\n )\n .map((account) => `eip155:${reference}:${account}`);\n}\n\n/**\n * Numeric EIP-155 chain id, or undefined for any other namespace.\n *\n * The previous implementation took the last CAIP segment and ran parseInt over\n * it, which stops at the first non-digit: `solana:5eykt4Us…` became 5 — an\n * EVM chain id — so asking for Solana mainnet's token list returned Goerli's,\n * and `xrpl:0` became chain 0. Anchor the whole reference instead.\n */\nexport function eip155ChainIdToNumber(chainId: string): number | undefined {\n const match = /^eip155:([1-9][0-9]*)$/.exec(chainId);\n if (!match) return undefined;\n const numeric = Number(match[1]);\n return Number.isSafeInteger(numeric) ? numeric : undefined;\n}\n\n// ── State machine ──────────────────────────────────────────────────────\n\nexport type Web3Action =\n | { type: \"SET_STATUS\"; payload: ConnectionStatus }\n | { type: \"SET_SESSION\"; payload: Web3State[\"session\"] }\n | { type: \"SET_ACCOUNTS\"; payload: string[] }\n | { type: \"SET_CHAIN\"; payload: string | null }\n | { type: \"SET_ERROR\"; payload: Error | null }\n | { type: \"RESET\" };\n\nexport const initialWeb3State: Web3State = {\n status: \"disconnected\",\n session: null,\n accounts: [],\n chainId: null,\n error: null,\n};\n\nexport function web3Reducer(state: Web3State, action: Web3Action): Web3State {\n switch (action.type) {\n case \"SET_STATUS\":\n return { ...state, status: action.payload };\n case \"SET_SESSION\":\n return { ...state, session: action.payload };\n case \"SET_ACCOUNTS\":\n return { ...state, accounts: action.payload };\n case \"SET_CHAIN\":\n return { ...state, chainId: action.payload };\n case \"SET_ERROR\":\n return { ...state, error: action.payload };\n case \"RESET\":\n return initialWeb3State;\n default:\n return state;\n }\n}\n"],"mappings":"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","names":["chainNumber","WalletError"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naculus/connect-appkit-core",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",