@usebutr/core 2.0.1 → 3.0.1

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["VALID_CHAIN_PLATFORMS","isChainPlatform","toCookieMap"],"sources":["../src/types/account.ts","../src/types/chains-by-platform.ts","../src/types/errors.ts","../src/types/platform.ts","../src/wallet-source.ts","../src/store/shadow-adapter.ts","../src/logger.ts","../src/storage/browser-storage-driver.ts","../src/storage/validation.ts","../src/storage/wallet-storage.ts","../src/store/connector-lifecycle.ts","../src/store/hydration.ts","../src/store/reducer.ts","../src/store/wallet-store-helpers.ts","../src/store/wallet-store.ts","../src/storage/cookie-storage-driver.ts","../src/storage/snapshot.ts","../src/group-by-platform.ts","../src/encoding/bytes.ts","../src/sign-in/sign-in-flow.ts","../src/wallet-equal.ts","../src/sanitize-icon.ts"],"sourcesContent":["import type { ChainBase } from \"./chain\";\n\ntype Account = {\n chain: ChainBase;\n id: string;\n walletAddress: string;\n};\n\n/**\n * The reducer compares accounts by the composite `<chain>:<address>`\n * id, so every adapter must build accounts here or reproduce that\n * format byte for byte.\n */\nconst buildAccount = (address: string, chain: ChainBase): Account => ({\n chain,\n id: `${chain.id}:${address}`,\n walletAddress: address,\n});\n\ntype Balance = {\n /** Token decimals (e.g. 9 for SOL, 18 for ETH) */\n decimals: number;\n /** Human-readable string, trimmed of trailing zeros */\n formatted: string;\n /** Token symbol (e.g. \"SOL\", \"ETH\") */\n symbol: string;\n /** Raw integer amount */\n value: bigint;\n};\n\nexport type { Account, Balance };\nexport { buildAccount };\n","import type { ChainBase } from \"./chain\";\nimport type { ChainPlatform } from \"./platform\";\n\n/**\n * Values come from the per-platform packages (`EVM_CHAINS_LIST`,\n * `SVM_CHAINS_LIST`, …); an empty list opts that platform out of the\n * consumer's chain UI.\n */\ntype ChainsByPlatform = Readonly<Record<ChainPlatform, ReadonlyArray<ChainBase>>>;\n\n/**\n * Naming only the platforms an app targets keeps the other packages'\n * chain registries out of its bundle. Apps that want all of them import\n * `CHAINS_BY_PLATFORM` from `@usebutr/wallets`.\n */\nconst buildChainsByPlatform = (partial: Partial<ChainsByPlatform>) =>\n ({\n bitcoin: partial.bitcoin ?? [],\n evm: partial.evm ?? [],\n polkadot: partial.polkadot ?? [],\n sui: partial.sui ?? [],\n svm: partial.svm ?? [],\n }) satisfies ChainsByPlatform;\n\nexport type { ChainsByPlatform };\nexport { buildChainsByPlatform };\n","/**\n * Wallet SDKs disagree on error shape (EIP-1193 codes, bare strings,\n * bespoke classes), so consumers branch on `kind` rather than regexing\n * messages. `cause` keeps the original value for the `Unknown` case.\n */\ntype ConnectionError =\n | { kind: \"UserRejected\"; message: string }\n | { kind: \"RequestPending\"; message: string }\n | { kind: \"WalletLocked\"; message: string }\n | { actualChain?: string; expectedChain?: string; kind: \"ChainMismatch\"; message: string }\n | { kind: \"NotConnected\"; message: string }\n | { kind: \"Timeout\"; message: string }\n | { cause?: ErrorCause; kind: \"Unknown\"; message: string };\n\ntype ConnectionErrorKind = ConnectionError[\"kind\"];\ntype ErrorCause = Error | string;\n\ntype CodedError = Error & { code: number | string };\n\nconst isCodedError = (error: Error): error is CodedError => \"code\" in error;\n\nconst toErrorCause = (value: ErrorCause): ErrorCause => value;\n\n/**\n * EIP-1193 codes: `4001` rejected, `-32002` pending, `4100`/`4900`/`4901`\n * unauthorized or disconnected. Message-substring matching is the last\n * resort for SDKs that ship no codes at all.\n */\nconst mapConnectionError = (raw: ErrorCause): ConnectionError => {\n if (raw instanceof Error) {\n const message = raw.message;\n const lower = message.toLowerCase();\n\n if (message === \"Connection timeout\") {\n return { kind: \"Timeout\", message };\n }\n if (message === \"Failed to get account\" || lower.includes(\"not connected\")) {\n return { kind: \"NotConnected\", message };\n }\n\n const code = isCodedError(raw) ? raw.code : undefined;\n if (code === 4001) {\n return { kind: \"UserRejected\", message };\n }\n if (code === -32_002) {\n return { kind: \"RequestPending\", message };\n }\n if (code === 4100 || code === 4900 || code === 4901) {\n return { kind: \"NotConnected\", message };\n }\n\n if (lower.includes(\"user rejected\") || lower.includes(\"user denied\")) {\n return { kind: \"UserRejected\", message };\n }\n if (lower.includes(\"locked\")) {\n return { kind: \"WalletLocked\", message };\n }\n if (lower.includes(\"chain\") && (lower.includes(\"mismatch\") || lower.includes(\"unsupported\"))) {\n return { kind: \"ChainMismatch\", message };\n }\n\n return { cause: raw, kind: \"Unknown\", message };\n }\n\n if (typeof raw === \"string\") {\n return { kind: \"Unknown\", message: raw };\n }\n\n return { cause: raw, kind: \"Unknown\", message: \"Connection failed\" };\n};\n\nexport type { CodedError, ConnectionError, ConnectionErrorKind, ErrorCause };\nexport { mapConnectionError, toErrorCause };\n","/**\n * Single runtime source of truth: `ChainPlatform` and the storage\n * validators' allowlists both derive from it. Omitting a platform here\n * silently stops its connections from persisting.\n */\nconst CHAIN_PLATFORMS = [\"evm\", \"svm\", \"sui\", \"bitcoin\", \"polkadot\"] as const;\n\ntype ChainPlatform = (typeof CHAIN_PLATFORMS)[number];\n\nexport type { ChainPlatform };\nexport { CHAIN_PLATFORMS };\n","import type { WalletAdapter } from \"./types\";\n\n/**\n * The discovery seam: implementable by third parties without depending\n * on `@usebutr/wallets`, which itself just composes the per-platform\n * sources into one.\n */\ntype WalletSource = {\n subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void;\n};\n\n/**\n * Takes the exact shape of `discoverEvmAdapters` and friends, so a\n * single-platform app keeps `@usebutr/wallets` out of its bundle.\n */\nconst createWalletSource = (\n subscribe: (onAdapter: (adapter: WalletAdapter) => void) => () => void,\n): WalletSource => ({ subscribe });\n\nexport type { WalletSource };\nexport { createWalletSource };\n","import type { StoredPoolEntry } from \"../storage/persistence\";\nimport type { CodedError, WalletAdapter, WalletCapabilities } from \"../types\";\n\n/**\n * Loud failure for consumers that called a wallet method without gating\n * on `capabilities.*` (all `false` here) or `reconnectingIds`, before\n * silent reconnect swapped in the live adapter.\n */\nclass ShadowConnectorError extends Error implements CodedError {\n readonly code = \"BUTR_RECONNECTING\";\n readonly connectorId: string;\n readonly method: string;\n constructor(method: string, connectorId: string) {\n super(\n `[butr] ${method} called on shadow connector \"${connectorId}\". Wait for silent reconnect to complete (check reconnectingIds.has(id) before calling).`,\n );\n this.name = \"ShadowConnectorError\";\n this.connectorId = connectorId;\n this.method = method;\n }\n}\n\nconst ALL_FALSE_CAPABILITIES: WalletCapabilities = Object.freeze({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: false,\n signIn: false,\n signMessage: false,\n signTransaction: false,\n subscribe: false,\n switchAccount: false,\n switchChain: false,\n});\n\n/**\n * Lets `createWalletStore` flip `isHydrated` synchronously from\n * `initialState`; the hydration coordinator later swaps each shadow for\n * a live adapter, or drops the entry from pool and storage.\n */\nconst createShadowAdapter = (entry: StoredPoolEntry): WalletAdapter => {\n const id = entry.connectorId;\n const name = entry.name;\n const icon = entry.icon;\n\n const reject = (method: string): Promise<never> =>\n Promise.reject(new ShadowConnectorError(method, id));\n\n const base = {\n capabilities: ALL_FALSE_CAPABILITIES,\n connect: () => reject(\"connect\"),\n disconnect: () => reject(\"disconnect\"),\n getAccount: () => reject(\"getAccount\"),\n getBalance: () => reject(\"getBalance\"),\n getSigner: () => reject(\"getSigner\"),\n getTransactionReceipt: () => reject(\"getTransactionReceipt\"),\n icon,\n id,\n name,\n sendTx: () => reject(\"sendTx\"),\n sendTxToChain: () => reject(\"sendTxToChain\"),\n signMessage: () => reject(\"signMessage\"),\n switchChain: () => reject(\"switchChain\"),\n };\n\n switch (entry.chainPlatform) {\n case \"bitcoin\": {\n return { ...base, chainPlatform: \"bitcoin\" };\n }\n case \"evm\": {\n return { ...base, chainPlatform: \"evm\" };\n }\n case \"sui\": {\n return { ...base, chainPlatform: \"sui\" };\n }\n case \"svm\": {\n return { ...base, chainPlatform: \"svm\" };\n }\n case \"polkadot\": {\n return { ...base, chainPlatform: \"polkadot\" };\n }\n default: {\n const _exhaustive: never = entry.chainPlatform;\n void _exhaustive;\n throw new Error(`[butr] unknown chainPlatform: ${String(entry.chainPlatform)}`);\n }\n }\n};\n\n/**\n * Structural detection: relies on every live adapter advertising at\n * least one capability, since `getBalance`, `signMessage` and\n * `switchChain` are required on all wallet surfaces.\n */\nconst isShadowAdapter = (adapter: WalletAdapter): boolean => {\n return Object.values(adapter.capabilities).every((flag) => !flag);\n};\n\nexport { createShadowAdapter, isShadowAdapter, ShadowConnectorError };\n","/* eslint-disable no-console -- central logging sink; the one sanctioned console use */\nconst logWarn = (...args: ReadonlyArray<unknown>): void => {\n console.warn(...args);\n};\n\nconst logError = (...args: ReadonlyArray<unknown>): void => {\n console.error(...args);\n};\n/* eslint-enable no-console */\n\nexport { logError, logWarn };\n","import type { StorageDriver } from \"./persistence\";\n\ntype BrowserStorageDrivers = {\n /** Survives app restart (localStorage on web, AsyncStorage/MMKV on RN). */\n persistent: StorageDriver;\n /** Cleared when the app session ends (sessionStorage on web, in-memory on RN). */\n session: StorageDriver;\n};\n\ntype WebStorageKind = \"localStorage\" | \"sessionStorage\";\n\nconst hasWebStorage = (kind: WebStorageKind): boolean => {\n try {\n return kind === \"localStorage\"\n ? globalThis.localStorage !== undefined\n : globalThis.sessionStorage !== undefined;\n } catch {\n return false;\n }\n};\n\nconst createMemoryStorageDriver = (): StorageDriver => {\n const memory = new Map<string, string>();\n return {\n getItem(key) {\n return memory.get(key) ?? null;\n },\n removeItem(key) {\n memory.delete(key);\n },\n setItem(key, value) {\n memory.set(key, value);\n },\n };\n};\n\nconst createWebStorageDriver = (kind: WebStorageKind): StorageDriver => {\n if (!hasWebStorage(kind)) {\n return createMemoryStorageDriver();\n }\n\n const storage = kind === \"localStorage\" ? globalThis.localStorage : globalThis.sessionStorage;\n\n return {\n getItem(key) {\n try {\n return storage.getItem(key);\n } catch {\n return null;\n }\n },\n removeItem(key) {\n try {\n storage.removeItem(key);\n } catch {\n void 0;\n }\n },\n setItem(key, value) {\n try {\n storage.setItem(key, value);\n } catch {\n void 0;\n }\n },\n };\n};\n\n/**\n * Default storage drivers for web. `persistent` wraps `localStorage`,\n * `session` wraps `sessionStorage`. Both fall back to in-memory maps\n * when the global isn't available (SSR, non-browser environments).\n */\nconst createBrowserStorageDriver = (): BrowserStorageDrivers => ({\n persistent: createWebStorageDriver(\"localStorage\"),\n session: createWebStorageDriver(\"sessionStorage\"),\n});\n\nexport type { BrowserStorageDrivers };\nexport { createBrowserStorageDriver, createMemoryStorageDriver };\n","import { z } from \"zod\";\n\nimport { logWarn } from \"../logger\";\nimport { CHAIN_PLATFORMS } from \"../types/platform\";\n\nimport type { StoredPoolEntry, StoredPoolRecord, StoredSelectionRecord } from \"./persistence\";\n\nconst chainPlatformSchema = z.enum(CHAIN_PLATFORMS);\n\nconst chainSchema = z.looseObject({\n id: z.string(),\n name: z.string(),\n namespace: z.string(),\n reference: z.string(),\n});\n\nconst accountSchema = z.looseObject({\n chain: chainSchema,\n id: z.string(),\n walletAddress: z.string(),\n});\n\nconst storedPoolEntrySchema = z.looseObject({\n account: accountSchema,\n accounts: z.array(accountSchema),\n chainPlatform: chainPlatformSchema,\n connectorId: z.string(),\n icon: z.string().optional(),\n name: z.string().min(1),\n});\n\nconst recordSchema = z.record(z.string(), z.unknown());\n\nconst parseStoredPoolEntry = (key: string, value: StoredPoolEntry) => {\n const parsed = storedPoolEntrySchema.safeParse(value);\n if (!parsed.success || parsed.data.connectorId !== key) {\n return null;\n }\n return parsed.data;\n};\n\nconst DEFAULT_KEY_PREFIX = \"butr\";\n\n/**\n * `WalletStorage` writes these and `readWalletSnapshot` reads them from\n * a cookie jar; any divergence desyncs the SSR-seeded render from the\n * client that rehydrates it, the failure ADR 0003 exists to prevent.\n */\nconst storageKeys = (keyPrefix?: string) => {\n const prefix = keyPrefix === undefined || keyPrefix === \"\" ? DEFAULT_KEY_PREFIX : keyPrefix;\n return {\n active: `${prefix}-active`,\n pool: `${prefix}-pool`,\n selection: `${prefix}-selection`,\n userDisconnected: `${prefix}-user-disconnected`,\n };\n};\n\n/**\n * Never repairs or throws: one corrupt entry must not take down a whole\n * session, and `readWalletSnapshot` may run on a server with no cookie\n * jar to write the eviction to.\n */\nconst decodePool = (raw: string | null | undefined, label = \"[butr]\"): StoredPoolRecord => {\n if (raw === null || raw === undefined || raw === \"\") {\n return {};\n }\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch (error) {\n logWarn(`${label} failed to parse pool from storage:`, error);\n return {};\n }\n const parsed = recordSchema.safeParse(value);\n if (!parsed.success) {\n return {};\n }\n const result: StoredPoolRecord = {};\n for (const [key, entryValue] of Object.entries(parsed.data)) {\n const candidate = storedPoolEntrySchema.safeParse(entryValue);\n const entry = candidate.success ? parseStoredPoolEntry(key, candidate.data) : null;\n if (entry === null) {\n logWarn(`${label} dropping invalid pool entry for ${key}`);\n } else {\n result[key] = entry;\n }\n }\n return result;\n};\n\n/** Decode a persisted selection payload. Same contract as `decodePool`. */\nconst decodeSelection = (\n raw: string | null | undefined,\n label = \"[butr]\",\n): StoredSelectionRecord => {\n if (raw === null || raw === undefined || raw === \"\") {\n return {};\n }\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch (error) {\n logWarn(`${label} failed to parse selection from storage:`, error);\n return {};\n }\n const parsed = recordSchema.safeParse(value);\n if (!parsed.success) {\n return {};\n }\n const result: StoredSelectionRecord = {};\n for (const [key, selectionValue] of Object.entries(parsed.data)) {\n const platform = chainPlatformSchema.safeParse(key);\n if (platform.success && typeof selectionValue === \"string\" && selectionValue.length > 0) {\n result[platform.data] = selectionValue;\n }\n }\n return result;\n};\n\nexport { decodePool, decodeSelection, parseStoredPoolEntry, storageKeys };\n","import { logWarn } from \"../logger\";\nimport type { ChainPlatform, ConnectedWallet } from \"../types\";\n\nimport { createBrowserStorageDriver } from \"./browser-storage-driver\";\nimport type {\n StorageDriver,\n StoredPoolEntry,\n StoredPoolRecord,\n StoredSelectionRecord,\n WalletPersistence,\n} from \"./persistence\";\nimport { decodePool, decodeSelection, parseStoredPoolEntry, storageKeys } from \"./validation\";\n\ntype StorageConfig = {\n /** Defaults to `\"butr\"`, matching `readWalletSnapshot`. */\n keyPrefix?: string;\n /** Survives app restart. Defaults to localStorage on web. */\n persistent?: StorageDriver;\n /** Cleared on session end. Defaults to sessionStorage on web. */\n session?: StorageDriver;\n};\n\nclass WalletStorage implements WalletPersistence {\n private readonly poolKey: string;\n private readonly selectionKey: string;\n private readonly activeKey: string;\n private readonly userDisconnectedKey: string;\n private readonly persistent: StorageDriver;\n private readonly session: StorageDriver;\n /**\n * Serializes pool read-modify-writes; without it concurrent `setPool`\n * calls drop each other's entries. Not reentrant, so a queued mutation\n * must read via `readPool`: `getPool` re-enters and deadlocks it.\n */\n private poolMutationQueue: Promise<unknown> = Promise.resolve();\n\n constructor(config: StorageConfig) {\n const keys = storageKeys(config.keyPrefix);\n this.poolKey = keys.pool;\n this.selectionKey = keys.selection;\n this.activeKey = keys.active;\n this.userDisconnectedKey = keys.userDisconnected;\n\n if (config.persistent && config.session) {\n this.persistent = config.persistent;\n this.session = config.session;\n } else {\n const defaults = createBrowserStorageDriver();\n this.persistent = config.persistent ?? defaults.persistent;\n this.session = config.session ?? defaults.session;\n }\n }\n\n /** Chain `fn` after the in-flight pool mutation. */\n private async serializePoolMutation<T>(fn: () => Promise<T>): Promise<T> {\n const previous = this.poolMutationQueue;\n // oxlint-disable-next-line unicorn/consistent-function-scoping -- assigned by Promise constructor below\n let resolve: () => void = () => {};\n this.poolMutationQueue = new Promise<void>((r) => {\n resolve = r;\n });\n try {\n await previous;\n } catch {\n // Previous mutation's failure shouldn't jam the queue; each\n // call site already observes its own rejection.\n }\n try {\n return await fn();\n } finally {\n resolve();\n }\n }\n\n /** Read and decode the pool without touching the mutation queue. Safe to\n * call from inside a queued mutation, unlike `getPool`. */\n private async readPool(): Promise<{ corrupt: boolean; decoded: StoredPoolRecord }> {\n let stored: string | null;\n try {\n stored = await this.persistent.getItem(this.poolKey);\n } catch (error) {\n logWarn(\"[butr] failed to read pool from storage:\", error);\n return { corrupt: false, decoded: {} };\n }\n if (stored === null || stored === \"\") {\n return { corrupt: false, decoded: {} };\n }\n const decoded = decodePool(stored);\n // An empty decode from a non-empty payload means the whole record was\n // unreadable, not that the user has no wallets.\n return { corrupt: Object.keys(decoded).length === 0, decoded };\n }\n\n async getPool(): Promise<StoredPoolRecord> {\n const { corrupt, decoded } = await this.readPool();\n if (corrupt) {\n // Evict the unreadable key directly. Going through `clearPool` would\n // acquire the mutation queue, which self-deadlocks when a queued\n // mutation is what triggered this read.\n try {\n await this.persistent.removeItem(this.poolKey);\n } catch (error) {\n logWarn(\"[butr] failed to clear corrupt pool:\", error);\n }\n }\n return decoded;\n }\n\n /**\n * Additive on purpose: a failed silent reconnect drops the entry from\n * the live pool, and it must survive to be retried next load.\n * Eviction goes through `removePoolEntry` or `clearAll`.\n */\n async setPool(pool: Map<string, ConnectedWallet>): Promise<void> {\n await this.serializePoolMutation(async () => {\n try {\n const { decoded: existing } = await this.readPool();\n const serializable: StoredPoolRecord = { ...existing };\n for (const [connectorId, wallet] of pool) {\n const entry: StoredPoolEntry = {\n account: wallet.account,\n accounts: wallet.accounts,\n chainPlatform: wallet.connector.chainPlatform,\n connectorId,\n icon: wallet.connector.icon,\n name: wallet.connector.name,\n };\n if (parseStoredPoolEntry(connectorId, entry) === null) {\n throw new Error(`[butr] refusing to persist invalid pool entry for ${connectorId}`);\n }\n serializable[connectorId] = entry;\n }\n await this.persistent.setItem(this.poolKey, JSON.stringify(serializable));\n } catch (error) {\n logWarn(\"[butr] failed to persist pool:\", error);\n }\n });\n }\n\n async removePoolEntry(connectorId: string): Promise<void> {\n await this.serializePoolMutation(async () => {\n try {\n const { decoded: stored } = await this.readPool();\n if (stored[connectorId]) {\n const { [connectorId]: _, ...remaining } = stored;\n await this.persistent.setItem(this.poolKey, JSON.stringify(remaining));\n }\n } catch (error) {\n logWarn(`[butr] failed to remove pool entry ${connectorId}:`, error);\n }\n });\n }\n\n async clearPool(): Promise<void> {\n await this.serializePoolMutation(async () => {\n await this.persistent.removeItem(this.poolKey);\n });\n }\n\n async getSelection(): Promise<StoredSelectionRecord> {\n try {\n return decodeSelection(await this.persistent.getItem(this.selectionKey));\n } catch (error) {\n logWarn(\"[butr] failed to read selection from storage:\", error);\n return {};\n }\n }\n\n async setSelection(selection: Map<ChainPlatform, string>): Promise<void> {\n try {\n const serializable: StoredSelectionRecord = {};\n for (const [platform, connectorId] of selection) {\n serializable[platform] = connectorId;\n }\n await this.persistent.setItem(this.selectionKey, JSON.stringify(serializable));\n } catch (error) {\n logWarn(\"[butr] failed to persist selection:\", error);\n }\n }\n\n async getActiveConnectorId(): Promise<string | null> {\n try {\n const value = await this.persistent.getItem(this.activeKey);\n return value !== null && value.length > 0 ? value : null;\n } catch {\n return null;\n }\n }\n\n async setActiveConnectorId(connectorId: string | null): Promise<void> {\n try {\n await (connectorId === null\n ? this.persistent.removeItem(this.activeKey)\n : this.persistent.setItem(this.activeKey, connectorId));\n } catch (error) {\n logWarn(\"[butr] failed to persist active connector id:\", error);\n }\n }\n\n async clearAll(): Promise<void> {\n await Promise.all([\n this.clearPool(),\n this.persistent.removeItem(this.selectionKey),\n this.persistent.removeItem(this.activeKey),\n ]);\n }\n\n /**\n * Kept in the session driver so it survives remounts (unlike a ref)\n * yet clears at session end (unlike the persistent driver): a manual\n * disconnect must suppress auto-connect now, not forever.\n */\n async isUserDisconnected(): Promise<boolean> {\n try {\n const value = await this.session.getItem(this.userDisconnectedKey);\n return value === \"true\";\n } catch {\n return false;\n }\n }\n\n async markUserDisconnected(value: boolean): Promise<void> {\n try {\n await (value\n ? this.session.setItem(this.userDisconnectedKey, \"true\")\n : this.session.removeItem(this.userDisconnectedKey));\n } catch {\n void 0;\n }\n }\n}\n\nexport { WalletStorage };\n","import { logWarn } from \"../logger\";\nimport type { Account, ChainPlatform, Connector } from \"../types\";\n\n/**\n * The bridge owns event-to-handler mapping and the unsubscribe handle;\n * the runtime owns the effects (dispatch, persist, consumer callbacks).\n */\ntype LifecycleHandlers = {\n /** Wallet exposed a new accounts list (or active account swap). The\n * bridge forwards the full array; single-account wallets included.\n * `active` is the address the wallet picked as current. */\n onAccountChanged: (\n connectorId: string,\n accounts: ReadonlyArray<Account>,\n active: Account,\n ) => void;\n /** Wallet disconnected externally (user locked, extension removed,\n * WC session ended on the relay). The bridge has already cleared\n * the subscription for `connectorId` by the time this fires. */\n onDisconnected: (connectorId: string, chainPlatform: ChainPlatform) => void;\n};\n\n/**\n * Owns the \"exactly one subscription per connector\" invariant for the\n * whole runtime; nothing else may call `connector.subscribe`.\n */\ntype ConnectorLifecycle = {\n /**\n * Idempotent: a second `attach` for the same id detaches the previous\n * subscription first. No-op for connectors without `subscribe`.\n */\n attach: (connectorId: string, connector: Connector) => void;\n /** Detach a single connector. Safe to call when no subscription is\n * registered. */\n detach: (connectorId: string) => void;\n /** Detach every active subscription. Used during `reset`. */\n detachAll: () => void;\n};\n\nconst createConnectorLifecycle = (handlers: LifecycleHandlers): ConnectorLifecycle => {\n const unsubscribers = new Map<string, () => void>();\n\n const detach = (connectorId: string) => {\n const unsub = unsubscribers.get(connectorId);\n if (!unsub) {\n return;\n }\n try {\n unsub();\n } catch (error: unknown) {\n logWarn(\"[butr] unsubscribe threw:\", error);\n }\n unsubscribers.delete(connectorId);\n };\n\n return {\n attach: (connectorId, connector) => {\n if (!connector.subscribe) {\n return;\n }\n detach(connectorId);\n try {\n const unsub = connector.subscribe((event) => {\n switch (event.type) {\n case \"accountChanged\": {\n handlers.onAccountChanged(connectorId, event.accounts, event.account);\n break;\n }\n case \"disconnected\": {\n detach(connectorId);\n handlers.onDisconnected(connectorId, connector.chainPlatform);\n break;\n }\n default: {\n const exhaustiveCheck: never = event;\n void exhaustiveCheck;\n }\n }\n });\n unsubscribers.set(connectorId, unsub);\n } catch (error: unknown) {\n logWarn(`[butr] subscribe failed for ${connectorId}:`, error);\n }\n },\n\n detach,\n\n detachAll: () => {\n const ids: Array<string> = [];\n for (const id of unsubscribers.keys()) {\n ids.push(id);\n }\n for (const id of ids) {\n detach(id);\n }\n },\n };\n};\n\nexport type { ConnectorLifecycle, LifecycleHandlers };\nexport { createConnectorLifecycle };\n","import type { StoredPoolEntry, WalletPersistence } from \"../storage\";\nimport type {\n Account,\n ChainPlatform,\n ConnectedWallet,\n Connector,\n WalletAdapter,\n WalletManagerConfig,\n} from \"../types\";\nimport type { ErrorCause } from \"../types/errors\";\nimport { toErrorCause } from \"../types/errors\";\nimport { CHAIN_PLATFORMS } from \"../types/platform\";\n\nconst VALID_CHAIN_PLATFORMS: ReadonlySet<string> = new Set(CHAIN_PLATFORMS);\n\nconst isChainPlatform = (value: string): value is ChainPlatform => VALID_CHAIN_PLATFORMS.has(value);\n\n/**\n * Restore outcome for a single pool entry. `kind: \"ok\"` carries the\n * fully-formed `ConnectedWallet`. `kind: \"fail\"` carries the raw error\n * so the caller can route it to telemetry.\n */\ntype RestoreOutcome =\n | { connectorId: string; entry: ConnectedWallet; kind: \"ok\" }\n | { connectorId: string; error: ErrorCause; kind: \"fail\" };\n\n/**\n * Snapshot returned by `HydrationCoordinator.hydrate()`; everything\n * the reducer needs to compose a `HYDRATED` event plus the metadata the\n * runtime uses to wire subscriptions and surface the outcome.\n */\ntype HydrateResult = {\n activeConnectorId: string | null;\n /** Entries whose restore failed (connect() rejected, the connector\n * threw mid-flight). Reported for telemetry only; storage is\n * preserved so the next load can retry. The most common cause is\n * EIP-1193 `eth_accounts: []` from a locked wallet, which clears as\n * soon as the user unlocks and reloads. */\n dropped: Array<{ connectorId: string; reason: ErrorCause }>;\n isUserDisconnected: boolean;\n /** Entries parked because `createConnector(id)` returned `null` at\n * hydration time (typical for Wallet Standard adapters announced\n * asynchronously). The coordinator keeps an internal record of these\n * and resolves them when the runtime later calls `drainPending`. */\n pendingIds: Array<string>;\n pool: Map<string, ConnectedWallet>;\n selection: Map<ChainPlatform, string>;\n};\n\ntype HydrationCoordinator = {\n /** Storage cleanup on a `fail` outcome is handled internally; the\n * caller only routes the error. */\n drainPending: (connectorId: string) => Promise<RestoreOutcome | null>;\n /** Entries whose adapter is not registered yet are parked, not\n * dropped; the caller must drain them via `drainPending` or those\n * wallets never reconnect. */\n hydrate: () => Promise<HydrateResult>;\n /** Read once right after `hydrate()` resolves, to catch adapters that\n * announced during the hydration window. */\n pendingIds: () => Array<string>;\n};\n\n/** Pick the right `accounts` list: fresh from the connector if it\n * exposes `getAccounts`, otherwise the persisted list, otherwise just\n * the active account. */\nconst resolveHydratedAccounts = async (\n connector: Connector,\n storedAccounts: ReadonlyArray<Account>,\n fallbackAccount: Account,\n): Promise<Array<Account>> => {\n if (connector.getAccounts) {\n const fresh = await connector.getAccounts().catch(() => null);\n if (fresh && fresh.length > 0) {\n return fresh;\n }\n }\n if (storedAccounts.length > 0) {\n return [...storedAccounts];\n }\n return [fallbackAccount];\n};\n\nconst restoreOneEntry = async (\n connectorId: string,\n entry: { account: Account; accounts: ReadonlyArray<Account> },\n connector: WalletAdapter,\n): Promise<RestoreOutcome> => {\n try {\n await connector.connect({ silent: true });\n const freshAccount = await connector.getAccount();\n const accountToUse = freshAccount ?? entry.account;\n const accounts = await resolveHydratedAccounts(connector, entry.accounts, accountToUse);\n return {\n connectorId,\n entry: {\n account: accountToUse,\n accounts,\n connector,\n },\n kind: \"ok\",\n };\n } catch (error) {\n return {\n connectorId,\n error: toErrorCause(error instanceof Error ? error : String(error)),\n kind: \"fail\",\n };\n }\n};\n\nconst createHydrationCoordinator = (\n storage: WalletPersistence,\n createConnector: WalletManagerConfig[\"createConnector\"],\n): HydrationCoordinator => {\n const pending = new Map<string, StoredPoolEntry>();\n\n return {\n drainPending: async (connectorId) => {\n const entry = pending.get(connectorId);\n if (!entry) {\n return null;\n }\n const connector = createConnector(connectorId);\n if (!connector) {\n return null;\n }\n const outcome = await restoreOneEntry(connectorId, entry, connector);\n pending.delete(connectorId);\n return outcome;\n },\n\n hydrate: async () => {\n const [storedPool, storedSelection, storedActive, userDisconnected] = await Promise.all([\n storage.getPool(),\n storage.getSelection(),\n storage.getActiveConnectorId(),\n storage.isUserDisconnected(),\n ]);\n\n const pool = new Map<string, ConnectedWallet>();\n const dropped: Array<{ connectorId: string; reason: ErrorCause }> = [];\n\n pending.clear();\n\n const tasks: Array<Promise<RestoreOutcome>> = [];\n for (const [connectorId, entry] of Object.entries(storedPool)) {\n if (!entry) {\n continue;\n }\n const connector = createConnector(connectorId);\n if (!connector) {\n pending.set(connectorId, entry);\n continue;\n }\n tasks.push(restoreOneEntry(connectorId, entry, connector));\n }\n\n const outcomes = await Promise.all(tasks);\n for (const outcome of outcomes) {\n if (outcome.kind === \"ok\") {\n pool.set(outcome.connectorId, outcome.entry);\n continue;\n }\n dropped.push({ connectorId: outcome.connectorId, reason: outcome.error });\n }\n\n const selection = new Map<ChainPlatform, string>();\n for (const [platform, connectorId] of Object.entries(storedSelection)) {\n if (isChainPlatform(platform) && connectorId !== undefined && pool.has(connectorId)) {\n selection.set(platform, connectorId);\n }\n }\n for (const [connectorId, wallet] of pool) {\n const platform = wallet.connector.chainPlatform;\n if (!selection.has(platform)) {\n selection.set(platform, connectorId);\n }\n }\n\n let activeConnectorId: string | null = null;\n if (storedActive !== null && pool.has(storedActive)) {\n activeConnectorId = storedActive;\n } else if (pool.size > 0) {\n activeConnectorId = pool.keys().next().value ?? null;\n }\n\n return {\n activeConnectorId,\n dropped,\n isUserDisconnected: userDisconnected,\n pendingIds: [...pending.keys()],\n pool,\n selection,\n };\n },\n\n pendingIds: () => [...pending.keys()],\n };\n};\n\nexport type { HydrateResult, HydrationCoordinator, RestoreOutcome };\nexport { createHydrationCoordinator };\n","import type { Account, ChainPlatform, ConnectedWallet } from \"../types\";\nimport type { ConnectionError } from \"../types/errors\";\n\n/**\n * The reducer never writes `\"reconnecting\"`; `useConnectionStatus`\n * derives it from `reconnectingIds` so the state machine stays narrow\n * while the public vocabulary matches wagmi's.\n */\ntype ConnectionStatus = \"idle\" | \"connecting\" | \"success\" | \"error\" | \"reconnecting\";\n\ntype State = {\n activeConnectorId: string | null;\n connectingConnectorId: string | null;\n connectionError: ConnectionError | null;\n connectionStatus: ConnectionStatus;\n isHydrated: boolean;\n isUserDisconnected: boolean;\n pool: Map<string, ConnectedWallet>;\n /**\n * Ids still backed by a shadow adapter from `initialState`. Empty\n * without `initialState`; shrinks on `HYDRATED` / `CONNECT_SUCCEEDED`\n * and on `DISCONNECTED` / `RESET`.\n */\n reconnectingIds: ReadonlySet<string>;\n selection: Map<ChainPlatform, string>;\n};\n\n/**\n * Lifecycle events: hydration, full reset, and the explicit\n * user-disconnected flag the runtime sets to suppress eager reconnect.\n */\ntype LifecycleEvent =\n | {\n activeConnectorId: string | null;\n /** Connector ids that were seeded from `initialState` but whose silent\n * reconnect failed. Their pool entries are still shadow-backed, so they\n * are evicted rather than left permanently unusable. */\n dropped: ReadonlyArray<string>;\n isUserDisconnected: boolean;\n pool: Map<string, ConnectedWallet>;\n selection: Map<ChainPlatform, string>;\n type: \"HYDRATED\";\n }\n | { type: \"RESET\" }\n | { type: \"USER_DISCONNECTED_SET\"; value: boolean };\n\n/**\n * None of these mutate the pool; `CONNECT_SUCCEEDED` is the only bridge\n * across to `PoolMutationEvent`.\n */\ntype ConnectionStatusEvent =\n | { connectorId: string; type: \"CONNECT_STARTED\" }\n | { connectorId: string; entry: ConnectedWallet; type: \"CONNECT_SUCCEEDED\" }\n | { connectorId: string; error: ConnectionError; type: \"CONNECT_FAILED\" }\n | { error: ConnectionError | null; type: \"ERROR_SET\" }\n | { type: \"STATUS_RESET\" };\n\n/**\n * Pool-mutation events: changes to the set of connected wallets and\n * their per-wallet account lists. `DISCONNECTED` removes an entry;\n * the refresh events update an existing one in-place.\n */\ntype PoolMutationEvent =\n | {\n /**\n * Deliberately not `CONNECT_SUCCEEDED`: discovery fires this at an\n * arbitrary time, so it must not overwrite the connect-attempt\n * status that the user's own action owns.\n */\n connectorId: string;\n entry: ConnectedWallet;\n type: \"ENTRY_RESTORED\";\n }\n | {\n /** When set, becomes the new active account. Used by paths that\n * know which account should be active (wallet event, manual\n * update). When unset, the reducer preserves the current active\n * if it's still in `accounts`, else picks `accounts[0]`. */\n accounts: Array<Account>;\n active?: Account;\n connectorId: string;\n type: \"ACCOUNTS_REFRESHED\";\n }\n | { connectorId: string; type: \"DISCONNECTED\" }\n | { connectorId: string; type: \"WALLET_REFRESHED\" };\n\n/**\n * Selection events: which connector is active, and which connector\n * serves each chain platform. Pure pointer updates; no pool mutation.\n */\ntype SelectionEvent =\n | { chainPlatform: ChainPlatform; connectorId: string | null; type: \"SELECTION_CHANGED\" }\n | { connectorId: string | null; type: \"ACTIVE_CHANGED\" };\n\n/**\n * The single event type the reducer dispatches on. Grouped above by\n * concern so a reader sees the map before the cases; adding an event\n * means picking the group it belongs to.\n */\ntype Event = ConnectionStatusEvent | LifecycleEvent | PoolMutationEvent | SelectionEvent;\n\nconst initialState: State = {\n activeConnectorId: null,\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"idle\",\n isHydrated: false,\n isUserDisconnected: false,\n pool: new Map(),\n reconnectingIds: new Set(),\n selection: new Map(),\n};\n\n/** Find any connector in the pool serving a given platform. */\nconst findConnectorForPlatform = (\n pool: Map<string, ConnectedWallet>,\n chainPlatform: ChainPlatform,\n): string | undefined => {\n for (const [id, wallet] of pool) {\n if (wallet.connector.chainPlatform === chainPlatform) {\n return id;\n }\n }\n return undefined;\n};\n\n/** Pure reducer: every state transition is expressed as `(state, event) => state`.\n * No I/O, no async, no side effects. Side effects live in the runtime. */\nconst reducer = (state: State, event: Event): State => {\n switch (event.type) {\n case \"HYDRATED\": {\n const pool = new Map<string, ConnectedWallet>(state.pool);\n for (const [id, entry] of event.pool) {\n pool.set(id, entry);\n }\n // A seeded entry whose reconnect failed is still backed by a shadow\n // adapter whose every method rejects. Leaving it in the pool would strand\n // it: nothing else ever clears it, so consumers would read a permanently\n // erroring wallet that reports itself as reconnecting forever.\n for (const id of event.dropped) {\n pool.delete(id);\n }\n const selection = new Map<ChainPlatform, string>(state.selection);\n for (const [platform, id] of event.selection) {\n selection.set(platform, id);\n }\n let activeConnectorId: string | null = null;\n if (event.activeConnectorId !== null && pool.has(event.activeConnectorId)) {\n activeConnectorId = event.activeConnectorId;\n } else if (state.activeConnectorId !== null && pool.has(state.activeConnectorId)) {\n activeConnectorId = state.activeConnectorId;\n } else if (pool.size > 0) {\n activeConnectorId = pool.keys().next().value ?? null;\n }\n let nextReconnecting: ReadonlySet<string> = state.reconnectingIds;\n if (state.reconnectingIds.size > 0) {\n const next = new Set(state.reconnectingIds);\n for (const id of event.pool.keys()) {\n next.delete(id);\n }\n for (const id of event.dropped) {\n next.delete(id);\n }\n if (next.size !== state.reconnectingIds.size) {\n nextReconnecting = next;\n }\n }\n const selectionAfterDrop = new Map(selection);\n for (const [platform, id] of selectionAfterDrop) {\n if (!pool.has(id)) {\n selectionAfterDrop.delete(platform);\n const fallback = findConnectorForPlatform(pool, platform);\n if (fallback !== undefined) {\n selectionAfterDrop.set(platform, fallback);\n }\n }\n }\n return {\n ...state,\n activeConnectorId,\n isHydrated: true,\n isUserDisconnected: event.isUserDisconnected,\n pool,\n reconnectingIds: nextReconnecting,\n selection: selectionAfterDrop,\n };\n }\n\n case \"USER_DISCONNECTED_SET\": {\n return { ...state, isUserDisconnected: event.value };\n }\n\n case \"CONNECT_STARTED\": {\n return {\n ...state,\n connectingConnectorId: event.connectorId,\n connectionError: null,\n connectionStatus: \"connecting\",\n };\n }\n\n case \"CONNECT_SUCCEEDED\": {\n const { connectorId, entry } = event;\n const existing = state.pool.get(connectorId);\n\n const nextReconnecting: ReadonlySet<string> = state.reconnectingIds.has(connectorId)\n ? new Set([...state.reconnectingIds].filter((id) => id !== connectorId))\n : state.reconnectingIds;\n\n if (\n existing &&\n existing.account.walletAddress.toLowerCase() === entry.account.walletAddress.toLowerCase()\n ) {\n return {\n ...state,\n activeConnectorId: connectorId,\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"success\",\n pool:\n existing.connector === entry.connector\n ? state.pool\n : new Map([...state.pool, [connectorId, entry] as const]),\n reconnectingIds: nextReconnecting,\n };\n }\n\n const newPool = new Map([...state.pool, [connectorId, entry] as const]);\n const newSelection = new Map([\n ...state.selection,\n [entry.connector.chainPlatform, connectorId] as const,\n ]);\n\n return {\n ...state,\n activeConnectorId: connectorId,\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"success\",\n pool: newPool,\n reconnectingIds: nextReconnecting,\n selection: newSelection,\n };\n }\n\n case \"ENTRY_RESTORED\": {\n const { connectorId, entry } = event;\n const nextReconnecting: ReadonlySet<string> = state.reconnectingIds.has(connectorId)\n ? new Set([...state.reconnectingIds].filter((id) => id !== connectorId))\n : state.reconnectingIds;\n\n const platform = entry.connector.chainPlatform;\n const selection = state.selection.has(platform)\n ? state.selection\n : new Map([...state.selection, [platform, connectorId] as const]);\n\n return {\n ...state,\n activeConnectorId: state.activeConnectorId ?? connectorId,\n pool: new Map([...state.pool, [connectorId, entry] as const]),\n reconnectingIds: nextReconnecting,\n selection,\n };\n }\n\n case \"CONNECT_FAILED\": {\n if (state.connectingConnectorId !== event.connectorId) {\n return state;\n }\n return {\n ...state,\n connectingConnectorId: null,\n connectionError: event.error,\n connectionStatus: \"error\",\n };\n }\n\n case \"DISCONNECTED\": {\n const wallet = state.pool.get(event.connectorId);\n if (!wallet) {\n return state;\n }\n\n const newPool = new Map(state.pool);\n newPool.delete(event.connectorId);\n\n const newSelection = new Map(state.selection);\n const platform = wallet.connector.chainPlatform;\n if (newSelection.get(platform) === event.connectorId) {\n newSelection.delete(platform);\n const fallback = findConnectorForPlatform(newPool, platform);\n if (fallback !== undefined) {\n newSelection.set(platform, fallback);\n }\n }\n\n const firstId = newPool.keys().next();\n const activeConnectorId =\n state.activeConnectorId === event.connectorId\n ? (firstId.value ?? null)\n : state.activeConnectorId;\n\n const nextReconnecting: ReadonlySet<string> = state.reconnectingIds.has(event.connectorId)\n ? new Set([...state.reconnectingIds].filter((id) => id !== event.connectorId))\n : state.reconnectingIds;\n\n return {\n ...state,\n activeConnectorId,\n pool: newPool,\n reconnectingIds: nextReconnecting,\n selection: newSelection,\n };\n }\n\n case \"ACCOUNTS_REFRESHED\": {\n const wallet = state.pool.get(event.connectorId);\n if (!wallet) {\n return state;\n }\n let nextAccount: Account;\n if (event.active) {\n nextAccount = event.active;\n } else {\n const stillHasCurrent = event.accounts.some(\n (a) =>\n a.walletAddress === wallet.account.walletAddress &&\n a.chain.id === wallet.account.chain.id,\n );\n nextAccount = stillHasCurrent ? wallet.account : (event.accounts[0] ?? wallet.account);\n }\n const sameAccount =\n nextAccount.walletAddress === wallet.account.walletAddress &&\n nextAccount.chain.id === wallet.account.chain.id;\n const hasSameAccounts =\n event.accounts.length === wallet.accounts.length &&\n event.accounts.every((a, i) => a.walletAddress === wallet.accounts[i]?.walletAddress);\n if (sameAccount && hasSameAccounts) {\n return state;\n }\n const updated: ConnectedWallet = {\n ...wallet,\n account: nextAccount,\n accounts: [...event.accounts],\n };\n const newPool = new Map([...state.pool, [event.connectorId, updated] as const]);\n return { ...state, pool: newPool };\n }\n\n case \"WALLET_REFRESHED\": {\n const wallet = state.pool.get(event.connectorId);\n if (!wallet) {\n return state;\n }\n const newPool = new Map([...state.pool, [event.connectorId, { ...wallet }] as const]);\n return { ...state, pool: newPool };\n }\n\n case \"ACTIVE_CHANGED\": {\n return { ...state, activeConnectorId: event.connectorId };\n }\n\n case \"SELECTION_CHANGED\": {\n const newSelection = new Map(state.selection);\n if (event.connectorId === null) {\n newSelection.delete(event.chainPlatform);\n } else {\n newSelection.set(event.chainPlatform, event.connectorId);\n }\n return { ...state, selection: newSelection };\n }\n\n case \"STATUS_RESET\": {\n return {\n ...state,\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"idle\",\n };\n }\n\n case \"ERROR_SET\": {\n return {\n ...state,\n connectionError: event.error,\n connectionStatus: event.error ? \"error\" : \"idle\",\n };\n }\n\n case \"RESET\": {\n return {\n ...initialState,\n isHydrated: state.isHydrated,\n isUserDisconnected: true,\n reconnectingIds: new Set(),\n };\n }\n\n default: {\n const exhaustiveCheck: never = event;\n void exhaustiveCheck;\n return state;\n }\n }\n};\n\nexport type {\n ConnectionStatus,\n ConnectionStatusEvent,\n Event,\n LifecycleEvent,\n PoolMutationEvent,\n SelectionEvent,\n State,\n};\nexport { initialState, reducer };\n","import type { ErrorCause } from \"../types/errors\";\nimport { toErrorCause } from \"../types/errors\";\n\n/** Run an async operation fire-and-forget, calling onError if it throws. */\nconst run = async (\n fn: () => Promise<void>,\n onError: (error: ErrorCause) => void,\n): Promise<void> => {\n try {\n await fn();\n } catch (error) {\n onError(toErrorCause(error instanceof Error ? error : String(error)));\n }\n};\n\nexport { run };\n","import { createStore } from \"zustand/vanilla\";\n\nimport { logError, logWarn } from \"../logger\";\nimport type { WalletPersistence, WalletSnapshot } from \"../storage\";\nimport { WalletStorage } from \"../storage/wallet-storage\";\nimport type {\n Account,\n ChainPlatform,\n ConnectedWallet,\n WalletAdapter,\n WalletManagerConfig,\n} from \"../types\";\nimport type { ConnectionError, ErrorCause } from \"../types/errors\";\nimport { mapConnectionError } from \"../types/errors\";\nimport { CHAIN_PLATFORMS } from \"../types/platform\";\n\nimport { createConnectorLifecycle } from \"./connector-lifecycle\";\nimport { createHydrationCoordinator } from \"./hydration\";\nimport { type Event, type State, initialState, reducer } from \"./reducer\";\nimport { createShadowAdapter } from \"./shadow-adapter\";\nimport { run } from \"./wallet-store-helpers\";\n\nconst VALID_CHAIN_PLATFORMS: ReadonlySet<string> = new Set(CHAIN_PLATFORMS);\n\nconst isChainPlatform = (value: string): value is ChainPlatform => VALID_CHAIN_PLATFORMS.has(value);\n\n/**\n * `isHydrated` is true from the first render on this path, so\n * `reconnectingIds` (not `isHydrated`) is the \"is this connection\n * verified\" signal until silent reconnect lands.\n */\nconst seedStateFromSnapshot = (snapshot: WalletSnapshot): State => {\n const pool = new Map<string, ConnectedWallet>();\n const reconnectingIds = new Set<string>();\n for (const [connectorId, entry] of Object.entries(snapshot.pool)) {\n if (!entry) {\n continue;\n }\n const connector: WalletAdapter = createShadowAdapter(entry);\n pool.set(connectorId, {\n account: entry.account,\n accounts: [...entry.accounts],\n connector,\n });\n reconnectingIds.add(connectorId);\n }\n const selection = new Map<ChainPlatform, string>();\n for (const [platform, connectorId] of Object.entries(snapshot.selection)) {\n if (isChainPlatform(platform) && connectorId !== undefined && pool.has(connectorId)) {\n selection.set(platform, connectorId);\n }\n }\n const activeConnectorId =\n snapshot.activeConnectorId !== null && pool.has(snapshot.activeConnectorId)\n ? snapshot.activeConnectorId\n : (pool.keys().next().value ?? null);\n return {\n ...initialState,\n activeConnectorId,\n isHydrated: true,\n pool,\n reconnectingIds,\n selection,\n };\n};\n\nconst CONNECT_TIMEOUT_MS = 90_000;\nconst DEFAULT_SLOW_CONNECT_THRESHOLD_MS = 5000;\n\nconst normaliseAddress = (addr: string): string => addr.toLowerCase();\n\ntype ExtractState<S> = S extends { getState: () => infer T } ? T : never;\n\ntype RuntimeMembers = {\n _config: WalletManagerConfig;\n _storage: WalletPersistence;\n connectWallet: (\n connectorId: string,\n onSuccess?: (wallet: ConnectedWallet) => void,\n onError?: (error: Error) => void,\n ) => Promise<void>;\n disconnectWallet: (connectorId: string) => void;\n getConnectorInstance: (id: string) => ReturnType<WalletManagerConfig[\"createConnector\"]>;\n hydrateWallets: () => Promise<void>;\n refreshWallet: (connectorId: string) => void;\n requestAccounts: (connectorId: string) => Promise<void>;\n reset: () => void;\n resetConnectionStatus: () => void;\n setActiveConnector: (connectorId: string | null) => void;\n setConnectionError: (error: ConnectionError | null) => void;\n setSelection: (chainPlatform: ChainPlatform, connectorId: string | null) => void;\n setUserDisconnected: (value: boolean) => void;\n tryRestoreFromPending: (connectorId: string) => Promise<void>;\n updateWalletAccount: (connectorId: string, account: Account) => void;\n};\n\ntype WalletStore = ReturnType<typeof createWalletStore>;\ntype WalletStoreState = ExtractState<WalletStore>;\n\nconst createWalletStore = (config: WalletManagerConfig) => {\n const storage = config.storage ?? new WalletStorage({ keyPrefix: config.storageKeyPrefix });\n\n const reportStorageError = (context: string) => (error: ErrorCause) => {\n if (config.onStorageError) {\n try {\n config.onStorageError(error, context);\n } catch (cbError) {\n logWarn(\"[butr] onStorageError threw:\", cbError);\n }\n return;\n }\n logWarn(`[butr] ${context}:`, error);\n };\n\n const hydration = createHydrationCoordinator(storage, config.createConnector);\n\n return createStore<State & RuntimeMembers>()((set, get) => {\n const dispatch = (event: Event) => {\n set((prev) => reducer(prev, event), false);\n };\n\n const persistPool = (): Promise<void> =>\n run(() => storage.setPool(get().pool), reportStorageError(\"failed to persist pool\"));\n\n const persistSelection = (): Promise<void> =>\n run(\n () => storage.setSelection(get().selection),\n reportStorageError(\"failed to persist selection\"),\n );\n\n const persistActive = (): Promise<void> =>\n run(\n () => storage.setActiveConnectorId(get().activeConnectorId),\n reportStorageError(\"failed to persist active connector\"),\n );\n\n const refreshPoolEntry = (connectorId: string, accounts: Array<Account>, active?: Account) => {\n dispatch({ accounts, active, connectorId, type: \"ACCOUNTS_REFRESHED\" });\n void persistPool();\n };\n\n const lifecycle = createConnectorLifecycle({\n onAccountChanged: (connectorId, accounts, active) => {\n refreshPoolEntry(connectorId, [...accounts], active);\n },\n onDisconnected: (connectorId, chainPlatform) => {\n // Tear the connector down as well as dropping it from the pool. The\n // adapter instance is cached by discovery and handed back on the next\n // connect, so an adapter that reported itself disconnected while\n // holding a live session would be reused in that state.\n const wallet = get().pool.get(connectorId);\n if (wallet) {\n void run(() => wallet.connector.disconnect?.() ?? Promise.resolve(), logError);\n }\n dispatch({ connectorId, type: \"DISCONNECTED\" });\n config.onDisconnect?.(chainPlatform);\n },\n });\n\n const seeded: State = config.initialState\n ? seedStateFromSnapshot(config.initialState)\n : initialState;\n\n return {\n ...seeded,\n _config: config,\n _storage: storage,\n connectWallet: async (connectorId, onSuccess, onError) => {\n get().setUserDisconnected(false);\n\n const connector = config.createConnector(connectorId);\n if (!connector) {\n throw new Error(`Failed to create connector for ${connectorId}`);\n }\n\n dispatch({ connectorId, type: \"CONNECT_STARTED\" });\n\n const slowThreshold = config.slowConnectThresholdMs ?? DEFAULT_SLOW_CONNECT_THRESHOLD_MS;\n const slowTimer = config.onSlowConnect\n ? setTimeout(() => {\n try {\n config.onSlowConnect?.(connectorId);\n } catch (cbError) {\n logWarn(\"[butr] onSlowConnect threw:\", cbError);\n }\n }, slowThreshold)\n : null;\n\n let connectTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n const connectPromise = connector.connect();\n // oxlint-disable-next-line promise/prefer-await-to-then -- suppress unhandled rejection; we await via Promise.race below\n void connectPromise.catch(() => {});\n await Promise.race([\n connectPromise,\n new Promise((_, reject) => {\n connectTimeoutId = setTimeout(() => {\n reject(new Error(\"Connection timeout\"));\n }, CONNECT_TIMEOUT_MS);\n }),\n ]);\n\n const account = await connector.getAccount();\n if (!account) {\n throw new Error(\"Failed to get account\");\n }\n\n const fetchedAccounts = connector.getAccounts\n ? await connector.getAccounts().catch(() => null)\n : null;\n const accounts =\n fetchedAccounts && fetchedAccounts.length > 0 ? fetchedAccounts : [account];\n\n const entry: ConnectedWallet = { account, accounts, connector };\n dispatch({ connectorId, entry, type: \"CONNECT_SUCCEEDED\" });\n\n lifecycle.attach(connectorId, connector);\n\n await Promise.all([persistPool(), persistSelection(), persistActive()]);\n\n config.onConnect?.(entry);\n onSuccess?.(entry);\n } catch (error) {\n const normalised = mapConnectionError(error instanceof Error ? error : String(error));\n dispatch({ connectorId, error: normalised, type: \"CONNECT_FAILED\" });\n try {\n await connector.disconnect?.();\n } catch (disconnectError) {\n logWarn(\"[butr] disconnect during error recovery failed:\", disconnectError);\n }\n try {\n config.onConnectError?.(normalised, connectorId);\n } catch (cbError) {\n logWarn(\"[butr] onConnectError threw:\", cbError);\n }\n onError?.(error instanceof Error ? error : new Error(String(error)));\n throw error;\n } finally {\n clearTimeout(connectTimeoutId);\n if (slowTimer) {\n clearTimeout(slowTimer);\n }\n }\n },\n\n disconnectWallet: (connectorId) => {\n get().setUserDisconnected(true);\n\n const state = get();\n if (!state.isHydrated) {\n return;\n }\n const wallet = state.pool.get(connectorId);\n if (!wallet) {\n return;\n }\n\n lifecycle.detach(connectorId);\n\n void run(() => wallet.connector.disconnect?.() ?? Promise.resolve(), logError);\n\n const platform = wallet.connector.chainPlatform;\n dispatch({ connectorId, type: \"DISCONNECTED\" });\n\n if (get().pool.size === 0) {\n void run(() => storage.clearAll(), reportStorageError(\"failed to clear storage\"));\n } else {\n void run(\n () => storage.removePoolEntry(connectorId),\n reportStorageError(\"failed to remove pool entry\"),\n );\n void persistSelection();\n void persistActive();\n }\n\n config.onDisconnect?.(platform);\n },\n\n getConnectorInstance: (id) => config.createConnector(id),\n\n hydrateWallets: async () => {\n const result = await hydration.hydrate();\n dispatch({\n activeConnectorId: result.activeConnectorId,\n dropped: result.dropped.map((d) => d.connectorId),\n isUserDisconnected: result.isUserDisconnected,\n pool: result.pool,\n selection: result.selection,\n type: \"HYDRATED\",\n });\n for (const [connectorId, wallet] of result.pool) {\n lifecycle.attach(connectorId, wallet.connector);\n }\n await Promise.all([persistSelection(), persistActive()]);\n for (const id of hydration.pendingIds()) {\n void run(\n () => get().tryRestoreFromPending(id),\n (e) => {\n logWarn(`[butr] late restore rejected for ${id}:`, e);\n },\n );\n }\n if (config.onHydrated) {\n try {\n config.onHydrated({\n dropped: result.dropped,\n pendingIds: [...result.pendingIds],\n restoredIds: [...result.pool.keys()],\n });\n } catch (cbError) {\n logWarn(\"[butr] onHydrated threw:\", cbError);\n }\n } else if (result.dropped.length > 0) {\n for (const { connectorId, reason } of result.dropped) {\n logWarn(`[butr] failed to restore connector ${connectorId}:`, reason);\n }\n }\n },\n refreshWallet: (connectorId) => {\n dispatch({ connectorId, type: \"WALLET_REFRESHED\" });\n },\n\n requestAccounts: async (connectorId) => {\n const entry = get().pool.get(connectorId);\n if (!entry) {\n return;\n }\n await entry.connector.requestAccounts?.();\n const fresh = entry.connector.getAccounts\n ? await entry.connector.getAccounts().catch(() => null)\n : null;\n if (!fresh || fresh.length === 0) {\n return;\n }\n refreshPoolEntry(connectorId, fresh);\n },\n\n reset: () => {\n get().setUserDisconnected(true);\n\n const state = get();\n if (!state.isHydrated) {\n return;\n }\n\n lifecycle.detachAll();\n\n for (const wallet of state.pool.values()) {\n void run(() => wallet.connector.disconnect?.() ?? Promise.resolve(), logError);\n }\n\n void run(() => storage.clearAll(), reportStorageError(\"failed to clear storage\"));\n\n if (config.onReset) {\n const onReset = config.onReset;\n void run(() => Promise.resolve(onReset()), logError);\n }\n\n dispatch({ type: \"RESET\" });\n },\n\n resetConnectionStatus: () => {\n dispatch({ type: \"STATUS_RESET\" });\n },\n\n setActiveConnector: (connectorId) => {\n dispatch({ connectorId, type: \"ACTIVE_CHANGED\" });\n void persistActive();\n },\n\n setConnectionError: (error) => {\n dispatch({ error, type: \"ERROR_SET\" });\n },\n\n setSelection: (chainPlatform, connectorId) => {\n dispatch({ chainPlatform, connectorId, type: \"SELECTION_CHANGED\" });\n void persistSelection();\n },\n\n setUserDisconnected: (value: boolean) => {\n dispatch({ type: \"USER_DISCONNECTED_SET\", value });\n void run(\n () => storage.markUserDisconnected(value),\n reportStorageError(\"failed to persist disconnect intent\"),\n );\n },\n\n tryRestoreFromPending: async (connectorId) => {\n const outcome = await hydration.drainPending(connectorId);\n if (!outcome) {\n return;\n }\n if (outcome.kind === \"fail\") {\n logWarn(`[butr] late restore failed for ${connectorId}:`, outcome.error);\n return;\n }\n dispatch({ connectorId, entry: outcome.entry, type: \"ENTRY_RESTORED\" });\n lifecycle.attach(connectorId, outcome.entry.connector);\n await Promise.all([persistPool(), persistSelection(), persistActive()]);\n config.onConnect?.(outcome.entry);\n },\n\n updateWalletAccount: (connectorId, account) => {\n const entry = get().pool.get(connectorId);\n if (!entry) {\n return;\n }\n const newChain = account.chain;\n const remapped = entry.accounts.map((a) =>\n a.chain.id === newChain.id\n ? a\n : {\n chain: newChain,\n id: `${newChain.id}:${normaliseAddress(a.walletAddress)}`,\n walletAddress: a.walletAddress,\n },\n );\n const seen = remapped.some(\n (a) => normaliseAddress(a.walletAddress) === normaliseAddress(account.walletAddress),\n );\n const accounts = seen ? remapped : [...remapped, account];\n refreshPoolEntry(connectorId, accounts, account);\n },\n };\n });\n};\n\nexport type { WalletStore, WalletStoreState };\nexport { createWalletStore };\n","import type { StorageDriver } from \"./persistence\";\n\ntype InitialCookies = Iterable<[string, string]> | Readonly<Record<string, string>>;\n\ntype CookieDriverOptions = {\n /**\n * Cookie `domain` attribute. Omit to scope to the current host.\n * Set to a parent domain (e.g. `.example.com`) to share state\n * across subdomains.\n */\n domain?: string;\n /**\n * Server-side cookie snapshot (Next.js' `cookies()`, a parsed\n * `req.headers.cookie`, …) read only when `document` is unavailable,\n * so the SSR pass sees the state the client will hydrate with.\n */\n initialCookies?: InitialCookies;\n /**\n * Lifetime in seconds. Defaults to 30 days.\n */\n maxAgeSeconds?: number;\n /**\n * URL path the cookie applies to. Defaults to `/` (whole origin).\n */\n path?: string;\n /**\n * `SameSite` attribute. Defaults to `\"lax\"`; restrictive enough\n * to avoid CSRF on cross-origin POSTs, permissive enough for\n * top-level navigations.\n */\n sameSite?: \"lax\" | \"strict\" | \"none\";\n /**\n * `Secure` attribute. Defaults to `true` (cookies only sent over\n * HTTPS). Force `false` for local development on plain `http://`.\n */\n secure?: boolean;\n};\n\nconst DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;\n\nconst encode = (value: string): string => encodeURIComponent(value);\nconst decode = (value: string): string => decodeURIComponent(value);\n\nconst readCookies = (): Map<string, string> => {\n const out = new Map<string, string>();\n if (typeof document === \"undefined\") {\n return out;\n }\n const cookieString = document.cookie;\n if (!cookieString) {\n return out;\n }\n for (const segment of cookieString.split(\";\")) {\n const trimmed = segment.trim();\n if (!trimmed) {\n continue;\n }\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) {\n continue;\n }\n const name = trimmed.slice(0, eq);\n const raw = trimmed.slice(eq + 1);\n try {\n out.set(name, decode(raw));\n } catch {\n void 0;\n }\n }\n return out;\n};\n\nconst buildCookieString = (\n name: string,\n value: string,\n options: CookieDriverOptions,\n expired = false,\n): string => {\n const parts: Array<string> = [`${name}=${encode(value)}`, `Path=${options.path ?? \"/\"}`];\n if (options.domain !== undefined && options.domain !== \"\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (expired) {\n parts.push(\"Max-Age=0\");\n } else {\n const maxAge = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;\n parts.push(`Max-Age=${maxAge}`);\n }\n parts.push(`SameSite=${options.sameSite ?? \"lax\"}`);\n if (options.secure ?? true) {\n parts.push(\"Secure\");\n }\n return parts.join(\"; \");\n};\n\nconst toCookieMap = (input: InitialCookies | undefined): Map<string, string> | null => {\n if (!input) {\n return null;\n }\n if (Symbol.iterator in input) {\n return new Map(input);\n }\n return new Map(Object.entries(input));\n};\n\n/**\n * Server-side writes are no-ops: `Set-Cookie` needs the framework's\n * response object. Cookies ride every request, so use this driver for\n * the `persistent` slot only and leave `session` on `sessionStorage`.\n */\nconst createCookieStorageDriver = (options: CookieDriverOptions = {}): StorageDriver => {\n const seeded = toCookieMap(options.initialCookies);\n\n if (typeof document === \"undefined\") {\n if (!seeded) {\n return {\n getItem() {\n return null;\n },\n removeItem() {},\n setItem() {},\n };\n }\n return {\n getItem(key) {\n return seeded.get(key) ?? null;\n },\n removeItem() {},\n setItem() {},\n };\n }\n return {\n getItem(key) {\n return readCookies().get(key) ?? null;\n },\n removeItem(key) {\n // eslint-disable-next-line unicorn/no-document-cookie -- cookie storage driver is the document.cookie abstraction by design\n document.cookie = buildCookieString(key, \"\", options, true);\n },\n setItem(key, value) {\n // eslint-disable-next-line unicorn/no-document-cookie -- cookie storage driver is the document.cookie abstraction by design\n document.cookie = buildCookieString(key, value, options);\n },\n };\n};\n\nexport type { CookieDriverOptions, InitialCookies };\nexport { createCookieStorageDriver };\n","import type { StoredPoolRecord, StoredSelectionRecord } from \"./persistence\";\nimport { decodePool, decodeSelection, storageKeys } from \"./validation\";\n\n/**\n * Carries no `Connector` by design: a wallet extension exists only in\n * the browser, so a server render can name the wallet and its address\n * but can never dispatch on it.\n */\ntype WalletSnapshot = {\n activeConnectorId: string | null;\n pool: StoredPoolRecord;\n selection: StoredSelectionRecord;\n};\n\ntype CookieSource =\n | Iterable<{ name: string; value: string }>\n | Iterable<[string, string]>\n | Readonly<Record<string, string>>;\n\ntype SnapshotOptions = {\n /**\n * Same prefix passed to `WalletManagerProvider` / `WalletStorage`.\n * Defaults to `\"butr\"` to match the library default.\n */\n keyPrefix?: string;\n};\n\nconst EMPTY_POOL: StoredPoolRecord = Object.freeze({});\nconst EMPTY_SELECTION: StoredSelectionRecord = Object.freeze({});\n\nconst EMPTY_SNAPSHOT: WalletSnapshot = Object.freeze({\n activeConnectorId: null,\n pool: EMPTY_POOL,\n selection: EMPTY_SELECTION,\n});\n\nconst toCookieMap = (input: CookieSource): Map<string, string> => {\n if (!(Symbol.iterator in input)) {\n return new Map(Object.entries(input));\n }\n const out = new Map<string, string>();\n for (const entry of input) {\n if (Array.isArray(entry)) {\n const [name, value] = entry;\n if (typeof name === \"string\" && typeof value === \"string\") {\n out.set(name, value);\n }\n continue;\n }\n if (typeof entry.name === \"string\" && typeof entry.value === \"string\") {\n out.set(entry.name, entry.value);\n }\n }\n return out;\n};\n\nconst SNAPSHOT_LABEL = \"[butr] readWalletSnapshot:\";\n\n/**\n * Optimistic: only what the browser last persisted, so an uninstall or\n * other-tab disconnect makes it stale. Authoritative once the entry\n * leaves `reconnectingIds`; `isHydrated` is true from render one.\n */\nconst readWalletSnapshot = (\n source: CookieSource,\n options: SnapshotOptions = {},\n): WalletSnapshot => {\n const keys = storageKeys(options.keyPrefix);\n const cookies = toCookieMap(source);\n\n const pool = decodePool(cookies.get(keys.pool), SNAPSHOT_LABEL);\n const selection = decodeSelection(cookies.get(keys.selection), SNAPSHOT_LABEL);\n\n const rawActive = cookies.get(keys.active);\n let activeConnectorId: string | null = null;\n if (rawActive !== undefined && rawActive.length > 0 && pool[rawActive] !== undefined) {\n activeConnectorId = rawActive;\n } else {\n const firstKey = Object.keys(pool)[0];\n if (firstKey) {\n activeConnectorId = firstKey;\n }\n }\n\n return { activeConnectorId, pool, selection };\n};\n\nexport type { CookieSource, SnapshotOptions, WalletSnapshot };\nexport { EMPTY_SNAPSHOT, readWalletSnapshot };\n","import type { ChainPlatform } from \"./types/platform\";\nimport { CHAIN_PLATFORMS } from \"./types/platform\";\n\n/**\n * A multi-chain wallet announces one adapter per platform, so brands\n * repeat in the flat list. Keys follow `CHAIN_PLATFORMS` order and empty\n * platforms are omitted, so `[...groups]` needs no emptiness filter.\n */\nconst groupByPlatform = <T>(\n items: ReadonlyArray<T>,\n getPlatform: (item: T) => ChainPlatform,\n): Map<ChainPlatform, Array<T>> => {\n const buckets = new Map<ChainPlatform, Array<T>>();\n for (const item of items) {\n const platform = getPlatform(item);\n const bucket = buckets.get(platform);\n if (bucket === undefined) {\n buckets.set(platform, [item]);\n } else {\n bucket.push(item);\n }\n }\n\n const ordered = new Map<ChainPlatform, Array<T>>();\n for (const platform of CHAIN_PLATFORMS) {\n const bucket = buckets.get(platform);\n if (bucket !== undefined) {\n ordered.set(platform, bucket);\n }\n }\n return ordered;\n};\n\nexport { groupByPlatform };\n","/**\n * Hex prefixing diverges by chain (bare for Bitcoin and Ledger, `0x` for\n * EVM and Polkadot), so the variants stay explicit: these sit on the\n * signing path, where a silent mismatch corrupts one chain's signatures.\n */\n\n/** Bitcoin/Solana alphabet; omits the visually ambiguous `0`, `O`, `I`, `l`. */\nconst BASE58_ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\nconst BASE58_INDEX = new Map<string, number>();\nfor (let i = 0; i < BASE58_ALPHABET.length; i += 1) {\n BASE58_INDEX.set(BASE58_ALPHABET[i] ?? \"\", i);\n}\n\n/** Bare lowercase hex, no `0x` prefix (Bitcoin, Ledger). */\nconst bytesToHex = (bytes: Uint8Array): string => {\n let hex = \"\";\n for (const byte of bytes) {\n hex += byte.toString(16).padStart(2, \"0\");\n }\n return hex;\n};\n\n/** `0x`-prefixed lowercase hex (EVM, Polkadot). */\nconst bytesToHexPrefixed = (bytes: Uint8Array): string => `0x${bytesToHex(bytes)}`;\n\n/**\n * Decode hex (with or without a leading `0x`) into raw bytes. Throws on\n * odd-length input or non-hex characters rather than silently producing\n * `NaN`-filled bytes.\n */\nconst hexToBytes = (hex: string): Uint8Array => {\n const clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex;\n if (clean.length % 2 !== 0) {\n throw new TypeError(\"Invalid hex: odd length\");\n }\n const out = new Uint8Array(clean.length / 2);\n for (let i = 0; i < out.length; i += 1) {\n const byte = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n if (Number.isNaN(byte)) {\n throw new TypeError(`Invalid hex character at offset ${i * 2}`);\n }\n out[i] = byte;\n }\n return out;\n};\n\n/**\n * Cross-platform base64 → `Uint8Array`. `atob` is available everywhere\n * butr runs (browsers, RN since Hermes, Node 16+, Bun, Deno).\n */\nconst base64ToBytes = (b64: string): Uint8Array => {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n out[i] = binary.codePointAt(i) ?? 0;\n }\n return out;\n};\n\n/** Cross-platform `Uint8Array` → base64. */\nconst bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary);\n};\n\n/**\n * `Uint8Array` → base58 (Solana addresses and signatures). Leading zero\n * bytes are significant in base58 and survive the BigInt round-trip only\n * because they're re-prefixed as `1`s afterwards.\n */\nconst bytesToBase58 = (bytes: Uint8Array): string => {\n let intVal = 0n;\n for (const byte of bytes) {\n intVal = (intVal << 8n) | BigInt(byte);\n }\n let out = \"\";\n while (intVal > 0n) {\n const remainder = intVal % 58n;\n intVal /= 58n;\n out = BASE58_ALPHABET[Number(remainder)] + out;\n }\n for (const byte of bytes) {\n if (byte !== 0) {\n break;\n }\n out = `1${out}`;\n }\n return out;\n};\n\n/** Decode base58 into raw bytes. Throws on characters outside the alphabet\n * rather than silently dropping them. */\nconst base58ToBytes = (input: string): Uint8Array => {\n let intVal = 0n;\n let leadingZeros = 0;\n let stillLeading = true;\n for (const char of input) {\n if (stillLeading && char === \"1\") {\n leadingZeros += 1;\n } else {\n stillLeading = false;\n }\n const digit = BASE58_INDEX.get(char);\n if (digit === undefined) {\n throw new Error(`Invalid base58 character \"${char}\"`);\n }\n intVal = intVal * 58n + BigInt(digit);\n }\n const bytes: Array<number> = [];\n const byteMask = 255n;\n while (intVal > 0n) {\n bytes.unshift(Number(intVal & byteMask));\n intVal >>= 8n;\n }\n for (let i = 0; i < leadingZeros; i += 1) {\n bytes.unshift(0);\n }\n return Uint8Array.from(bytes);\n};\n\nexport {\n base58ToBytes,\n base64ToBytes,\n bytesToBase58,\n bytesToBase64,\n bytesToHex,\n bytesToHexPrefixed,\n hexToBytes,\n};\n","import { bytesToBase64 } from \"../encoding/bytes\";\nimport type { Account, ConnectedWallet } from \"../types\";\n\n/**\n * Verify against `signedMessage`, not `message`: Solana Wallet Standard\n * wallets may prefix or re-encode what they sign. Base64 mirrors exist\n * because `Uint8Array` does not survive `JSON.stringify`.\n */\ntype SignInResult = {\n account: Account;\n /** The message handed to the wallet. Absent on the SIWS path, where the\n * wallet composes the message itself. */\n message?: string;\n nonce: string;\n signature: Uint8Array;\n /** Base64 of `signature`. */\n signatureBase64: string;\n signedMessage: Uint8Array;\n /** Base64 of `signedMessage`; verify against this. */\n signedMessageBase64: string;\n wallet: ConnectedWallet;\n};\n\ntype SignInMessageContext = {\n account: Account;\n nonce: string;\n wallet: ConnectedWallet;\n};\n\ntype SignInFlowOptions = {\n /**\n * butr ships no wire format: a server-parsed message is an auth spec,\n * and SIWE / SIWS already fill that role. Pass what your backend\n * expects. Unused on the SIWS path, where the wallet composes it.\n */\n buildMessage?: (ctx: SignInMessageContext) => string;\n /** Fetch a single-use nonce from your backend. */\n getNonce: (ctx: { account: Account; wallet: ConnectedWallet }) => Promise<string>;\n /**\n * Skip the Sign In With Solana path even on wallets that advertise it,\n * forcing every platform down the same `signMessage` route. Useful when\n * one backend verifier has to handle every chain identically.\n */\n preferSignMessage?: boolean;\n /** Hand the signed result to your backend. Throw to fail the flow. */\n verify: (result: SignInResult) => Promise<void>;\n};\n\ntype SignInFlow = {\n signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;\n};\n\n/** Thrown before any wallet interaction when the wallet can't sign at\n * all. Distinct from a rejection: nothing was asked of the user, so UI\n * should say \"this wallet can't sign in\" rather than \"you declined\". */\nclass SignInUnsupportedError extends Error {\n readonly connectorId: string;\n\n constructor(connectorId: string) {\n super(\n `Wallet \"${connectorId}\" reports capabilities.signMessage === false, so it cannot sign in.`,\n );\n this.connectorId = connectorId;\n this.name = \"SignInUnsupportedError\";\n }\n}\n\nconst defaultBuildMessage = ({ account, nonce }: SignInMessageContext): string =>\n `${account.walletAddress} signs in.\\nNonce: ${nonce}`;\n\n/**\n * Solana wallets advertising `solana:signIn` take the SIWS path, so\n * `result.signedMessage` holds the wallet-composed statement and\n * `result.message` is absent. `preferSignMessage` opts out.\n */\nconst createSignInFlow = (options: SignInFlowOptions): SignInFlow => {\n const buildMessage = options.buildMessage ?? defaultBuildMessage;\n\n const sign = async (wallet: ConnectedWallet, account?: Account): Promise<SignInResult> => {\n const { connector } = wallet;\n const signingAccount = account ?? wallet.account;\n\n if (!connector.capabilities.signMessage) {\n throw new SignInUnsupportedError(connector.id);\n }\n\n const nonce = await options.getNonce({ account: signingAccount, wallet });\n\n if (\n options.preferSignMessage !== true &&\n connector.chainPlatform === \"svm\" &&\n connector.capabilities.signIn &&\n connector.signIn !== undefined\n ) {\n const output = await connector.signIn({ nonce });\n return {\n account: output.account,\n nonce,\n signature: output.signature,\n signatureBase64: bytesToBase64(output.signature),\n signedMessage: output.signedMessage,\n signedMessageBase64: bytesToBase64(output.signedMessage),\n wallet,\n };\n }\n\n const message = buildMessage({ account: signingAccount, nonce, wallet });\n const { signature, signedMessage } = await connector.signMessage(\n new TextEncoder().encode(message),\n signingAccount,\n );\n\n return {\n account: signingAccount,\n message,\n nonce,\n signature,\n signatureBase64: bytesToBase64(signature),\n signedMessage,\n signedMessageBase64: bytesToBase64(signedMessage),\n wallet,\n };\n };\n\n return {\n signIn: async (wallet, account) => {\n const result = await sign(wallet, account);\n await options.verify(result);\n return result;\n },\n };\n};\n\nexport type { SignInFlow, SignInFlowOptions, SignInMessageContext, SignInResult };\nexport { SignInUnsupportedError, createSignInFlow };\n","import type { ConnectedWallet } from \"./types\";\n\n/**\n * The adapter is compared by reference, not `connector.id`: hydration\n * swaps a shadow adapter for the live one under an unchanged id, and an\n * id check would strand consumers on the throwing placeholder.\n */\nconst walletEqual = (a: ConnectedWallet | undefined, b: ConnectedWallet | undefined): boolean => {\n if (a === b) {\n return true;\n }\n if (!a || !b) {\n return false;\n }\n return (\n a.connector === b.connector &&\n a.account.walletAddress === b.account.walletAddress &&\n a.account.chain.id === b.account.chain.id\n );\n};\n\nexport { walletEqual };\n","/**\n * Some wallets announce data-URI icons wrapped in whitespace, which\n * makes Next.js `<Image>` throw on a leading control character.\n * Discovery already applies this; only hand-built metadata needs it.\n */\nconst sanitizeIcon = (icon: string | undefined): string | undefined => {\n if (icon === undefined) {\n return undefined;\n }\n const trimmed = icon.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\nexport { sanitizeIcon };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["toCookieMap"],"sources":["../src/types/account.ts","../src/types/chain.ts","../src/types/errors.ts","../src/types/platform.ts","../src/types/wallet.ts","../src/chains.ts","../src/logger.ts","../src/wallet-source.ts","../src/store/shadow-adapter.ts","../src/storage/browser-storage-driver.ts","../src/storage/validation.ts","../src/storage/wallet-storage.ts","../src/store/connector-lifecycle.ts","../src/wallet-equal.ts","../src/store/reducer.ts","../src/store/wallet-manager.ts","../src/storage/cookie-storage-driver.ts","../src/storage/snapshot.ts","../src/group-by-platform.ts","../src/encoding/bytes.ts","../src/sign-in/sign-in-flow.ts","../src/sanitize-icon.ts"],"sourcesContent":["import type { ChainBase } from \"./chain\";\n\ntype Account = {\n chain: ChainBase;\n id: string;\n walletAddress: string;\n};\n\n/**\n * The reducer compares accounts by the composite `<chain>:<address>`\n * id, so every adapter must build accounts here or reproduce that\n * format byte for byte.\n */\nconst buildAccount = (address: string, chain: ChainBase): Account => ({\n chain,\n id: `${chain.id}:${address}`,\n walletAddress: address,\n});\n\ntype Balance = {\n /** Token decimals (e.g. 9 for SOL, 18 for ETH) */\n decimals: number;\n /** Human-readable string, trimmed of trailing zeros */\n formatted: string;\n /** Token symbol (e.g. \"SOL\", \"ETH\") */\n symbol: string;\n /** Raw integer amount */\n value: bigint;\n};\n\nexport type { Account, Balance };\nexport { buildAccount };\n","/**\n * CAIP-2 shaped. butr never reads past these four fields, so consumers\n * can extend the type structurally with logos, explorers and the like.\n */\ntype ChainBase = {\n /** CAIP-2 identifier, e.g. \"eip155:1\", \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\" */\n id: string;\n /** Human-readable chain name, e.g. \"Ethereum\". Never the wallet's name. */\n name: string;\n /** CAIP-2 namespace, e.g. \"eip155\", \"solana\" */\n namespace: string;\n /** CAIP-2 reference, e.g. \"1\", \"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\" */\n reference: string;\n};\n\n/**\n * Wallets report bare CAIP-2 ids, so the display name comes from a chain\n * registry. A chain outside `known` keeps its id as its name rather than\n * borrowing the wallet's.\n */\nconst resolveChain = (id: string, known: ReadonlyArray<ChainBase> = []): ChainBase => {\n const match = known.find((chain) => chain.id === id);\n if (match !== undefined) {\n return match;\n }\n const [namespace = \"\", reference = \"\"] = id.split(\":\");\n return { id, name: id, namespace, reference };\n};\n\nexport type { ChainBase };\nexport { resolveChain };\n","/**\n * Wallet SDKs disagree on error shape (EIP-1193 codes, bare strings,\n * bespoke classes), so consumers branch on `kind` rather than regexing\n * messages. `cause` keeps the original value.\n */\ntype ConnectionErrorKind =\n | \"ChainMismatch\"\n | \"NotConnected\"\n | \"RequestPending\"\n | \"Timeout\"\n | \"Unknown\"\n | \"UserRejected\"\n | \"WalletLocked\"\n | \"WalletNotFound\";\n\nclass ConnectionError extends Error {\n readonly kind: ConnectionErrorKind;\n\n constructor(kind: ConnectionErrorKind, message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"ConnectionError\";\n this.kind = kind;\n }\n}\n\nconst readCode = (error: Error): number | string | undefined =>\n \"code\" in error && (typeof error.code === \"number\" || typeof error.code === \"string\")\n ? error.code\n : undefined;\n\n/**\n * EIP-1193 codes: `4001` rejected, `-32002` pending, `4100`/`4900`/`4901`\n * unauthorized or disconnected. Message-substring matching is the last\n * resort for SDKs that ship no codes at all.\n */\nconst classify = (error: Error): ConnectionErrorKind => {\n const code = readCode(error);\n if (code === 4001) {\n return \"UserRejected\";\n }\n if (code === -32_002) {\n return \"RequestPending\";\n }\n if (code === 4100 || code === 4900 || code === 4901) {\n return \"NotConnected\";\n }\n const lower = error.message.toLowerCase();\n if (lower.includes(\"user rejected\") || lower.includes(\"user denied\")) {\n return \"UserRejected\";\n }\n if (lower.includes(\"not connected\")) {\n return \"NotConnected\";\n }\n if (lower.includes(\"locked\")) {\n return \"WalletLocked\";\n }\n if (lower.includes(\"chain\") && (lower.includes(\"mismatch\") || lower.includes(\"unsupported\"))) {\n return \"ChainMismatch\";\n }\n return \"Unknown\";\n};\n\n/** The one boundary where a thrown value of unknown shape becomes typed. */\nconst toConnectionError = (cause: unknown): ConnectionError => {\n if (cause instanceof ConnectionError) {\n return cause;\n }\n if (cause instanceof Error) {\n return new ConnectionError(classify(cause), cause.message, { cause });\n }\n const message = typeof cause === \"string\" && cause !== \"\" ? cause : \"Connection failed\";\n return new ConnectionError(\"Unknown\", message, { cause });\n};\n\nexport type { ConnectionErrorKind };\nexport { ConnectionError, toConnectionError };\n","/**\n * Single runtime source of truth: `ChainPlatform` and the storage\n * validators' allowlists both derive from it. Omitting a platform here\n * silently stops its connections from persisting.\n */\nconst CHAIN_PLATFORMS = [\"evm\", \"svm\", \"sui\", \"bitcoin\", \"polkadot\"] as const;\n\ntype ChainPlatform = (typeof CHAIN_PLATFORMS)[number];\n\nconst PLATFORMS: ReadonlySet<string> = new Set(CHAIN_PLATFORMS);\n\nconst isChainPlatform = (value: string): value is ChainPlatform => PLATFORMS.has(value);\n\nexport type { ChainPlatform };\nexport { CHAIN_PLATFORMS, isChainPlatform };\n","import type { Account, Balance } from \"./account\";\nimport type { ChainBase } from \"./chain\";\nimport type { Connector } from \"./connector\";\nimport type { ChainPlatform } from \"./platform\";\nimport type { WalletSigner } from \"./signer\";\n\ntype SignInInput = { readonly [key: string]: SignInValue };\ntype SignInValue = boolean | number | string | null | ReadonlyArray<SignInValue> | SignInInput;\n\ntype SignedMessage = { signature: Uint8Array; signedMessage: Uint8Array };\n\ntype TransactionReceipt = { status: \"Error\" | \"Pending\" | \"Success\" };\n\n/** Acts as `account`, which must be one the wallet exposes; an unknown one\n * rejects rather than signing with another. Omitted, the wallet uses its own\n * active account, so pass `wallet.account` to honour `setAccount`. */\ntype AccountOptions = { account?: Account };\n\n/**\n * `chain` targets this one transaction. Wallet Standard routes it per call;\n * an EVM wallet has one global network, so it switches first; a transport\n * that can do neither rejects when `chain` is not its current chain.\n */\ntype TransactionOptions = AccountOptions & { chain?: ChainBase };\n\n/** `token` is chain-specific: an ERC-20 contract address on EVM. Omit it for\n * the native asset. */\ntype BalanceOptions = AccountOptions & { token?: string };\n\n/**\n * Presence is the capability check: an adapter defines an optional method\n * only when calling it can succeed for this wallet, and never ships a\n * placeholder (a zero balance, a receipt that stays pending).\n */\ntype WalletBase<Tx> = {\n getBalance?: (options?: BalanceOptions) => Promise<Balance>;\n /** Narrow with `switch (signer.kind)`; see `WalletSignerRegistry`. */\n getSigner: () => Promise<WalletSigner>;\n getTransactionReceipt?: (hash: string) => Promise<TransactionReceipt>;\n /** Signs and broadcasts; resolves the transaction hash or signature. */\n sendTx?: (tx: Tx, options?: TransactionOptions) => Promise<string>;\n /**\n * Verify against `signedMessage`, not the input: Solana Wallet Standard\n * wallets may prefix or re-encode it.\n */\n signMessage?: (message: Uint8Array, options?: AccountOptions) => Promise<SignedMessage>;\n /** Moves the wallet (or butr's view of it) to `chain`. Rejects for a\n * chain outside this adapter's namespace or not advertised by the wallet. */\n switchChain?: (chain: ChainBase) => Promise<void>;\n};\n\ntype EvmTransactionValue =\n | bigint\n | boolean\n | number\n | string\n | null\n | ReadonlyArray<EvmTransactionValue>\n | { readonly [key: string]: EvmTransactionValue | undefined };\n\n/** An `eth_sendTransaction` request. `bigint` quantities are encoded as hex. */\ntype EvmTransactionRequest = Readonly<Record<string, EvmTransactionValue | undefined>>;\n\n/** A `@mysten/sui` `Transaction` (anything with `toJSON()`), its JSON string,\n * or BCS bytes. */\ntype SuiTransactionInput = string | Uint8Array | { toJSON: () => Promise<string> };\n\n/** `amount` in satoshis. */\ntype BitcoinTransfer = { amount: bigint; recipient: string };\n\ntype SignInOutput = { account: Account; signature: Uint8Array; signedMessage: Uint8Array };\n\n/** No `signIn` (SIWE is app-level) and no `signTransaction`: EVM wallets sign\n * and send in one step through `eth_sendTransaction`. */\ntype EvmWallet = WalletBase<EvmTransactionRequest>;\n\ntype SvmWallet = WalletBase<Uint8Array> & {\n /** Sign In With Solana (`solana:signIn`). `input` holds the SIWS message\n * fields (domain, statement, nonce, …). */\n signIn?: (input?: SignInInput) => Promise<SignInOutput>;\n /** Signs a serialized transaction without broadcasting it and resolves the\n * full signed transaction, ready for your own RPC client. */\n signTransaction?: (tx: Uint8Array, options?: TransactionOptions) => Promise<Uint8Array>;\n};\n\ntype SuiWallet = WalletBase<SuiTransactionInput> & {\n /** Signs without executing. `SuiClient.executeTransactionBlock` needs both\n * halves, so the result carries the transaction bytes and the signature. */\n signTransaction?: (\n tx: SuiTransactionInput,\n options?: TransactionOptions,\n ) => Promise<{ bytes: Uint8Array; signature: Uint8Array }>;\n};\n\ntype BitcoinWallet = WalletBase<BitcoinTransfer> & {\n /** `bitcoin:signPsbt`: PSBT bytes in (`psbt.toBuffer()`), signed PSBT bytes\n * out, to finalise and broadcast through your own Esplora or Electrum\n * client. */\n signTransaction?: (psbt: Uint8Array, options?: TransactionOptions) => Promise<Uint8Array>;\n};\n\n/** Building an extrinsic needs chain metadata over RPC, which butr does not\n * ship, so transactions go through the `getSigner()` handoff. */\ntype PolkadotWallet = Omit<WalletBase<never>, \"sendTx\">;\n\ntype EvmAdapter = Connector<\"evm\"> & EvmWallet;\ntype SvmAdapter = Connector<\"svm\"> & SvmWallet;\ntype SuiAdapter = Connector<\"sui\"> & SuiWallet;\ntype BitcoinAdapter = Connector<\"bitcoin\"> & BitcoinWallet;\ntype PolkadotAdapter = Connector<\"polkadot\"> & PolkadotWallet;\n\n/** Narrow on `chainPlatform` to reach a platform's own methods and\n * transaction type. */\ntype WalletAdapter = EvmAdapter | SvmAdapter | SuiAdapter | BitcoinAdapter | PolkadotAdapter;\n\ntype AdapterByPlatform = {\n bitcoin: BitcoinAdapter;\n evm: EvmAdapter;\n polkadot: PolkadotAdapter;\n sui: SuiAdapter;\n svm: SvmAdapter;\n};\n\ntype WalletAdapterFor<P extends ChainPlatform> = Extract<WalletAdapter, { chainPlatform: P }>;\n\ntype ConnectedWallet<P extends ChainPlatform = ChainPlatform> = {\n /** The active account; always one of `accounts`. */\n account: Account;\n /** Every account the wallet exposed at the last connect or refresh. */\n accounts: ReadonlyArray<Account>;\n /** An indexed access rather than `WalletAdapterFor`, so TypeScript measures\n * `P` covariant and `ConnectedWallet<\"evm\">` stays a `ConnectedWallet`. */\n connector: AdapterByPlatform[P];\n};\n\n/** Narrows a pool entry to one platform, e.g. before calling its `sendTx`. */\nconst isPlatformWallet = <P extends ChainPlatform>(\n wallet: ConnectedWallet,\n platform: P,\n): wallet is ConnectedWallet<P> => wallet.connector.chainPlatform === platform;\n\nexport type {\n AccountOptions,\n BalanceOptions,\n BitcoinAdapter,\n BitcoinTransfer,\n BitcoinWallet,\n ConnectedWallet,\n EvmAdapter,\n EvmTransactionRequest,\n EvmTransactionValue,\n EvmWallet,\n PolkadotAdapter,\n PolkadotWallet,\n SignedMessage,\n SignInInput,\n SignInOutput,\n SignInValue,\n SuiAdapter,\n SuiTransactionInput,\n SuiWallet,\n SvmAdapter,\n SvmWallet,\n TransactionOptions,\n TransactionReceipt,\n WalletAdapter,\n WalletAdapterFor,\n WalletBase,\n};\nexport { isPlatformWallet };\n","import type { ChainBase } from \"./types/chain\";\nimport type { ChainsByPlatform } from \"./types/chains-by-platform\";\n\n/*\n * Every platform's chain registry, as plain data, so every transport names\n * a chain the same way through `resolveChain`. Unused ones tree-shake away.\n */\n\n/** Common EVM chains. `switchChain` accepts any `ChainBase`, so a chain\n * missing here still works; it is just named by its CAIP-2 id. */\nconst EVM_CHAINS = {\n arbitrum: {\n id: \"eip155:42161\",\n name: \"Arbitrum One\",\n namespace: \"eip155\",\n reference: \"42161\",\n },\n base: { id: \"eip155:8453\", name: \"Base\", namespace: \"eip155\", reference: \"8453\" },\n bsc: { id: \"eip155:56\", name: \"BNB Smart Chain\", namespace: \"eip155\", reference: \"56\" },\n ethereum: { id: \"eip155:1\", name: \"Ethereum\", namespace: \"eip155\", reference: \"1\" },\n optimism: { id: \"eip155:10\", name: \"Optimism\", namespace: \"eip155\", reference: \"10\" },\n polygon: { id: \"eip155:137\", name: \"Polygon\", namespace: \"eip155\", reference: \"137\" },\n sepolia: { id: \"eip155:11155111\", name: \"Sepolia\", namespace: \"eip155\", reference: \"11155111\" },\n} as const satisfies Record<string, ChainBase>;\n\nconst SVM_CHAINS = {\n devnet: { id: \"solana:devnet\", name: \"Solana Devnet\", namespace: \"solana\", reference: \"devnet\" },\n mainnet: {\n id: \"solana:mainnet\",\n name: \"Solana Mainnet\",\n namespace: \"solana\",\n reference: \"mainnet\",\n },\n testnet: {\n id: \"solana:testnet\",\n name: \"Solana Testnet\",\n namespace: \"solana\",\n reference: \"testnet\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst SUI_CHAINS = {\n devnet: { id: \"sui:devnet\", name: \"Sui Devnet\", namespace: \"sui\", reference: \"devnet\" },\n localnet: {\n id: \"sui:localnet\",\n name: \"Sui Localnet\",\n namespace: \"sui\",\n reference: \"localnet\",\n },\n mainnet: {\n id: \"sui:mainnet\",\n name: \"Sui Mainnet\",\n namespace: \"sui\",\n reference: \"mainnet\",\n },\n testnet: { id: \"sui:testnet\", name: \"Sui Testnet\", namespace: \"sui\", reference: \"testnet\" },\n} as const satisfies Record<string, ChainBase>;\n\n/** CAIP-2 Bitcoin references are the first 16 bytes of each network's\n * genesis block hash, which is what wallets advertise in `wallet.chains`. */\nconst BITCOIN_CHAINS = {\n mainnet: {\n id: \"bip122:000000000019d6689c085ae165831e93\",\n name: \"Bitcoin\",\n namespace: \"bip122\",\n reference: \"000000000019d6689c085ae165831e93\",\n },\n signet: {\n id: \"bip122:00000008819873e925422c1ff0f99f7c\",\n name: \"Bitcoin Signet\",\n namespace: \"bip122\",\n reference: \"00000008819873e925422c1ff0f99f7c\",\n },\n testnet: {\n id: \"bip122:000000000933ea01ad0ee984209779ba\",\n name: \"Bitcoin Testnet\",\n namespace: \"bip122\",\n reference: \"000000000933ea01ad0ee984209779ba\",\n },\n testnet4: {\n id: \"bip122:00000000da84f2bafbbc53dee25a72ae\",\n name: \"Bitcoin Testnet4\",\n namespace: \"bip122\",\n reference: \"00000000da84f2bafbbc53dee25a72ae\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst POLKADOT_CHAINS = {\n kusama: {\n id: \"polkadot:b0a8d493285c2df73290dfb7e61f870f\",\n name: \"Kusama\",\n namespace: \"polkadot\",\n reference: \"b0a8d493285c2df73290dfb7e61f870f\",\n },\n paseo: {\n id: \"polkadot:77afd6190f1554ad45fd0d31aee62aac\",\n name: \"Paseo\",\n namespace: \"polkadot\",\n reference: \"77afd6190f1554ad45fd0d31aee62aac\",\n },\n polkadot: {\n id: \"polkadot:91b171bb158e2d3848fa23a9f1c25182\",\n name: \"Polkadot\",\n namespace: \"polkadot\",\n reference: \"91b171bb158e2d3848fa23a9f1c25182\",\n },\n westend: {\n id: \"polkadot:e143f23803ac50e8f6f8e62695d1ce9e\",\n name: \"Westend\",\n namespace: \"polkadot\",\n reference: \"e143f23803ac50e8f6f8e62695d1ce9e\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst EVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(EVM_CHAINS);\nconst SVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SVM_CHAINS);\nconst SUI_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SUI_CHAINS);\nconst BITCOIN_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(BITCOIN_CHAINS);\nconst POLKADOT_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(POLKADOT_CHAINS);\n\n/** Every registry as a list, keyed by platform: the shape chain pickers want. */\nconst CHAINS_BY_PLATFORM: ChainsByPlatform = {\n bitcoin: BITCOIN_CHAINS_LIST,\n evm: EVM_CHAINS_LIST,\n polkadot: POLKADOT_CHAINS_LIST,\n sui: SUI_CHAINS_LIST,\n svm: SVM_CHAINS_LIST,\n};\n\nexport {\n BITCOIN_CHAINS,\n BITCOIN_CHAINS_LIST,\n CHAINS_BY_PLATFORM,\n EVM_CHAINS,\n EVM_CHAINS_LIST,\n POLKADOT_CHAINS,\n POLKADOT_CHAINS_LIST,\n SUI_CHAINS,\n SUI_CHAINS_LIST,\n SVM_CHAINS,\n SVM_CHAINS_LIST,\n};\n","const logWarn = (...args: ReadonlyArray<unknown>): void => {\n console.warn(...args);\n};\n\nconst logError = (...args: ReadonlyArray<unknown>): void => {\n console.error(...args);\n};\n\nexport { logError, logWarn };\n","import { logWarn } from \"./logger\";\nimport type { MaybePromise } from \"./storage/persistence\";\nimport type { WalletAdapter } from \"./types/wallet\";\n\n/**\n * The discovery seam: a function that announces adapters and returns its\n * unsubscribe. Every `discover*Adapters` export already has this shape, so\n * it goes into `sources` as-is.\n */\ntype WalletSource = (onAdapter: (adapter: WalletAdapter) => void) => () => void;\n\nconst isIterable = (\n value: WalletAdapter | Iterable<WalletAdapter>,\n): value is Iterable<WalletAdapter> => Symbol.iterator in value;\n\n/**\n * For adapters that are built rather than discovered: WalletConnect, Ledger,\n * hand-rolled ones. Takes one adapter or several, or a promise of either; a\n * rejected promise is logged and contributes nothing.\n */\nconst fromAdapters =\n (adapters: MaybePromise<WalletAdapter | Iterable<WalletAdapter>>): WalletSource =>\n (onAdapter) => {\n // SAFETY: the returned cleanup clears this after the await; TypeScript cannot see writes from closures\n let active = true as boolean;\n void (async () => {\n try {\n const resolved = await adapters;\n for (const adapter of isIterable(resolved) ? resolved : [resolved]) {\n if (!active) {\n return;\n }\n onAdapter(adapter);\n }\n } catch (error) {\n logWarn(\"[butr] fromAdapters: adapters failed to load:\", error);\n }\n })();\n return () => {\n active = false;\n };\n };\n\nexport type { WalletSource };\nexport { fromAdapters };\n","import type { StoredPoolEntry } from \"../storage/persistence\";\nimport type { WalletAdapter } from \"../types\";\n\n/**\n * Thrown by the three required methods of a shadow adapter. Every optional\n * method is simply absent, so only code that skipped the `reconnectingIds`\n * check and called `connect`, `getAccounts` or `getSigner` sees this.\n */\nclass ShadowConnectorError extends Error {\n readonly connectorId: string;\n readonly method: string;\n\n constructor(method: string, connectorId: string) {\n super(\n `[butr] ${method} called on \"${connectorId}\" while it is still reconnecting. Check useIsReconnecting(id) before calling it.`,\n );\n this.name = \"ShadowConnectorError\";\n this.connectorId = connectorId;\n this.method = method;\n }\n}\n\n/**\n * Stands in for a connection seeded from `initialState` so its identity\n * renders before discovery runs. It exposes no optional methods; the manager\n * swaps it for the live adapter, or evicts it, once silent reconnect settles.\n */\nconst createShadowAdapter = (entry: StoredPoolEntry): WalletAdapter => {\n const reject = (method: string) => () =>\n Promise.reject(new ShadowConnectorError(method, entry.connectorId));\n return {\n chainPlatform: entry.chainPlatform,\n connect: reject(\"connect\"),\n getAccounts: reject(\"getAccounts\"),\n getSigner: reject(\"getSigner\"),\n icon: entry.icon,\n id: entry.connectorId,\n name: entry.name,\n };\n};\n\nexport { createShadowAdapter, ShadowConnectorError };\n","import type { StorageDriver } from \"./persistence\";\n\ntype BrowserStorageDrivers = {\n /** Survives app restart (localStorage on web, AsyncStorage/MMKV on RN). */\n persistent: StorageDriver;\n /** Cleared when the app session ends (sessionStorage on web, in-memory on RN). */\n session: StorageDriver;\n};\n\ntype WebStorageKind = \"localStorage\" | \"sessionStorage\";\n\nconst webStorageGlobals: Partial<Pick<typeof globalThis, WebStorageKind>> = globalThis;\n\nconst hasWebStorage = (kind: WebStorageKind): boolean => {\n try {\n return kind === \"localStorage\"\n ? webStorageGlobals.localStorage !== undefined\n : webStorageGlobals.sessionStorage !== undefined;\n } catch {\n return false;\n }\n};\n\nconst createMemoryStorageDriver = (): StorageDriver => {\n const memory = new Map<string, string>();\n return {\n getItem(key) {\n return memory.get(key) ?? null;\n },\n removeItem(key) {\n memory.delete(key);\n },\n setItem(key, value) {\n memory.set(key, value);\n },\n };\n};\n\nconst createWebStorageDriver = (kind: WebStorageKind): StorageDriver => {\n if (!hasWebStorage(kind)) {\n return createMemoryStorageDriver();\n }\n\n const storage = kind === \"localStorage\" ? globalThis.localStorage : globalThis.sessionStorage;\n\n return {\n getItem(key) {\n try {\n return storage.getItem(key);\n } catch {\n return null;\n }\n },\n removeItem(key) {\n try {\n storage.removeItem(key);\n } catch {\n void 0;\n }\n },\n setItem(key, value) {\n try {\n storage.setItem(key, value);\n } catch {\n void 0;\n }\n },\n };\n};\n\n/**\n * Default storage drivers for web. `persistent` wraps `localStorage`,\n * `session` wraps `sessionStorage`. Both fall back to in-memory maps\n * when the global isn't available (SSR, non-browser environments).\n */\nconst createBrowserStorageDriver = (): BrowserStorageDrivers => ({\n persistent: createWebStorageDriver(\"localStorage\"),\n session: createWebStorageDriver(\"sessionStorage\"),\n});\n\nexport type { BrowserStorageDrivers };\nexport { createBrowserStorageDriver, createMemoryStorageDriver };\n","import { z } from \"zod\";\n\nimport { logWarn } from \"../logger\";\nimport { CHAIN_PLATFORMS } from \"../types/platform\";\n\nimport type { StoredPoolRecord, StoredSelectionRecord } from \"./persistence\";\n\nconst chainPlatformSchema = z.enum(CHAIN_PLATFORMS);\n\nconst chainSchema = z.looseObject({\n id: z.string(),\n name: z.string(),\n namespace: z.string(),\n reference: z.string(),\n});\n\nconst accountSchema = z.looseObject({\n chain: chainSchema,\n id: z.string(),\n walletAddress: z.string(),\n});\n\nconst storedPoolEntrySchema = z.looseObject({\n account: accountSchema,\n accounts: z.array(accountSchema),\n chainPlatform: chainPlatformSchema,\n connectorId: z.string(),\n icon: z.string().optional(),\n name: z.string().min(1),\n});\n\nconst recordSchema = z.record(z.string(), z.unknown());\n\nconst DEFAULT_KEY_PREFIX = \"butr\";\n\n/**\n * `createWalletStorage` writes these and `readWalletSnapshot` reads them from\n * a cookie jar; any divergence desyncs the SSR-seeded render from the\n * client that rehydrates it, the failure ADR 0003 exists to prevent.\n */\nconst storageKeys = (keyPrefix?: string) => {\n const prefix = keyPrefix === undefined || keyPrefix === \"\" ? DEFAULT_KEY_PREFIX : keyPrefix;\n return {\n active: `${prefix}-active`,\n pool: `${prefix}-pool`,\n selection: `${prefix}-selection`,\n userDisconnected: `${prefix}-user-disconnected`,\n };\n};\n\nconst parseRecord = (raw: string | null | undefined, label: string, what: string) => {\n if (raw === null || raw === undefined || raw === \"\") {\n return {};\n }\n try {\n const parsed = recordSchema.safeParse(JSON.parse(raw));\n return parsed.success ? parsed.data : {};\n } catch (error) {\n logWarn(`${label} failed to parse ${what} from storage:`, error);\n return {};\n }\n};\n\n/**\n * Never repairs or throws: one corrupt entry must not take down a whole\n * session, and `readWalletSnapshot` may run on a server with no cookie\n * jar to write the eviction to. The next save overwrites the bad value.\n */\nconst decodePool = (raw: string | null | undefined, label = \"[butr]\"): StoredPoolRecord => {\n const result: StoredPoolRecord = {};\n for (const [key, value] of Object.entries(parseRecord(raw, label, \"pool\"))) {\n const entry = storedPoolEntrySchema.safeParse(value);\n if (entry.success && entry.data.connectorId === key) {\n result[key] = entry.data;\n } else {\n logWarn(`${label} dropping invalid pool entry for ${key}`);\n }\n }\n return result;\n};\n\n/** Decode a persisted selection payload. Same contract as `decodePool`. */\nconst decodeSelection = (\n raw: string | null | undefined,\n label = \"[butr]\",\n): StoredSelectionRecord => {\n const result: StoredSelectionRecord = {};\n for (const [key, value] of Object.entries(parseRecord(raw, label, \"selection\"))) {\n const platform = chainPlatformSchema.safeParse(key);\n if (platform.success && typeof value === \"string\" && value.length > 0) {\n result[platform.data] = value;\n }\n }\n return result;\n};\n\nexport { decodePool, decodeSelection, storageKeys };\n","import { logWarn } from \"../logger\";\n\nimport { createBrowserStorageDriver } from \"./browser-storage-driver\";\nimport type {\n PersistedWalletState,\n StorageDriver,\n StoredPoolRecord,\n StoredSelectionRecord,\n WalletPersistence,\n} from \"./persistence\";\nimport { decodePool, decodeSelection, storageKeys } from \"./validation\";\n\ntype WalletStorageOptions = {\n /** Defaults to `\"butr\"`, matching `readWalletSnapshot`. */\n keyPrefix?: string;\n /** Survives app restart. Defaults to localStorage on web. */\n persistent?: StorageDriver;\n /** Cleared on session end. Defaults to sessionStorage on web. */\n session?: StorageDriver;\n};\n\nconst isEmpty = (record: StoredPoolRecord | StoredSelectionRecord): boolean =>\n Object.keys(record).length === 0;\n\n/**\n * Pool, selection and active id go to the persistent driver, the disconnect\n * intent to the session driver. Unchanged keys are not rewritten, so a\n * cookie-backed driver does not re-send every cookie on each save.\n */\nconst createWalletStorage = (options: WalletStorageOptions = {}): WalletPersistence => {\n const keys = storageKeys(options.keyPrefix);\n // Cheap: wraps the globals (or in-memory maps) without touching them.\n const browser = createBrowserStorageDriver();\n const persistent = options.persistent ?? browser.persistent;\n const session = options.session ?? browser.session;\n const written = new Map<string, string | null>();\n\n const read = async (driver: StorageDriver, key: string): Promise<string | null> => {\n try {\n const value = await driver.getItem(key);\n written.set(key, value);\n return value;\n } catch (error) {\n logWarn(`[butr] failed to read ${key} from storage:`, error);\n return null;\n }\n };\n\n const write = async (driver: StorageDriver, key: string, value: string | null) => {\n if (written.get(key) === value) {\n return;\n }\n await (value === null ? driver.removeItem(key) : driver.setItem(key, value));\n written.set(key, value);\n };\n\n return {\n load: async () => {\n const [pool, selection, active, userDisconnected] = await Promise.all([\n read(persistent, keys.pool),\n read(persistent, keys.selection),\n read(persistent, keys.active),\n read(session, keys.userDisconnected),\n ]);\n return {\n activeConnectorId: active === null || active === \"\" ? null : active,\n isUserDisconnected: userDisconnected === \"true\",\n pool: decodePool(pool),\n selection: decodeSelection(selection),\n };\n },\n\n save: async (state: PersistedWalletState) => {\n // A failed key must not release the manager's save queue while other\n // writes from this snapshot can still overwrite the next snapshot.\n const results = await Promise.allSettled([\n write(persistent, keys.pool, isEmpty(state.pool) ? null : JSON.stringify(state.pool)),\n write(\n persistent,\n keys.selection,\n isEmpty(state.selection) ? null : JSON.stringify(state.selection),\n ),\n write(persistent, keys.active, state.activeConnectorId),\n write(session, keys.userDisconnected, state.isUserDisconnected ? \"true\" : null),\n ]);\n const failure = results.find((result) => result.status === \"rejected\");\n if (failure !== undefined) {\n throw failure.reason;\n }\n },\n };\n};\n\nexport type { WalletStorageOptions };\nexport { createWalletStorage };\n","import { logWarn } from \"../logger\";\nimport type { Account, ConnectedWallet, Connector } from \"../types\";\n\ntype LifecycleHandlers = {\n /** The wallet exposed a new account list, active first. Never empty. */\n onAccountsChanged: (connectorId: string, accounts: ReadonlyArray<Account>) => void;\n /** The wallet ended the session itself. The bridge has already dropped its\n * subscription by the time this fires. */\n onDisconnected: (connectorId: string) => void;\n};\n\n/**\n * Owns the \"at most one subscription per live connector\" invariant. The\n * manager never attaches or detaches by hand: it calls `sync` with the pool\n * whenever the pool changes, and the bridge diffs.\n */\ntype ConnectorLifecycle = {\n detachAll: () => void;\n sync: (pool: ReadonlyMap<string, ConnectedWallet>) => void;\n};\n\nconst createConnectorLifecycle = (handlers: LifecycleHandlers): ConnectorLifecycle => {\n const live = new Map<string, { connector: Connector; unsubscribe: () => void }>();\n\n const detach = (connectorId: string) => {\n const subscription = live.get(connectorId);\n if (subscription === undefined) {\n return;\n }\n live.delete(connectorId);\n try {\n subscription.unsubscribe();\n } catch (error) {\n logWarn(\"[butr] unsubscribe threw:\", error);\n }\n };\n\n const attach = (connectorId: string, connector: Connector) => {\n if (connector.subscribe === undefined) {\n return;\n }\n const disconnected = () => {\n detach(connectorId);\n handlers.onDisconnected(connectorId);\n };\n try {\n const unsubscribe = connector.subscribe((event) => {\n // Ignore events from a connector that has since been replaced.\n if (live.get(connectorId)?.connector !== connector) {\n return;\n }\n if (event.type === \"disconnected\" || event.accounts.length === 0) {\n disconnected();\n return;\n }\n handlers.onAccountsChanged(connectorId, event.accounts);\n });\n live.set(connectorId, { connector, unsubscribe });\n } catch (error) {\n logWarn(`[butr] subscribe failed for ${connectorId}:`, error);\n }\n };\n\n return {\n detachAll: () => {\n for (const connectorId of live.keys()) {\n detach(connectorId);\n }\n },\n\n sync: (pool) => {\n for (const [connectorId, subscription] of live) {\n if (pool.get(connectorId)?.connector !== subscription.connector) {\n detach(connectorId);\n }\n }\n for (const [connectorId, wallet] of pool) {\n if (!live.has(connectorId)) {\n attach(connectorId, wallet.connector);\n }\n }\n },\n };\n};\n\nexport type { ConnectorLifecycle, LifecycleHandlers };\nexport { createConnectorLifecycle };\n","import type { Account, ConnectedWallet } from \"./types\";\n\n/** Account ids are canonical (`buildAccount`), so identity is the id. */\nconst accountsEqual = (a: ReadonlyArray<Account>, b: ReadonlyArray<Account>): boolean =>\n a === b || (a.length === b.length && a.every((account, i) => account.id === b[i]?.id));\n\n/**\n * The adapter is compared by reference, not `connector.id`: hydration\n * swaps a shadow adapter for the live one under an unchanged id, and an\n * id check would strand consumers on the placeholder.\n */\nconst walletEqual = (a: ConnectedWallet | undefined, b: ConnectedWallet | undefined): boolean =>\n a === b ||\n (a !== undefined &&\n b !== undefined &&\n a.connector === b.connector &&\n a.account.id === b.account.id &&\n accountsEqual(a.accounts, b.accounts));\n\nexport { accountsEqual, walletEqual };\n","import type {\n StoredPoolEntry,\n StoredPoolRecord,\n StoredSelectionRecord,\n WalletSnapshot,\n} from \"../storage/persistence\";\nimport type { Account, ChainPlatform, ConnectedWallet, WalletAdapter } from \"../types\";\nimport type { ConnectionError } from \"../types/errors\";\nimport { isChainPlatform } from \"../types/platform\";\nimport { accountsEqual } from \"../wallet-equal\";\n\nimport { createShadowAdapter } from \"./shadow-adapter\";\n\n/** Status of the latest `connect` attempt, not of the connection itself;\n * `useConnectionStatus` derives the latter. */\ntype ConnectStatus = \"idle\" | \"connecting\" | \"success\" | \"error\";\n\ntype WalletState = {\n activeConnectorId: string | null;\n /** Every adapter the sources announced, in announcement order. */\n adapters: ReadonlyArray<WalletAdapter>;\n connectingConnectorId: string | null;\n connectionError: ConnectionError | null;\n connectionStatus: ConnectStatus;\n /**\n * Persisted connections that are not live: awaiting their adapter, failed\n * their silent reconnect, or ended by the wallet rather than the user.\n * They stay persisted so the next load retries them.\n */\n dormant: ReadonlyMap<string, StoredPoolEntry>;\n isHydrated: boolean;\n isUserDisconnected: boolean;\n pool: ReadonlyMap<string, ConnectedWallet>;\n /** Pool ids still backed by a shadow adapter seeded from `initialState`. */\n reconnectingIds: ReadonlySet<string>;\n /** Which pool entry serves each platform present in the pool. */\n selection: ReadonlyMap<ChainPlatform, string>;\n};\n\ntype WalletEvent =\n | { adapter: WalletAdapter; type: \"ADAPTER_REGISTERED\" }\n | { entries: StoredPoolRecord; isUserDisconnected: boolean; type: \"STORAGE_LOADED\" }\n | { connectorId: string; entry: ConnectedWallet; type: \"ENTRY_RESTORED\" }\n | { connectorId: string; type: \"RESTORE_FAILED\" }\n | { activeConnectorId: string | null; selection: StoredSelectionRecord; type: \"HYDRATED\" }\n | { connectorId: string; type: \"CONNECT_STARTED\" }\n | { connectorId: string; entry: ConnectedWallet; type: \"CONNECT_SUCCEEDED\" }\n | { connectorId: string; error: ConnectionError; type: \"CONNECT_FAILED\" }\n | { type: \"STATUS_RESET\" }\n | {\n accounts: ReadonlyArray<Account>;\n /** Becomes the active account. Omitted, the current one is kept while\n * still exposed, else the first. */\n active?: Account;\n connectorId: string;\n type: \"ACCOUNTS_CHANGED\";\n }\n | { byUser: boolean; connectorId: string; type: \"DISCONNECTED\" }\n | { type: \"RESET\" }\n | { connectorId: string; type: \"ACTIVE_CHANGED\" }\n | { chainPlatform: ChainPlatform; connectorId: string; type: \"SELECTION_CHANGED\" };\n\nconst initialState: WalletState = {\n activeConnectorId: null,\n adapters: [],\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"idle\",\n dormant: new Map(),\n isHydrated: false,\n isUserDisconnected: false,\n pool: new Map(),\n reconnectingIds: new Set(),\n selection: new Map(),\n};\n\nconst toStoredEntry = (connectorId: string, wallet: ConnectedWallet): StoredPoolEntry => ({\n account: wallet.account,\n accounts: wallet.accounts,\n chainPlatform: wallet.connector.chainPlatform,\n connectorId,\n icon: wallet.connector.icon,\n name: wallet.connector.name,\n});\n\nconst withEntry = <K, V>(map: ReadonlyMap<K, V>, key: K, value: V): ReadonlyMap<K, V> =>\n new Map(map).set(key, value);\n\nconst withoutKey = <K, V>(map: ReadonlyMap<K, V>, key: K): ReadonlyMap<K, V> => {\n if (!map.has(key)) {\n return map;\n }\n const next = new Map(map);\n next.delete(key);\n return next;\n};\n\nconst withoutId = (set: ReadonlySet<string>, id: string): ReadonlySet<string> => {\n if (!set.has(id)) {\n return set;\n }\n const next = new Set(set);\n next.delete(id);\n return next;\n};\n\nconst keepEntries = <K, V>(\n map: ReadonlyMap<K, V>,\n keep: (key: K, value: V) => boolean,\n): ReadonlyMap<K, V> => {\n const kept = [...map].filter(([key, value]) => keep(key, value));\n return kept.length === map.size ? map : new Map(kept);\n};\n\nconst keepIds = (set: ReadonlySet<string>, keep: (id: string) => boolean): ReadonlySet<string> => {\n const kept = [...set].filter(keep);\n return kept.length === set.size ? set : new Set(kept);\n};\n\nconst sameEntries = <K, V>(a: ReadonlyMap<K, V>, b: ReadonlyMap<K, V>): boolean =>\n a.size === b.size && [...a].every(([key, value]) => b.get(key) === value);\n\nconst reconcileSelection = (\n selection: ReadonlyMap<ChainPlatform, string>,\n pool: ReadonlyMap<string, ConnectedWallet>,\n): ReadonlyMap<ChainPlatform, string> => {\n const next = new Map<ChainPlatform, string>();\n for (const [platform, id] of selection) {\n if (pool.get(id)?.connector.chainPlatform === platform) {\n next.set(platform, id);\n }\n }\n for (const [id, wallet] of pool) {\n if (!next.has(wallet.connector.chainPlatform)) {\n next.set(wallet.connector.chainPlatform, id);\n }\n }\n return sameEntries(next, selection) ? selection : next;\n};\n\n/**\n * The invariants every event relies on, enforced once after every event so\n * no case re-derives a fallback. Returns `state` itself when it already\n * holds, which keeps zustand from notifying.\n */\nconst reconcile = (state: WalletState): WalletState => {\n const { pool } = state;\n const reconnectingIds = keepIds(state.reconnectingIds, (id) => pool.has(id));\n const dormant = keepEntries(state.dormant, (id) => !pool.has(id) || reconnectingIds.has(id));\n const selection = reconcileSelection(state.selection, pool);\n const activeConnectorId =\n state.activeConnectorId !== null && pool.has(state.activeConnectorId)\n ? state.activeConnectorId\n : (pool.keys().next().value ?? null);\n\n if (\n reconnectingIds === state.reconnectingIds &&\n dormant === state.dormant &&\n selection === state.selection &&\n activeConnectorId === state.activeConnectorId\n ) {\n return state;\n }\n return { ...state, activeConnectorId, dormant, reconnectingIds, selection };\n};\n\nconst applyStoredSelection = (\n selection: ReadonlyMap<ChainPlatform, string>,\n stored: StoredSelectionRecord,\n pool: ReadonlyMap<string, ConnectedWallet>,\n): ReadonlyMap<ChainPlatform, string> => {\n let next = selection;\n for (const [platform, id] of Object.entries(stored)) {\n if (isChainPlatform(platform) && pool.get(id)?.connector.chainPlatform === platform) {\n next = withEntry(next, platform, id);\n }\n }\n return next;\n};\n\nconst changeAccounts = (\n state: WalletState,\n event: Extract<WalletEvent, { type: \"ACCOUNTS_CHANGED\" }>,\n): WalletState => {\n const wallet = state.pool.get(event.connectorId);\n const first = event.accounts.at(0);\n if (wallet === undefined || first === undefined) {\n return state;\n }\n const account = event.active ?? event.accounts.find((a) => a.id === wallet.account.id) ?? first;\n if (account.id === wallet.account.id && accountsEqual(event.accounts, wallet.accounts)) {\n return state;\n }\n const updated: ConnectedWallet = { ...wallet, account, accounts: event.accounts };\n return { ...state, pool: withEntry(state.pool, event.connectorId, updated) };\n};\n\nconst apply = (state: WalletState, event: WalletEvent): WalletState => {\n switch (event.type) {\n case \"ADAPTER_REGISTERED\": {\n if (state.adapters.some((adapter) => adapter.id === event.adapter.id)) {\n return state;\n }\n return { ...state, adapters: [...state.adapters, event.adapter] };\n }\n\n case \"STORAGE_LOADED\": {\n const dormant = new Map(state.dormant);\n for (const [id, entry] of Object.entries(event.entries)) {\n if (entry !== undefined) {\n dormant.set(id, entry);\n }\n }\n return { ...state, dormant, isUserDisconnected: event.isUserDisconnected };\n }\n\n case \"ENTRY_RESTORED\": {\n // No longer dormant means the user connected or disconnected it while\n // the silent reconnect was in flight; their action wins.\n if (!state.dormant.has(event.connectorId)) {\n return state;\n }\n return {\n ...state,\n pool: withEntry(state.pool, event.connectorId, event.entry),\n reconnectingIds: withoutId(state.reconnectingIds, event.connectorId),\n };\n }\n\n case \"RESTORE_FAILED\": {\n // A failed seed is still a shadow whose every method rejects; leaving it\n // would strand a permanently reconnecting wallet in the pool.\n if (!state.reconnectingIds.has(event.connectorId)) {\n return state;\n }\n return {\n ...state,\n pool: withoutKey(state.pool, event.connectorId),\n reconnectingIds: withoutId(state.reconnectingIds, event.connectorId),\n };\n }\n\n case \"HYDRATED\": {\n const preferred = event.activeConnectorId;\n return {\n ...state,\n activeConnectorId:\n preferred !== null && state.pool.has(preferred) ? preferred : state.activeConnectorId,\n isHydrated: true,\n selection: applyStoredSelection(state.selection, event.selection, state.pool),\n };\n }\n\n case \"CONNECT_STARTED\": {\n return {\n ...state,\n connectingConnectorId: event.connectorId,\n connectionError: null,\n connectionStatus: \"connecting\",\n isUserDisconnected: false,\n };\n }\n\n case \"CONNECT_SUCCEEDED\": {\n const { connectorId, entry } = event;\n const isCurrentAttempt = state.connectingConnectorId === connectorId;\n return {\n ...state,\n activeConnectorId: connectorId,\n ...(isCurrentAttempt && {\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"success\" as const,\n }),\n pool: withEntry(state.pool, connectorId, entry),\n reconnectingIds: withoutId(state.reconnectingIds, connectorId),\n // A reconnect of a wallet already in the pool must not steal the\n // platform from whichever wallet the user selected.\n selection: state.pool.has(connectorId)\n ? state.selection\n : withEntry(state.selection, entry.connector.chainPlatform, connectorId),\n };\n }\n\n case \"CONNECT_FAILED\": {\n if (state.connectingConnectorId !== event.connectorId) {\n return state;\n }\n return {\n ...state,\n connectingConnectorId: null,\n connectionError: event.error,\n connectionStatus: \"error\",\n };\n }\n\n case \"STATUS_RESET\": {\n if (state.connectionStatus === \"idle\") {\n return state;\n }\n return {\n ...state,\n connectingConnectorId: null,\n connectionError: null,\n connectionStatus: \"idle\",\n };\n }\n\n case \"ACCOUNTS_CHANGED\": {\n return changeAccounts(state, event);\n }\n\n case \"DISCONNECTED\": {\n const pool = withoutKey(state.pool, event.connectorId);\n if (event.byUser) {\n // Disconnecting the last live wallet forgets everything, including\n // connections that were only waiting to be retried.\n const dormant =\n pool.size === 0\n ? keepEntries(state.dormant, () => false)\n : withoutKey(state.dormant, event.connectorId);\n if (pool === state.pool && dormant === state.dormant && state.isUserDisconnected) {\n return state;\n }\n return { ...state, dormant, isUserDisconnected: true, pool };\n }\n const wallet = state.pool.get(event.connectorId);\n if (wallet === undefined) {\n return state;\n }\n // The wallet ended the session (locked, removed): keep it persisted so\n // the next load can retry it.\n const stored = toStoredEntry(event.connectorId, wallet);\n return { ...state, dormant: withEntry(state.dormant, event.connectorId, stored), pool };\n }\n\n case \"RESET\": {\n return {\n ...initialState,\n adapters: state.adapters,\n isHydrated: state.isHydrated,\n isUserDisconnected: true,\n };\n }\n\n case \"ACTIVE_CHANGED\": {\n if (state.activeConnectorId === event.connectorId || !state.pool.has(event.connectorId)) {\n return state;\n }\n return { ...state, activeConnectorId: event.connectorId };\n }\n\n case \"SELECTION_CHANGED\": {\n if (\n state.selection.get(event.chainPlatform) === event.connectorId ||\n state.pool.get(event.connectorId)?.connector.chainPlatform !== event.chainPlatform\n ) {\n return state;\n }\n return {\n ...state,\n selection: withEntry(state.selection, event.chainPlatform, event.connectorId),\n };\n }\n\n default: {\n const exhaustive: never = event;\n void exhaustive;\n return state;\n }\n }\n};\n\n/** Pure: `(state, event) => state`. Side effects live in the manager. */\nconst reducer = (state: WalletState, event: WalletEvent): WalletState => {\n const next = apply(state, event);\n return next === state ? state : reconcile(next);\n};\n\n/**\n * `isHydrated` is true from the first render on this path, so\n * `reconnectingIds` says whether a connection is verified. Seeded entries\n * are also dormant: persisted, not yet live.\n */\nconst stateFromSnapshot = (snapshot: WalletSnapshot): WalletState => {\n const pool = new Map<string, ConnectedWallet>();\n const dormant = new Map<string, StoredPoolEntry>();\n for (const [connectorId, entry] of Object.entries(snapshot.pool)) {\n if (entry !== undefined) {\n pool.set(connectorId, {\n account: entry.account,\n accounts: entry.accounts,\n connector: createShadowAdapter(entry),\n });\n dormant.set(connectorId, entry);\n }\n }\n return reconcile({\n ...initialState,\n activeConnectorId: snapshot.activeConnectorId,\n dormant,\n isHydrated: true,\n pool,\n reconnectingIds: new Set(pool.keys()),\n selection: applyStoredSelection(new Map(), snapshot.selection, pool),\n });\n};\n\nexport type { ConnectStatus, WalletEvent, WalletState };\nexport { initialState, reconcile, reducer, stateFromSnapshot, toStoredEntry };\n","import type { StoreApi } from \"zustand/vanilla\";\nimport { createStore } from \"zustand/vanilla\";\n\nimport { logError, logWarn } from \"../logger\";\nimport type {\n PersistedWalletState,\n StoredPoolRecord,\n WalletSnapshot,\n} from \"../storage/persistence\";\nimport { createWalletStorage } from \"../storage/wallet-storage\";\nimport type {\n Account,\n ChainPlatform,\n ConnectedWallet,\n HydrationOutcome,\n WalletAdapter,\n WalletManagerConfig,\n} from \"../types\";\nimport { ConnectionError, toConnectionError } from \"../types/errors\";\n\nimport { createConnectorLifecycle } from \"./connector-lifecycle\";\nimport type { WalletEvent, WalletState } from \"./reducer\";\nimport { initialState, reducer, stateFromSnapshot, toStoredEntry } from \"./reducer\";\n\nconst CONNECT_TIMEOUT_MS = 90_000;\n// A silent reconnect needs no user, so it is short. Some wallets ignore\n// `silent` and prompt; without a bound they would hold `isHydrated` false\n// until the user answers a popup they never asked for.\nconst RESTORE_TIMEOUT_MS = 15_000;\nconst DEFAULT_SLOW_CONNECT_THRESHOLD_MS = 5000;\n\ntype WalletManager = Pick<StoreApi<WalletState>, \"getInitialState\" | \"getState\" | \"subscribe\"> & {\n /** Clears `connectionError` and returns `connectionStatus` to idle. */\n clearConnectionError: () => void;\n /** Resolves the connected wallet; rejects with a `ConnectionError`. */\n connect: (connectorId: string) => Promise<ConnectedWallet>;\n disconnect: (connectorId: string) => void;\n /** Disconnects every wallet and forgets every persisted connection. */\n disconnectAll: () => void;\n /** Opens the wallet's account picker, then refreshes the pool entry. A no-op\n * for wallets without `requestAccounts`. */\n requestAccounts: (connectorId: string) => Promise<void>;\n /** Selects an exposed account without changing its chain or the account\n * list. Unknown wallets and accounts are ignored. */\n setAccount: (connectorId: string, account: Account) => void;\n setActive: (connectorId: string) => void;\n setSelection: (chainPlatform: ChainPlatform, connectorId: string) => void;\n /**\n * Subscribes the sources, hydrates persisted connections (once per\n * manager) and bridges wallet events. The returned function undoes all of\n * it except hydration, so a StrictMode remount is safe.\n */\n start: () => () => void;\n};\n\ntype RestoreResult =\n | { connectorId: string; ok: true }\n | { connectorId: string; error: ConnectionError; ok: false };\n\nconst EMPTY_PERSISTED: PersistedWalletState = {\n activeConnectorId: null,\n isUserDisconnected: false,\n pool: {},\n selection: {},\n};\n\nconst toPersistedState = (state: WalletState): PersistedWalletState => {\n const pool: StoredPoolRecord = Object.fromEntries(state.dormant);\n for (const [connectorId, wallet] of state.pool) {\n pool[connectorId] = toStoredEntry(connectorId, wallet);\n }\n return {\n activeConnectorId: state.activeConnectorId,\n isUserDisconnected: state.isUserDisconnected,\n pool,\n selection: Object.fromEntries(state.selection),\n };\n};\n\nconst persistedSliceChanged = (state: WalletState, prev: WalletState): boolean =>\n state.pool !== prev.pool ||\n state.dormant !== prev.dormant ||\n state.selection !== prev.selection ||\n state.activeConnectorId !== prev.activeConnectorId ||\n state.isUserDisconnected !== prev.isUserDisconnected;\n\nconst toError = (cause: unknown): Error =>\n cause instanceof Error ? cause : new Error(String(cause));\n\n/** A consumer callback that throws must not break the manager. */\nconst safely = <Args extends ReadonlyArray<unknown>>(\n name: string,\n callback: ((...args: Args) => void) | undefined,\n ...args: Args\n) => {\n try {\n callback?.(...args);\n } catch (error) {\n logWarn(`[butr] ${name} threw:`, error);\n }\n};\n\nconst withTimeout = async <T>(promise: Promise<T>, ms: number): Promise<T> => {\n // oxlint-disable-next-line promise/prefer-await-to-then -- the race below may abandon it; keep its rejection handled\n void promise.catch(() => {});\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new ConnectionError(\"Timeout\", `Connection timed out after ${ms / 1000}s`));\n }, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n};\n\nconst teardown = async (connector: WalletAdapter) => {\n try {\n await connector.disconnect?.();\n } catch (error) {\n logWarn(`[butr] disconnect of ${connector.id} failed:`, error);\n }\n};\n\n/**\n * Framework-free: React binds it through `WalletManagerProvider`, anything\n * else calls `start()` itself. Creating one has no side effects, so it is\n * safe during a server render.\n */\nconst createWalletManager = (\n config: WalletManagerConfig = {},\n options: { initialState?: WalletSnapshot } = {},\n): WalletManager => {\n const storage = config.storage ?? createWalletStorage({ keyPrefix: config.storageKeyPrefix });\n const store = createStore<WalletState>()(() =>\n options.initialState ? stateFromSnapshot(options.initialState) : initialState,\n );\n const { getState } = store;\n\n const dispatch = (event: WalletEvent) => {\n store.setState((state) => reducer(state, event), true);\n };\n\n const findAdapter = (connectorId: string) =>\n getState().adapters.find((adapter) => adapter.id === connectorId);\n\n const reportStorageError = (cause: unknown) => {\n if (config.onStorageError) {\n safely(\"onStorageError\", config.onStorageError, toError(cause));\n } else {\n logWarn(\"[butr] wallet persistence failed:\", cause);\n }\n };\n\n // Persistence: a pure function of state, written after every change once\n // hydration has loaded what was there before. Saves are coalesced so an\n // async driver never writes an older state after a newer one.\n let persistEnabled = false;\n let flushing = false;\n let dirty = false;\n const persist = async () => {\n dirty = true;\n if (flushing) {\n return;\n }\n flushing = true;\n while (dirty) {\n dirty = false;\n try {\n await storage.save(toPersistedState(getState()));\n } catch (error) {\n reportStorageError(error);\n }\n }\n flushing = false;\n };\n store.subscribe((state, prev) => {\n if (persistEnabled && persistedSliceChanged(state, prev)) {\n void persist();\n }\n });\n\n const lifecycle = createConnectorLifecycle({\n onAccountsChanged: (connectorId, accounts) => {\n dispatch({ accounts, active: accounts[0], connectorId, type: \"ACCOUNTS_CHANGED\" });\n },\n onDisconnected: (connectorId) => {\n const wallet = getState().pool.get(connectorId);\n if (wallet === undefined) {\n return;\n }\n // Discovery caches the adapter and hands it back on the next connect,\n // so it must not be reused while still holding the ended session.\n void teardown(wallet.connector);\n dispatch({ byUser: false, connectorId, type: \"DISCONNECTED\" });\n safely(\"onDisconnect\", config.onDisconnect, wallet, { byUser: false });\n },\n });\n\n // Hydration: silent reconnect of every dormant entry whose adapter exists,\n // then of each late adapter the moment a source announces it.\n const attempted = new Set<string>();\n let loaded = false;\n let hydration: Promise<void> | null = null;\n // Set once the user connects or picks a wallet: the stored active wallet\n // and selection are older than that choice, so hydration must not apply\n // them over it.\n let userChose = false;\n\n const choose = (event: WalletEvent) => {\n const before = getState();\n dispatch(event);\n if (getState() !== before) {\n userChose = true;\n }\n };\n\n const restore = async (connectorId: string): Promise<RestoreResult | null> => {\n const adapter = findAdapter(connectorId);\n if (\n adapter === undefined ||\n attempted.has(connectorId) ||\n !getState().dormant.has(connectorId)\n ) {\n return null;\n }\n attempted.add(connectorId);\n try {\n await withTimeout(adapter.connect({ silent: true }), RESTORE_TIMEOUT_MS);\n const accounts = await adapter.getAccounts();\n const account = accounts.at(0);\n if (account === undefined) {\n throw new ConnectionError(\"NotConnected\", `${adapter.name} exposed no accounts`);\n }\n const wallet: ConnectedWallet = { account, accounts, connector: adapter };\n const before = getState();\n dispatch({ connectorId, entry: wallet, type: \"ENTRY_RESTORED\" });\n if (getState() === before) {\n // The user acted on this wallet mid-reconnect. If they disconnected\n // it, close the session the silent reconnect just reopened.\n if (!before.pool.has(connectorId)) {\n await teardown(adapter);\n }\n return null;\n }\n safely(\"onConnect\", config.onConnect, wallet, { reconnected: true });\n return { connectorId, ok: true };\n } catch (error) {\n dispatch({ connectorId, type: \"RESTORE_FAILED\" });\n return { connectorId, error: toConnectionError(error), ok: false };\n }\n };\n\n const restoreLate = async (connectorId: string) => {\n const result = await restore(connectorId);\n if (result?.ok === false) {\n logWarn(`[butr] silent reconnect failed for ${connectorId}:`, result.error);\n }\n };\n\n const hydrate = async () => {\n let persisted = EMPTY_PERSISTED;\n try {\n persisted = await storage.load();\n } catch (error) {\n reportStorageError(error);\n }\n dispatch({\n entries: persisted.pool,\n isUserDisconnected: persisted.isUserDisconnected,\n type: \"STORAGE_LOADED\",\n });\n loaded = true;\n\n const ids = [...getState().dormant.keys()];\n const restores = ids.map(restore);\n // Read before awaiting: an adapter announced mid-pass is restored by\n // `register`, and still belongs in the outcome as pending.\n const pendingIds = ids.filter((id) => !attempted.has(id));\n const settled = await Promise.all(restores);\n const results = settled.filter((result) => result !== null);\n dispatch({\n activeConnectorId: userChose ? null : persisted.activeConnectorId,\n selection: userChose ? {} : persisted.selection,\n type: \"HYDRATED\",\n });\n persistEnabled = true;\n void persist();\n\n const outcome: HydrationOutcome = {\n dropped: results.flatMap((result) =>\n result.ok ? [] : [{ connectorId: result.connectorId, reason: result.error }],\n ),\n pendingIds,\n restoredIds: results.flatMap((result) => (result.ok ? [result.connectorId] : [])),\n };\n if (config.onHydrated) {\n safely(\"onHydrated\", config.onHydrated, outcome);\n } else {\n for (const { connectorId, reason } of outcome.dropped) {\n logWarn(`[butr] silent reconnect failed for ${connectorId}:`, reason);\n }\n }\n };\n\n const register = (adapter: WalletAdapter) => {\n const before = getState();\n dispatch({ adapter, type: \"ADAPTER_REGISTERED\" });\n if (loaded && getState() !== before) {\n void restoreLate(adapter.id);\n }\n };\n\n const subscribeSource = (source: NonNullable<WalletManagerConfig[\"sources\"]>[number]) => {\n try {\n return source(register);\n } catch (error) {\n logError(\"[butr] a wallet source threw on subscribe:\", error);\n return () => {};\n }\n };\n\n return {\n clearConnectionError: () => {\n dispatch({ type: \"STATUS_RESET\" });\n },\n\n connect: async (connectorId) => {\n dispatch({ connectorId, type: \"CONNECT_STARTED\" });\n const slowTimer = config.onSlowConnect\n ? setTimeout(() => {\n safely(\"onSlowConnect\", config.onSlowConnect, connectorId);\n }, config.slowConnectThresholdMs ?? DEFAULT_SLOW_CONNECT_THRESHOLD_MS)\n : undefined;\n const connector = findAdapter(connectorId);\n try {\n if (connector === undefined) {\n throw new ConnectionError(\n \"WalletNotFound\",\n `No wallet adapter is registered as \"${connectorId}\"`,\n );\n }\n await withTimeout(connector.connect(), CONNECT_TIMEOUT_MS);\n const accounts = await connector.getAccounts();\n const account = accounts.at(0);\n if (account === undefined) {\n throw new ConnectionError(\"NotConnected\", `${connector.name} exposed no accounts`);\n }\n const wallet: ConnectedWallet = { account, accounts, connector };\n choose({ connectorId, entry: wallet, type: \"CONNECT_SUCCEEDED\" });\n safely(\"onConnect\", config.onConnect, wallet, { reconnected: false });\n return wallet;\n } catch (error) {\n const failure = toConnectionError(error);\n dispatch({ connectorId, error: failure, type: \"CONNECT_FAILED\" });\n // Leave an already-live session alone: a failed re-connect of a\n // connected wallet must not disconnect it.\n if (connector !== undefined && getState().pool.get(connectorId)?.connector !== connector) {\n await teardown(connector);\n }\n safely(\"onConnectError\", config.onConnectError, failure, connectorId);\n throw failure;\n } finally {\n clearTimeout(slowTimer);\n }\n },\n\n disconnect: (connectorId) => {\n const wallet = getState().pool.get(connectorId);\n dispatch({ byUser: true, connectorId, type: \"DISCONNECTED\" });\n if (wallet !== undefined) {\n void teardown(wallet.connector);\n safely(\"onDisconnect\", config.onDisconnect, wallet, { byUser: true });\n }\n },\n\n disconnectAll: () => {\n const wallets = [...getState().pool.values()];\n dispatch({ type: \"RESET\" });\n for (const wallet of wallets) {\n void teardown(wallet.connector);\n safely(\"onDisconnect\", config.onDisconnect, wallet, { byUser: true });\n }\n },\n\n getInitialState: store.getInitialState,\n getState,\n\n requestAccounts: async (connectorId) => {\n const connector = getState().pool.get(connectorId)?.connector;\n if (connector?.requestAccounts === undefined) {\n return;\n }\n await connector.requestAccounts();\n const accounts = await connector.getAccounts();\n dispatch({ accounts, connectorId, type: \"ACCOUNTS_CHANGED\" });\n },\n\n setAccount: (connectorId, account) => {\n const wallet = getState().pool.get(connectorId);\n const active = wallet?.accounts.find((candidate) => candidate.id === account.id);\n if (wallet === undefined || active === undefined) {\n return;\n }\n dispatch({ accounts: wallet.accounts, active, connectorId, type: \"ACCOUNTS_CHANGED\" });\n },\n\n setActive: (connectorId) => {\n choose({ connectorId, type: \"ACTIVE_CHANGED\" });\n },\n\n setSelection: (chainPlatform, connectorId) => {\n choose({ chainPlatform, connectorId, type: \"SELECTION_CHANGED\" });\n },\n\n start: () => {\n const unsubscribes = (config.sources ?? []).map(subscribeSource);\n lifecycle.sync(getState().pool);\n const unsubscribePool = store.subscribe((state, prev) => {\n if (state.pool !== prev.pool) {\n lifecycle.sync(state.pool);\n }\n });\n hydration ??= hydrate();\n return () => {\n for (const unsubscribe of unsubscribes) {\n try {\n unsubscribe();\n } catch (error) {\n logWarn(\"[butr] a wallet source threw on unsubscribe:\", error);\n }\n }\n unsubscribePool();\n lifecycle.detachAll();\n };\n },\n\n subscribe: store.subscribe,\n };\n};\n\nexport type { WalletManager };\nexport { createWalletManager };\n","import type { StorageDriver } from \"./persistence\";\n\ntype InitialCookies = Iterable<[string, string]> | Readonly<Record<string, string>>;\n\ntype CookieDriverOptions = {\n /**\n * Cookie `domain` attribute. Omit to scope to the current host.\n * Set to a parent domain (e.g. `.example.com`) to share state\n * across subdomains.\n */\n domain?: string;\n /**\n * Server-side cookie snapshot (Next.js' `cookies()`, a parsed\n * `req.headers.cookie`, …) read only when `document` is unavailable,\n * so the SSR pass sees the state the client will hydrate with.\n */\n initialCookies?: InitialCookies;\n /**\n * Lifetime in seconds. Defaults to 30 days.\n */\n maxAgeSeconds?: number;\n /**\n * URL path the cookie applies to. Defaults to `/` (whole origin).\n */\n path?: string;\n /**\n * `SameSite` attribute. Defaults to `\"lax\"`; restrictive enough\n * to avoid CSRF on cross-origin POSTs, permissive enough for\n * top-level navigations.\n */\n sameSite?: \"lax\" | \"strict\" | \"none\";\n /**\n * `Secure` attribute. Defaults to `true` (cookies only sent over\n * HTTPS). Force `false` for local development on plain `http://`.\n */\n secure?: boolean;\n};\n\nconst DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;\n\nconst encode = (value: string): string => encodeURIComponent(value);\nconst decode = (value: string): string => decodeURIComponent(value);\n\nconst readCookies = (): Map<string, string> => {\n const out = new Map<string, string>();\n if (typeof document === \"undefined\") {\n return out;\n }\n const cookieString = document.cookie;\n if (!cookieString) {\n return out;\n }\n for (const segment of cookieString.split(\";\")) {\n const trimmed = segment.trim();\n if (!trimmed) {\n continue;\n }\n const eq = trimmed.indexOf(\"=\");\n if (eq === -1) {\n continue;\n }\n const name = trimmed.slice(0, eq);\n const raw = trimmed.slice(eq + 1);\n try {\n out.set(name, decode(raw));\n } catch {\n void 0;\n }\n }\n return out;\n};\n\nconst buildCookieString = (\n name: string,\n value: string,\n options: CookieDriverOptions,\n expired = false,\n): string => {\n const parts: Array<string> = [`${name}=${encode(value)}`, `Path=${options.path ?? \"/\"}`];\n if (options.domain !== undefined && options.domain !== \"\") {\n parts.push(`Domain=${options.domain}`);\n }\n if (expired) {\n parts.push(\"Max-Age=0\");\n } else {\n const maxAge = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;\n parts.push(`Max-Age=${maxAge}`);\n }\n parts.push(`SameSite=${options.sameSite ?? \"lax\"}`);\n if (options.secure ?? true) {\n parts.push(\"Secure\");\n }\n return parts.join(\"; \");\n};\n\nconst toCookieMap = (input: InitialCookies | undefined): Map<string, string> | null => {\n if (!input) {\n return null;\n }\n if (Symbol.iterator in input) {\n return new Map(input);\n }\n return new Map(Object.entries(input));\n};\n\n/**\n * Server-side writes are no-ops: `Set-Cookie` needs the framework's\n * response object. Cookies ride every request, so use this driver for\n * the `persistent` slot only and leave `session` on `sessionStorage`.\n */\nconst createCookieStorageDriver = (options: CookieDriverOptions = {}): StorageDriver => {\n const seeded = toCookieMap(options.initialCookies);\n\n if (typeof document === \"undefined\") {\n if (!seeded) {\n return {\n getItem() {\n return null;\n },\n removeItem() {},\n setItem() {},\n };\n }\n return {\n getItem(key) {\n return seeded.get(key) ?? null;\n },\n removeItem() {},\n setItem() {},\n };\n }\n return {\n getItem(key) {\n return readCookies().get(key) ?? null;\n },\n removeItem(key) {\n // eslint-disable-next-line unicorn/no-document-cookie -- cookie storage driver is the document.cookie abstraction by design\n document.cookie = buildCookieString(key, \"\", options, true);\n },\n setItem(key, value) {\n // eslint-disable-next-line unicorn/no-document-cookie -- cookie storage driver is the document.cookie abstraction by design\n document.cookie = buildCookieString(key, value, options);\n },\n };\n};\n\nexport type { CookieDriverOptions, InitialCookies };\nexport { createCookieStorageDriver };\n","import type { StoredPoolRecord, StoredSelectionRecord, WalletSnapshot } from \"./persistence\";\nimport { decodePool, decodeSelection, storageKeys } from \"./validation\";\n\ntype CookieSource =\n | Iterable<{ name: string; value: string }>\n | Iterable<[string, string]>\n | Readonly<Record<string, string>>;\n\ntype SnapshotOptions = {\n /**\n * Same prefix passed as `storageKeyPrefix` / to `createWalletStorage`.\n * Defaults to `\"butr\"` to match the library default.\n */\n keyPrefix?: string;\n};\n\nconst EMPTY_POOL: StoredPoolRecord = Object.freeze({});\nconst EMPTY_SELECTION: StoredSelectionRecord = Object.freeze({});\n\nconst EMPTY_SNAPSHOT: WalletSnapshot = Object.freeze({\n activeConnectorId: null,\n pool: EMPTY_POOL,\n selection: EMPTY_SELECTION,\n});\n\nconst toCookieMap = (input: CookieSource): Map<string, string> => {\n if (!(Symbol.iterator in input)) {\n return new Map(Object.entries(input));\n }\n const out = new Map<string, string>();\n for (const entry of input) {\n if (Array.isArray(entry)) {\n const [name, value] = entry;\n if (typeof name === \"string\" && typeof value === \"string\") {\n out.set(name, value);\n }\n continue;\n }\n if (typeof entry.name === \"string\" && typeof entry.value === \"string\") {\n out.set(entry.name, entry.value);\n }\n }\n return out;\n};\n\nconst SNAPSHOT_LABEL = \"[butr] readWalletSnapshot:\";\n\n/**\n * Optimistic: only what the browser last persisted, so an uninstall or\n * other-tab disconnect makes it stale. Authoritative once the entry\n * leaves `reconnectingIds`; `isHydrated` is true from render one.\n */\nconst readWalletSnapshot = (\n source: CookieSource,\n options: SnapshotOptions = {},\n): WalletSnapshot => {\n const keys = storageKeys(options.keyPrefix);\n const cookies = toCookieMap(source);\n\n const pool = decodePool(cookies.get(keys.pool), SNAPSHOT_LABEL);\n const selection = decodeSelection(cookies.get(keys.selection), SNAPSHOT_LABEL);\n\n const rawActive = cookies.get(keys.active);\n let activeConnectorId: string | null = null;\n if (rawActive !== undefined && rawActive.length > 0 && pool[rawActive] !== undefined) {\n activeConnectorId = rawActive;\n } else {\n const firstKey = Object.keys(pool)[0];\n if (firstKey !== undefined && firstKey !== \"\") {\n activeConnectorId = firstKey;\n }\n }\n\n return { activeConnectorId, pool, selection };\n};\n\nexport type { CookieSource, SnapshotOptions };\nexport { EMPTY_SNAPSHOT, readWalletSnapshot };\n","import type { ChainPlatform } from \"./types/platform\";\nimport { CHAIN_PLATFORMS } from \"./types/platform\";\n\n/**\n * A multi-chain wallet announces one adapter per platform, so brands\n * repeat in the flat list. Keys follow `CHAIN_PLATFORMS` order and empty\n * platforms are omitted, so `[...groups]` needs no emptiness filter.\n */\nconst groupByPlatform = <T>(\n items: ReadonlyArray<T>,\n getPlatform: (item: T) => ChainPlatform,\n): Map<ChainPlatform, Array<T>> => {\n const buckets = new Map<ChainPlatform, Array<T>>();\n for (const item of items) {\n const platform = getPlatform(item);\n const bucket = buckets.get(platform);\n if (bucket === undefined) {\n buckets.set(platform, [item]);\n } else {\n bucket.push(item);\n }\n }\n\n const ordered = new Map<ChainPlatform, Array<T>>();\n for (const platform of CHAIN_PLATFORMS) {\n const bucket = buckets.get(platform);\n if (bucket !== undefined) {\n ordered.set(platform, bucket);\n }\n }\n return ordered;\n};\n\nexport { groupByPlatform };\n","/**\n * Hex prefixing diverges by chain (bare for Bitcoin and Ledger, `0x` for\n * EVM and Polkadot), so the variants stay explicit: these sit on the\n * signing path, where a silent mismatch corrupts one chain's signatures.\n */\n\n/** Bitcoin/Solana alphabet; omits the visually ambiguous `0`, `O`, `I`, `l`. */\nconst BASE58_ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\nconst BASE58_INDEX = new Map<string, number>();\nfor (let i = 0; i < BASE58_ALPHABET.length; i += 1) {\n BASE58_INDEX.set(BASE58_ALPHABET[i] ?? \"\", i);\n}\n\n/** Bare lowercase hex, no `0x` prefix (Bitcoin, Ledger). */\nconst bytesToHex = (bytes: Uint8Array): string => {\n let hex = \"\";\n for (const byte of bytes) {\n hex += byte.toString(16).padStart(2, \"0\");\n }\n return hex;\n};\n\n/** `0x`-prefixed lowercase hex (EVM, Polkadot). */\nconst bytesToHexPrefixed = (bytes: Uint8Array): string => `0x${bytesToHex(bytes)}`;\n\n/**\n * Decode hex (with or without a leading `0x`) into raw bytes. Throws on\n * odd-length input or non-hex characters rather than silently producing\n * `NaN`-filled bytes.\n */\nconst hexToBytes = (hex: string): Uint8Array => {\n const clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex;\n if (clean.length % 2 !== 0) {\n throw new TypeError(\"Invalid hex: odd length\");\n }\n const out = new Uint8Array(clean.length / 2);\n for (let i = 0; i < out.length; i += 1) {\n const byte = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n if (Number.isNaN(byte)) {\n throw new TypeError(`Invalid hex character at offset ${i * 2}`);\n }\n out[i] = byte;\n }\n return out;\n};\n\n/**\n * Cross-platform base64 → `Uint8Array`. `atob` is available everywhere\n * butr runs (browsers, RN since Hermes, Node 16+, Bun, Deno).\n */\nconst base64ToBytes = (b64: string): Uint8Array => {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n out[i] = binary.codePointAt(i) ?? 0;\n }\n return out;\n};\n\n/** Cross-platform `Uint8Array` → base64. */\nconst bytesToBase64 = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const byte of bytes) {\n binary += String.fromCodePoint(byte);\n }\n return btoa(binary);\n};\n\n/**\n * `Uint8Array` → base58 (Solana addresses and signatures). Leading zero\n * bytes are significant in base58 and survive the BigInt round-trip only\n * because they're re-prefixed as `1`s afterwards.\n */\nconst bytesToBase58 = (bytes: Uint8Array): string => {\n let intVal = 0n;\n for (const byte of bytes) {\n intVal = (intVal << 8n) | BigInt(byte);\n }\n let out = \"\";\n while (intVal > 0n) {\n const remainder = intVal % 58n;\n intVal /= 58n;\n out = BASE58_ALPHABET[Number(remainder)] + out;\n }\n for (const byte of bytes) {\n if (byte !== 0) {\n break;\n }\n out = `1${out}`;\n }\n return out;\n};\n\n/** Decode base58 into raw bytes. Throws on characters outside the alphabet\n * rather than silently dropping them. */\nconst base58ToBytes = (input: string): Uint8Array => {\n let intVal = 0n;\n let leadingZeros = 0;\n let stillLeading = true;\n for (const char of input) {\n if (stillLeading && char === \"1\") {\n leadingZeros += 1;\n } else {\n stillLeading = false;\n }\n const digit = BASE58_INDEX.get(char);\n if (digit === undefined) {\n throw new Error(`Invalid base58 character \"${char}\"`);\n }\n intVal = intVal * 58n + BigInt(digit);\n }\n const bytes: Array<number> = [];\n const byteMask = 255n;\n while (intVal > 0n) {\n bytes.unshift(Number(intVal & byteMask));\n intVal >>= 8n;\n }\n for (let i = 0; i < leadingZeros; i += 1) {\n bytes.unshift(0);\n }\n return Uint8Array.from(bytes);\n};\n\nexport {\n base58ToBytes,\n base64ToBytes,\n bytesToBase58,\n bytesToBase64,\n bytesToHex,\n bytesToHexPrefixed,\n hexToBytes,\n};\n","import { bytesToBase64 } from \"../encoding/bytes\";\nimport type { Account, ConnectedWallet } from \"../types\";\n\n/**\n * Verify against `signedMessage`, not `message`: Solana Wallet Standard\n * wallets may prefix or re-encode what they sign. Base64 mirrors exist\n * because `Uint8Array` does not survive `JSON.stringify`.\n */\ntype SignInResult = {\n account: Account;\n /** The message handed to the wallet. Absent on the SIWS path, where the\n * wallet composes the message itself. */\n message?: string;\n nonce: string;\n signature: Uint8Array;\n /** Base64 of `signature`. */\n signatureBase64: string;\n signedMessage: Uint8Array;\n /** Base64 of `signedMessage`; verify against this. */\n signedMessageBase64: string;\n wallet: ConnectedWallet;\n};\n\ntype SignInMessageContext = {\n account: Account;\n nonce: string;\n wallet: ConnectedWallet;\n};\n\ntype SignInFlowOptions = {\n /**\n * butr ships no wire format: a server-parsed message is an auth spec,\n * and SIWE / SIWS already fill that role. Pass what your backend\n * expects. Unused on the SIWS path, where the wallet composes it.\n */\n buildMessage?: (ctx: SignInMessageContext) => string;\n /** Fetch a single-use nonce from your backend. */\n getNonce: (ctx: { account: Account; wallet: ConnectedWallet }) => Promise<string>;\n /**\n * Skip the Sign In With Solana path even on wallets that advertise it,\n * forcing every platform down the same `signMessage` route. Useful when\n * one backend verifier has to handle every chain identically.\n */\n preferSignMessage?: boolean;\n /** Hand the signed result to your backend. Throw to fail the flow. */\n verify: (result: SignInResult) => Promise<void>;\n};\n\ntype SignInFlow = {\n signIn: (wallet: ConnectedWallet, account?: Account) => Promise<SignInResult>;\n};\n\n/** Thrown before any wallet interaction when the wallet can't sign at\n * all. Distinct from a rejection: nothing was asked of the user, so UI\n * should say \"this wallet can't sign in\" rather than \"you declined\". */\nclass SignInUnsupportedError extends Error {\n readonly connectorId: string;\n\n constructor(connectorId: string) {\n super(`Wallet \"${connectorId}\" cannot sign messages, so it cannot sign in.`);\n this.connectorId = connectorId;\n this.name = \"SignInUnsupportedError\";\n }\n}\n\nconst defaultBuildMessage = ({ account, nonce }: SignInMessageContext): string =>\n `${account.walletAddress} signs in.\\nNonce: ${nonce}`;\n\n/**\n * Solana wallets advertising `solana:signIn` take the SIWS path, so\n * `result.signedMessage` holds the wallet-composed statement and\n * `result.message` is absent. `preferSignMessage` opts out.\n */\nconst createSignInFlow = (options: SignInFlowOptions): SignInFlow => {\n const buildMessage = options.buildMessage ?? defaultBuildMessage;\n\n const sign = async (wallet: ConnectedWallet, account?: Account): Promise<SignInResult> => {\n const { connector } = wallet;\n const signingAccount = account ?? wallet.account;\n\n // SIWS needs no `signMessage`: the wallet composes and signs itself.\n if (\n options.preferSignMessage !== true &&\n connector.chainPlatform === \"svm\" &&\n connector.signIn !== undefined\n ) {\n const nonce = await options.getNonce({ account: signingAccount, wallet });\n const output = await connector.signIn({ nonce });\n return {\n account: output.account,\n nonce,\n signature: output.signature,\n signatureBase64: bytesToBase64(output.signature),\n signedMessage: output.signedMessage,\n signedMessageBase64: bytesToBase64(output.signedMessage),\n wallet,\n };\n }\n\n if (connector.signMessage === undefined) {\n throw new SignInUnsupportedError(connector.id);\n }\n\n const nonce = await options.getNonce({ account: signingAccount, wallet });\n const message = buildMessage({ account: signingAccount, nonce, wallet });\n const { signature, signedMessage } = await connector.signMessage(\n new TextEncoder().encode(message),\n { account: signingAccount },\n );\n\n return {\n account: signingAccount,\n message,\n nonce,\n signature,\n signatureBase64: bytesToBase64(signature),\n signedMessage,\n signedMessageBase64: bytesToBase64(signedMessage),\n wallet,\n };\n };\n\n return {\n signIn: async (wallet, account) => {\n const result = await sign(wallet, account);\n await options.verify(result);\n return result;\n },\n };\n};\n\nexport type { SignInFlow, SignInFlowOptions, SignInMessageContext, SignInResult };\nexport { SignInUnsupportedError, createSignInFlow };\n","/**\n * Some wallets announce data-URI icons wrapped in whitespace, which\n * makes Next.js `<Image>` throw on a leading control character.\n * Discovery already applies this; only hand-built metadata needs it.\n */\nconst sanitizeIcon = (icon: string | undefined): string | undefined => {\n if (icon === undefined) {\n return undefined;\n }\n const trimmed = icon.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\nexport { sanitizeIcon };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebutr/core",
3
- "version": "2.0.1",
3
+ "version": "3.0.1",
4
4
  "description": "Core types, store, storage, and discovery seam for butr. No React, no protocols.",
5
5
  "keywords": [
6
6
  "butr",
@@ -41,18 +41,18 @@
41
41
  "access": "public"
42
42
  },
43
43
  "dependencies": {
44
- "zod": "^4.4.3"
44
+ "zod": "^4.6.5"
45
45
  },
46
46
  "devDependencies": {
47
- "@types/node": "^26.2.0",
47
+ "@types/node": "^26.6.2",
48
48
  "typescript": "^7.0.2",
49
- "vitest": "^4.1.10",
50
- "zustand": "^5.0.14",
49
+ "vitest": "^5.0.1",
50
+ "zustand": "^5.0.15",
51
51
  "@repo/config-vitest": "0.0.0",
52
52
  "@repo/typescript-config": "0.0.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "zustand": ">=4.0.0"
55
+ "zustand": ">=4.5.0"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsdown",