@unicitylabs/sphere-sdk 0.15.0 → 0.16.0-dev.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/README.md +15 -5
- package/dist/connect/index.cjs +14 -23
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.cts +4 -0
- package/dist/connect/index.d.ts +4 -0
- package/dist/connect/index.js +14 -23
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +726 -225
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +172 -21
- package/dist/core/index.d.ts +172 -21
- package/dist/core/index.js +726 -224
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +14 -23
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +14 -23
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +331 -504
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +331 -504
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +13 -22
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.js +13 -22
- package/dist/impl/nodejs/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +298 -569
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +90 -13
- package/dist/impl/nodejs/index.d.ts +90 -13
- package/dist/impl/nodejs/index.js +298 -569
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/impl/shared/wallet-api/index.d.cts +54 -2
- package/dist/impl/shared/wallet-api/index.d.ts +54 -2
- package/dist/index.cjs +728 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -108
- package/dist/index.d.ts +227 -108
- package/dist/index.js +728 -224
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +11 -7
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +56 -3
- package/dist/modules/payments-v2/index.d.ts +56 -3
- package/dist/modules/payments-v2/index.js +11 -7
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +46 -0
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.js +46 -0
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../impl/browser/index.ts","../../../core/logger.ts","../../../core/errors.ts","../../../constants.ts","../../../impl/browser/storage/LocalStorageProvider.ts","../../../impl/browser/storage/IndexedDBStorageProvider.ts","../../../transport/NostrTransportProvider.ts","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha2.ts","../../../transport/websocket.ts","../../../impl/browser/transport/index.ts","../../../oracle/UnicityAggregatorProvider.ts","../../../assets/trustbase.ts","../../../impl/shared/trustbase-loader.ts","../../../impl/browser/oracle/index.ts","../../../impl/shared/wallet-api/index.ts","../../../impl/browser/download.ts","../../../impl/shared/network.ts","../../../core/timeout.ts","../../../price/CoinGeckoPriceProvider.ts","../../../price/index.ts","../../../registry/TokenRegistry.ts","../../../impl/shared/resolvers.ts"],"sourcesContent":["/**\n * Browser-specific implementations\n * All platform-dependent code lives here\n */\n\n// Polyfill Buffer for browser environment\n// Many crypto libraries depend on Node.js Buffer API\nimport { Buffer } from 'buffer';\nif (typeof globalThis.Buffer === 'undefined') {\n globalThis.Buffer = Buffer;\n}\n\nexport * from './storage';\nexport * from './transport';\nexport * from './oracle';\n// Wallet-api storage provider (platform-neutral, sdk-changes S2)\nexport * from '../shared/wallet-api';\n\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport { assertNetworkConsistency } from '../shared/network';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig } from './storage';\nimport { createNostrTransportProvider } from './transport';\nimport { createUnicityAggregatorProvider } from './oracle';\nimport type { StorageProvider } from '../../storage';\nimport type { TransportProvider } from '../../transport';\nimport type { OracleProvider } from '../../oracle';\nimport type { NetworkType } from '../../constants';\nimport type { GroupChatModuleConfig } from '../../modules/groupchat';\nimport type { MarketModuleConfig } from '../../modules/market';\nimport type { PriceProvider } from '../../price';\nimport { createPriceProvider } from '../../price';\nimport { TokenRegistry } from '../../registry';\nimport {\n type BaseTransportConfig,\n type BaseOracleConfig,\n type BasePriceConfig,\n type BaseMarketConfig,\n type BrowserTransportExtensions,\n resolveTransportConfig,\n resolveOracleConfig,\n resolvePriceConfig,\n getNetworkConfig,\n resolveGroupChatConfig,\n resolveMarketConfig,\n} from '../shared';\n\n// =============================================================================\n// Browser-Specific Configuration Extensions\n// =============================================================================\n\n/**\n * Browser transport configuration\n * Extends base with browser-specific options\n */\nexport type TransportConfig = BaseTransportConfig & BrowserTransportExtensions;\n\n/**\n * Browser oracle configuration\n * Same as base (no browser-specific extensions)\n */\nexport type OracleConfig = BaseOracleConfig;\n\n// =============================================================================\n// Token Sync Backend Configurations\n// =============================================================================\n\n// =============================================================================\n// Browser Providers Configuration\n// =============================================================================\n\nexport interface BrowserProvidersConfig {\n /** Network preset: mainnet, testnet, or dev. Sets default URLs for all services */\n network?: NetworkType;\n /** Enable debug logging globally for all providers (default: false). Per-provider debug flags override this. */\n debug?: boolean;\n /** Storage configuration (IndexedDB) */\n storage?: IndexedDBStorageProviderConfig;\n /** Transport (Nostr) configuration - supports extend/override pattern */\n transport?: TransportConfig;\n /** Oracle (Aggregator) configuration - supports extend/override pattern */\n oracle?: OracleConfig;\n /**\n * Token sync backends configuration\n * Supports multiple backends: IPFS, file, cloud (future)\n * Each backend can be enabled/disabled independently\n */\n /** Price provider configuration (optional — enables fiat value display) */\n price?: BasePriceConfig;\n /** Group chat (NIP-29) configuration. true = enable with defaults, object = custom config */\n groupChat?: { enabled?: boolean; relays?: string[] } | boolean;\n /** Market module configuration. true = enable with defaults, object = custom config */\n market?: BaseMarketConfig | boolean;\n}\n\nexport interface BrowserProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n /** Group chat config (resolved, for passing to Sphere.init) */\n groupChat?: GroupChatModuleConfig | boolean;\n /** Market module config (resolved, for passing to Sphere.init) */\n market?: MarketModuleConfig | boolean;\n}\n\n// =============================================================================\n// Token Sync Resolution\n// =============================================================================\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create all browser providers with default configuration\n *\n * Supports extend/override pattern for flexible configuration:\n * - Use `network` preset for quick setup (mainnet/testnet/dev)\n * - Override specific values (e.g., `oracle.url` replaces default)\n * - Extend arrays with `additional*` (e.g., `additionalRelays` adds to defaults)\n *\n * @example\n * ```ts\n * // Simple - uses mainnet defaults\n * const providers = createBrowserProviders();\n *\n * // Testnet - all services use testnet URLs\n * const providers = createBrowserProviders({ network: 'testnet' });\n *\n * // Add extra relays to testnet defaults\n * const providers = createBrowserProviders({\n * network: 'testnet',\n * transport: {\n * additionalRelays: ['wss://my-relay.com', 'wss://backup-relay.com'],\n * },\n * });\n *\n * // Replace relays entirely (ignores network defaults)\n * const providers = createBrowserProviders({\n * network: 'testnet',\n * transport: {\n * relays: ['wss://only-this-relay.com'],\n * },\n * });\n *\n * // Use with Sphere.init (add the wallet-api transport config)\n * const { sphere } = await Sphere.init({\n * ...createWalletApiProviders(providers, { baseUrl, network: 'testnet' }),\n * autoGenerate: true,\n * });\n * ```\n */\nexport function createBrowserProviders(config?: BrowserProvidersConfig): BrowserProviders {\n // Fail loud: a missing network would silently load the wrong-network providers.\n if (!config?.network) {\n throw new SphereError('createBrowserProviders: config.network is required.', 'INVALID_CONFIG');\n }\n const network = config.network;\n // Refuse provably-broken networks (e.g. a null/mismatched trust base would\n // silently accept unverified tokens) before building any provider.\n assertNetworkConsistency(network);\n\n // Configure global logger: top-level debug enables all, per-provider overrides are additive.\n // Only override global debug flag when explicitly provided — don't reset a previously-configured value.\n if (config?.debug !== undefined) {\n sdkLogger.configure({ debug: config.debug });\n }\n if (config?.transport?.debug) sdkLogger.setTagDebug('Nostr', true);\n if (config?.oracle?.debug) sdkLogger.setTagDebug('Aggregator', true);\n if (config?.price?.debug) sdkLogger.setTagDebug('Price', true);\n\n // Resolve configurations using shared utilities\n const transportConfig = resolveTransportConfig(network, config?.transport);\n const oracleConfig = resolveOracleConfig(network, config?.oracle);\n\n const storage = createIndexedDBStorageProvider({ ...config?.storage, network });\n const priceConfig = resolvePriceConfig(config?.price, storage);\n\n // Resolve group chat config\n const groupChat = resolveGroupChatConfig(network, config?.groupChat);\n\n // Resolve market config\n const market = resolveMarketConfig(config?.market);\n\n // Configure token registry remote refresh with persistent cache\n const networkConfig = getNetworkConfig(network);\n TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });\n\n return {\n storage,\n groupChat,\n market,\n transport: createNostrTransportProvider({\n relays: transportConfig.relays,\n timeout: transportConfig.timeout,\n autoReconnect: transportConfig.autoReconnect,\n reconnectDelay: transportConfig.reconnectDelay,\n maxReconnectAttempts: transportConfig.maxReconnectAttempts,\n debug: transportConfig.debug,\n storage,\n }),\n oracle: createUnicityAggregatorProvider({\n url: oracleConfig.url,\n apiKey: oracleConfig.apiKey,\n timeout: oracleConfig.timeout,\n skipVerification: oracleConfig.skipVerification,\n debug: oracleConfig.debug,\n network,\n }),\n price: priceConfig ? createPriceProvider(priceConfig) : undefined,\n };\n}\n","/**\n * Centralized SDK Logger\n *\n * A lightweight singleton logger that works across all tsup bundles\n * by storing state on globalThis. Supports three log levels:\n * - debug: detailed messages (only shown when debug=true)\n * - warn: important warnings (ALWAYS shown regardless of debug flag)\n * - error: critical errors (ALWAYS shown regardless of debug flag)\n *\n * Global debug flag enables all logging. Per-tag overrides allow\n * granular control (e.g., only transport debug).\n *\n * @example\n * ```ts\n * import { logger } from '@unicitylabs/sphere-sdk';\n *\n * // Enable all debug logging\n * logger.configure({ debug: true });\n *\n * // Enable only specific tags\n * logger.setTagDebug('Nostr', true);\n *\n * // Usage in SDK classes\n * logger.debug('Payments', 'Transfer started', { amount, recipient });\n * logger.warn('Nostr', 'queryEvents timed out after 5s');\n * logger.error('Sphere', 'Critical failure', error);\n * ```\n */\n\nexport type LogLevel = 'debug' | 'warn' | 'error';\n\nexport type LogHandler = (level: LogLevel, tag: string, message: string, ...args: unknown[]) => void;\n\nexport interface LoggerConfig {\n /** Enable debug logging globally (default: false). When false, only warn and error messages are shown. */\n debug?: boolean;\n /** Custom log handler. If provided, replaces console output. Useful for tests or custom log sinks. */\n handler?: LogHandler | null;\n}\n\n// Use a unique symbol-like key on globalThis to share logger state across tsup bundles\nconst LOGGER_KEY = '__sphere_sdk_logger__';\n\ninterface LoggerState {\n debug: boolean;\n tags: Record<string, boolean>;\n handler: LogHandler | null;\n}\n\nfunction getState(): LoggerState {\n const g = globalThis as unknown as Record<string, unknown>;\n if (!g[LOGGER_KEY]) {\n g[LOGGER_KEY] = { debug: false, tags: {}, handler: null } satisfies LoggerState;\n }\n return g[LOGGER_KEY] as LoggerState;\n}\n\nfunction isEnabled(tag: string): boolean {\n const state = getState();\n // Per-tag override takes priority\n if (tag in state.tags) return state.tags[tag];\n // Fall back to global flag\n return state.debug;\n}\n\nexport const logger = {\n /**\n * Configure the logger. Can be called multiple times (last write wins).\n * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().\n */\n configure(config: LoggerConfig): void {\n const state = getState();\n if (config.debug !== undefined) state.debug = config.debug;\n if (config.handler !== undefined) state.handler = config.handler;\n },\n\n /**\n * Enable/disable debug logging for a specific tag.\n * Per-tag setting overrides the global debug flag.\n *\n * @example\n * ```ts\n * logger.setTagDebug('Nostr', true); // enable only Nostr logs\n * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true\n * ```\n */\n setTagDebug(tag: string, enabled: boolean): void {\n getState().tags[tag] = enabled;\n },\n\n /**\n * Clear per-tag override, falling back to global debug flag.\n */\n clearTagDebug(tag: string): void {\n delete getState().tags[tag];\n },\n\n /** Returns true if debug mode is enabled for the given tag (or globally). */\n isDebugEnabled(tag?: string): boolean {\n if (tag) return isEnabled(tag);\n return getState().debug;\n },\n\n /**\n * Debug-level log. Only shown when debug is enabled (globally or for this tag).\n * Use for detailed operational information.\n */\n debug(tag: string, message: string, ...args: unknown[]): void {\n if (!isEnabled(tag)) return;\n const state = getState();\n if (state.handler) {\n state.handler('debug', tag, message, ...args);\n } else {\n console.log(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Warning-level log. ALWAYS shown regardless of debug flag.\n * Use for important but non-critical issues (timeouts, retries, degraded state).\n */\n warn(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('warn', tag, message, ...args);\n } else {\n console.warn(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Error-level log. ALWAYS shown regardless of debug flag.\n * Use for critical failures that should never be silenced.\n */\n error(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('error', tag, message, ...args);\n } else {\n console.error(`[${tag}]`, message, ...args);\n }\n },\n\n /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */\n reset(): void {\n const g = globalThis as unknown as Record<string, unknown>;\n delete g[LOGGER_KEY];\n },\n};\n","/**\n * SDK Error Types\n *\n * Structured error codes for programmatic error handling in UI.\n * UI can switch on error.code to show appropriate user-facing messages.\n *\n * @example\n * ```ts\n * import { SphereError } from '@unicitylabs/sphere-sdk';\n *\n * try {\n * await sphere.payments.send({ ... });\n * } catch (err) {\n * if (err instanceof SphereError) {\n * switch (err.code) {\n * case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;\n * case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;\n * case 'TRANSPORT_ERROR': showToast('Network connection issue'); break;\n * case 'TIMEOUT': showToast('Request timed out, try again'); break;\n * default: showToast(err.message);\n * }\n * }\n * }\n * ```\n */\n\nexport type SphereErrorCode =\n | 'NOT_INITIALIZED'\n | 'ALREADY_INITIALIZED'\n | 'INVALID_CONFIG'\n | 'INVALID_IDENTITY'\n | 'INSUFFICIENT_BALANCE'\n | 'INVALID_RECIPIENT'\n | 'TRANSFER_FAILED'\n // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n // The op's certification is INDETERMINATE (submit accepted / proof fetch\n // inconclusive) — raised as ProofUnconfirmedError; keep the intent OPEN (#631).\n | 'CERTIFICATION_UNCONFIRMED'\n // Split burn checkpoint (sdk-changes E.4, sphere-sdk#501) — all keep-open (token-engine/errors.ts):\n // burn certified but the checkpoint could not be persisted (no mint submitted);\n | 'CHECKPOINT_PERSIST_FAILED'\n // a certified mint leaf has no reproducing stored bytes, or a stored checkpoint fails byte-binding;\n | 'SPLIT_CHECKPOINT_LOST'\n // a byte-bound checkpoint proof no longer verifies against the current trust base (validator rotation).\n | 'CHECKPOINT_TRUSTBASE_MISMATCH'\n | 'STORAGE_ERROR'\n // #665: the on-chain spend committed but the post-commit wallet-api mirror\n // sync (inventory apply / blob upload / save) failed. NOT a lost payment —\n // the intent is kept open and resume converges the mirror (idempotent apply,\n // #664). Surfaced distinctly so a UI can reassure (\"sent — wallet catching\n // up\") instead of showing a hard send failure.\n | 'SEND_SYNC_PENDING'\n // #677: a multi-leg send lost a source to a concurrent transfer AFTER ≥1\n // earlier leg had already certified on-chain AND been journaled/delivered.\n // The delivered value has irreversibly left the wallet, so this is NOT a\n // plain, re-sendable failure: surfacing a bare TransferConflictError makes\n // the caller re-send the FULL amount and pay the delivered leg twice. Raised\n // as PartialSendConflictError; the caller MUST re-plan ONLY the remainder\n // under a NEW transferId, never the whole amount.\n | 'SEND_PARTIALLY_COMPLETED'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'INVALID_AMOUNT'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR'\n // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n /**\n * #441 deferred-paid linkage: the transferId of the possibly-committed send,\n * stamped by `sendOnce` before the error leaves so a payment-request consumer\n * can durably journal request→transfer and resolve 'paid' only when that\n * transfer actually completes (never optimistically). Present ONLY on\n * possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).\n */\n transferId?: string;\n\n constructor(message: string, code: SphereErrorCode, cause?: unknown) {\n super(message);\n this.name = 'SphereError';\n this.code = code;\n this.cause = cause;\n }\n}\n\n/**\n * #677: a send that PARTIALLY completed before losing a source to a concurrent\n * transfer. At least one earlier leg certified on-chain and was journaled for\n * delivery (its {@link committedTokenIds}) — that value has irreversibly left\n * the wallet — but a LATER leg raised `TransferConflictError` (a lost race), and\n * `send()` could NOT COMPLETE the {@link remainingAmount} from the remaining live\n * sources. This is the fallback after the internal remainder re-plan gave up —\n * either insufficient funds OR a transient/transport error during the re-plan\n * (the underlying reason is on `cause`), not necessarily an \"out of funds\" state.\n *\n * Distinct from `TransferConflictError` on purpose: a bare conflict makes the\n * caller re-send the FULL amount, paying the already-delivered leg a second\n * time. Catching THIS type (or `code === 'SEND_PARTIALLY_COMPLETED'`) tells the\n * caller/UI: the delivered legs are final, the conflicted intent stays OPEN\n * (resume converges its certified legs), and only the REMAINDER\n * ({@link remainingAmount}) may be re-planned — under a NEW transferId, never\n * re-sending the whole amount.\n *\n * NOT a subclass of `TransferConflictError` by design: existing conflict\n * handlers that re-send in full must NOT treat this as an ordinary conflict.\n */\nexport class PartialSendConflictError extends SphereError {\n /**\n * The FIRST partial attempt's transferId — its intent stays open for resume,\n * and it is the §6 handoff anchor. NOTE: `send()` may re-plan the remainder across\n * SEVERAL internal attempts before giving up, so {@link committedTokenIds} can\n * span MULTIPLE transferIds — they do NOT all map to this single id.\n */\n readonly transferId: string;\n /**\n * Source token ids whose spend already certified on-chain (and whose finished\n * output blob is journaled for delivery). Their value has already been\n * delivered; NEVER re-send these. May accumulate across several internal\n * remainder-re-plan attempts (so not all are journaled under {@link transferId}).\n */\n readonly committedTokenIds: readonly string[];\n /**\n * The still-undelivered portion (base units, decimal string) — `send()` could\n * not COMPLETE it from the remaining live sources (insufficient funds or a\n * transient error; see `cause`). Re-plan ONLY this amount, under a new\n * transferId; the delivered legs converge via the recipient's §6 claim.\n */\n readonly remainingAmount: string;\n\n constructor(\n message: string,\n transferId: string,\n committedTokenIds: readonly string[],\n remainingAmount: string,\n cause?: unknown,\n ) {\n super(message, 'SEND_PARTIALLY_COMPLETED', cause);\n this.name = 'PartialSendConflictError';\n this.transferId = transferId;\n this.committedTokenIds = [...committedTokenIds];\n this.remainingAmount = remainingAmount;\n }\n}\n\n/**\n * Error codes for a send outcome where money has (or may have) already left the\n * wallet on-chain, so the send is NOT a clean, re-sendable failure. A consumer\n * that persists a \"paid\" flag (e.g. a payment request) must keep the request\n * NON-payable on any of these — reverting to a re-payable state would double-pay\n * (#441). Covers the post-commit mirror-sync-pending / keep-open certification\n * states AND the partial-conflict outcome.\n */\nconst POSSIBLY_COMMITTED_SEND_CODES: ReadonlySet<SphereErrorCode> = new Set([\n 'SEND_SYNC_PENDING',\n 'CERTIFICATION_UNCONFIRMED',\n 'CHECKPOINT_PERSIST_FAILED',\n 'SPLIT_CHECKPOINT_LOST',\n 'CHECKPOINT_TRUSTBASE_MISMATCH',\n 'SEND_PARTIALLY_COMPLETED',\n]);\n\n/**\n * #441: true when a send failed with an outcome where the payment has (or may\n * have) irreversibly left the wallet — a post-commit sync-pending / keep-open\n * certification state, or a `PartialSendConflictError`. Such a send completes (or\n * has already partially completed) via resume/claim and MUST NOT be re-sent, so\n * a persisted \"paid\" state must stay non-payable. A `false` result is a clean\n * pre-commit failure — nothing left the wallet, safe to re-pay.\n */\nexport function isPossiblyCommittedSendOutcome(err: unknown): boolean {\n return isSphereError(err) && POSSIBLY_COMMITTED_SEND_CODES.has(err.code);\n}\n\n/**\n * Type guard to check if an error is a SphereError\n */\nexport function isSphereError(err: unknown): err is SphereError {\n return err instanceof SphereError;\n}\n","/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/**\n * Global storage keys (one per wallet, no address index)\n * Final key format: sphere_{key}\n */\nexport const STORAGE_KEYS_GLOBAL = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: 'mnemonic',\n /** Encrypted master private key */\n MASTER_KEY: 'master_key',\n /** BIP32 chain code */\n CHAIN_CODE: 'chain_code',\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: 'derivation_path',\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: 'base_path',\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: 'derivation_mode',\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: 'wallet_source',\n /** Wallet existence flag */\n WALLET_EXISTS: 'wallet_exists',\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: 'current_address_index',\n /** Nametag cache per address (separate from tracked addresses registry) */\n ADDRESS_NAMETAGS: 'address_nametags',\n /** Active addresses registry (JSON: TrackedAddressesStorage) */\n TRACKED_ADDRESSES: 'tracked_addresses',\n /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */\n LAST_WALLET_EVENT_TS: 'last_wallet_event_ts',\n /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data is server custody (wallet-api backend), not here; the\n * payments vertical's durable client state self-prefixes `pv2g2:{network}:{pubkey}:`.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Transfer outbox for this address (pre-flip key name; kept as the network-scoping witness) */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Group chat: joined groups for this address */\n GROUP_CHAT_GROUPS: 'group_chat_groups',\n /** Group chat: messages for this address */\n GROUP_CHAT_MESSAGES: 'group_chat_messages',\n /** Group chat: members for this address */\n GROUP_CHAT_MEMBERS: 'group_chat_members',\n /** Group chat: processed event IDs for deduplication */\n GROUP_CHAT_PROCESSED_EVENTS: 'group_chat_processed_events',\n /** Auto-return settings (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Per-swap key prefix (pre-flip key name; kept as the network-scoping witness) */\n SWAP_RECORD_PREFIX: 'swap:',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe. Chat/identity per-address keys (CONVERSATIONS/MESSAGES/\n * GROUP_CHAT_*) are network-AGNOSTIC and deliberately NOT listed. The payments vertical\n * self-prefixes `pv2g2:{network}:{pubkey}:` and never rides this mechanism; the entries left\n * here are the pre-flip key names the network-isolation tests pin the provider behavior\n * with (P11 stage-2 note: the full isNetworkScopedAddressKey cut needs an owner call on\n * those tests first).\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n/**\n * NIP-29 Event Kinds for relay-based group chat\n * https://github.com/nostr-protocol/nips/blob/master/29.md\n */\nexport const NIP29_KINDS = {\n /** Chat message sent to group */\n CHAT_MESSAGE: 9,\n /** Thread root message */\n THREAD_ROOT: 11,\n /** Thread reply message */\n THREAD_REPLY: 12,\n /** User join request */\n JOIN_REQUEST: 9021,\n /** User leave request */\n LEAVE_REQUEST: 9022,\n /** Admin: add/update user */\n PUT_USER: 9000,\n /** Admin: remove user */\n REMOVE_USER: 9001,\n /** Admin: edit group metadata */\n EDIT_METADATA: 9002,\n /** Admin: delete event */\n DELETE_EVENT: 9005,\n /** Admin: create group */\n CREATE_GROUP: 9007,\n /** Admin: delete group */\n DELETE_GROUP: 9008,\n /** Admin: create invite code */\n CREATE_INVITE: 9009,\n /** Relay-signed group metadata */\n GROUP_METADATA: 39000,\n /** Relay-signed group admins */\n GROUP_ADMINS: 39001,\n /** Relay-signed group members */\n GROUP_MEMBERS: 39002,\n /** Relay-signed group roles */\n GROUP_ROLES: 39003,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n\n// =============================================================================\n// Wallet Defaults\n// =============================================================================\n\n/** Default BIP32 base path (without chain/index) */\nexport const DEFAULT_BASE_PATH = \"m/44'/0'/0'\" as const;\n\n/** Default BIP32 derivation path (full path with chain/index) */\nexport const DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0` as const;\n\n/** Coin types */\nexport const COIN_TYPES = {\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// Token Registry Defaults\n// =============================================================================\n\n/** Remote token registry URL (GitHub raw) */\nexport const TOKEN_REGISTRY_URL =\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json' as const;\n\n/** Default token registry refresh interval (ms) — 1 hour */\nexport const TOKEN_REGISTRY_REFRESH_INTERVAL = 3_600_000;\n\n// =============================================================================\n// Network Defaults\n// =============================================================================\n\n/** Testnet Nostr relays */\nexport const TEST_NOSTR_RELAYS = [\n 'wss://nostr-relay.testnet.unicity.network',\n] as const;\n\n/** Default group chat relays (NIP-29 Zooid relay) */\nexport const DEFAULT_GROUP_RELAYS = [\n 'wss://sphere-relay.unicity.network',\n] as const;\n\n/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** Canonical numeric network id (= RootTrustBase.networkId; testnet2 = 4).\n * Optional: only set for networks that have a live v2 trust base. */\n readonly networkId?: number;\n}\n\n/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n // NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot\n // operate against them until their gateways are cut over to the v2 protocol —\n // wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n/**\n * A network descriptor — the canonical way to identify a Unicity network across\n * the SDK and the Connect protocol. `id` is the canonical key (= RootTrustBase\n * networkId, analogous to an EIP-155 chainId); `name` is human-readable metadata.\n * Richer fields (gateway/symbol/explorer/icon) and switch/add-network are deferred.\n */\nexport interface NetworkInfo {\n readonly id: number;\n readonly name?: string;\n}\n\n/**\n * Registry of known networks for dApps and wallets. Single-sourced from NETWORKS\n * so it cannot drift. Use as `network: SPHERE_NETWORKS.testnet2`. Custom networks\n * are the same shape: `network: { id, name }`. Only live v2 networks appear here;\n * the legacy `testnet` alias is intentionally not surfaced.\n */\nexport const SPHERE_NETWORKS = {\n testnet2: { id: NETWORKS.testnet2.networkId as number, name: 'testnet2' },\n} as const satisfies Record<string, NetworkInfo>;\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n// =============================================================================\n// Sphere Connect\n// =============================================================================\n\n/** Signal sent by wallet popup to dApp when ConnectHost is ready */\nexport const HOST_READY_TYPE = 'sphere-connect:host-ready' as const;\n\n/** Default timeout (ms) for waiting for the host-ready signal */\nexport const HOST_READY_TIMEOUT = 30_000;\n\n/** Validation limits */\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * Browser LocalStorage Provider\n * Implements StorageProvider using browser localStorage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface LocalStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Custom storage instance (for testing/SSR) */\n storage?: Storage;\n /**\n * When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global/seed keys stay shared.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class LocalStorageProvider implements StorageProvider {\n readonly id = 'localStorage';\n readonly name = 'Local Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser localStorage for single-device persistence';\n\n private config: Required<Pick<LocalStorageProviderConfig, 'prefix' | 'debug'>> & {\n storage: Storage;\n };\n private network?: NetworkType;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n\n constructor(config?: LocalStorageProviderConfig) {\n // SSR fallback: use in-memory storage if localStorage unavailable\n const storage = config?.storage ?? this.getStorageSafe();\n\n this.config = {\n prefix: config?.prefix ?? 'sphere_',\n storage,\n debug: config?.debug ?? false,\n };\n this.network = config?.network;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Test storage availability\n const testKey = `${this.config.prefix}_test`;\n this.config.storage.setItem(testKey, 'test');\n this.config.storage.removeItem(testKey);\n\n this.status = 'connected';\n this.log('Connected to localStorage');\n } catch (error) {\n this.status = 'error';\n throw new SphereError(`LocalStorage not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.log('Disconnected from localStorage');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.chainPubkey);\n }\n\n async get(key: string): Promise<string | null> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n return this.config.storage.getItem(fullKey);\n }\n\n async set(key: string, value: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n this.config.storage.setItem(fullKey, value);\n }\n\n async remove(key: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n this.config.storage.removeItem(fullKey);\n }\n\n async has(key: string): Promise<boolean> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n return this.config.storage.getItem(fullKey) !== null;\n }\n\n async keys(prefix?: string): Promise<string[]> {\n this.ensureConnected();\n const basePrefix = this.getFullKey('');\n const searchPrefix = prefix ? this.getFullKey(prefix) : basePrefix;\n const result: string[] = [];\n\n for (let i = 0; i < this.config.storage.length; i++) {\n const key = this.config.storage.key(i);\n if (key?.startsWith(searchPrefix)) {\n // Return key without the base prefix\n result.push(key.slice(basePrefix.length));\n }\n }\n\n return result;\n }\n\n async clear(prefix?: string): Promise<void> {\n this.ensureConnected();\n const keysToRemove = await this.keys(prefix);\n for (const key of keysToRemove) {\n await this.remove(key);\n }\n }\n\n async saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void> {\n await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify({ version: 1, addresses: entries }));\n }\n\n async loadTrackedAddresses(): Promise<TrackedAddressEntry[]> {\n const data = await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);\n if (!data) return [];\n try {\n const parsed = JSON.parse(data);\n return parsed.addresses ?? [];\n } catch {\n return [];\n }\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /**\n * Get JSON data\n */\n async getJSON<T>(key: string): Promise<T | null> {\n const value = await this.get(key);\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Set JSON data\n */\n async setJSON<T>(key: string, value: T): Promise<void> {\n await this.set(key, JSON.stringify(value));\n }\n\n // ===========================================================================\n // Private Methods\n // ===========================================================================\n\n private getFullKey(key: string): string {\n // Check if this is a per-address key\n const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n // Add address ID prefix for per-address data\n const id = this.identity.chainPubkey;\n return `${this.config.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.config.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('LocalStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n private getStorageSafe(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n\n // SSR fallback: in-memory storage\n return createInMemoryStorage();\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('LocalStorage', message, ...args);\n }\n}\n\n// =============================================================================\n// In-Memory Storage (SSR Fallback)\n// =============================================================================\n\nfunction createInMemoryStorage(): Storage {\n const data = new Map<string, string>();\n\n return {\n get length() {\n return data.size;\n },\n clear() {\n data.clear();\n },\n getItem(key: string) {\n return data.get(key) ?? null;\n },\n setItem(key: string, value: string) {\n data.set(key, value);\n },\n removeItem(key: string) {\n data.delete(key);\n },\n key(index: number) {\n return Array.from(data.keys())[index] ?? null;\n },\n };\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createLocalStorageProvider(\n config?: LocalStorageProviderConfig\n): LocalStorageProvider {\n return new LocalStorageProvider(config);\n}\n","/**\n * Browser IndexedDB Storage Provider\n * Implements StorageProvider using IndexedDB for large-capacity browser storage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-storage';\nconst DB_VERSION = 1;\nconst STORE_NAME = 'kv';\n\nexport interface IndexedDBStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Database name (default: 'sphere-storage') */\n dbName?: string;\n /**\n * Network. When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global (seed) keys stay shared across networks.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class IndexedDBStorageProvider implements StorageProvider {\n readonly id = 'indexeddb-storage';\n readonly name = 'IndexedDB Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser IndexedDB for large-capacity persistence';\n\n private prefix: string;\n private dbName: string;\n private network?: NetworkType;\n private debug: boolean;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n private db: IDBDatabase | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBStorageProviderConfig) {\n this.prefix = config?.prefix ?? 'sphere_';\n this.dbName = config?.dbName ?? DB_NAME;\n this.network = config?.network;\n this.debug = config?.debug ?? false;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected' && this.db) return;\n\n // Retry once — a pending deleteDatabase() from a prior clear() can\n // block open() until the deletion completes. A short delay is usually\n // enough for the browser to finish the deletion.\n for (let attempt = 0; attempt < 2; attempt++) {\n this.status = 'connecting';\n const t0 = Date.now();\n logger.debug('IndexedDB',` connect: opening db=${this.dbName}, attempt=${attempt + 1}/2`);\n\n try {\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('IndexedDB open timed out after 5s')), 5000),\n ),\n ]);\n this.status = 'connected';\n logger.debug('IndexedDB',` connect: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return;\n } catch (error) {\n logger.warn('IndexedDB',` connect: open failed db=${this.dbName} attempt=${attempt + 1} (${Date.now() - t0}ms):`, error);\n if (attempt === 0) {\n this.status = 'disconnected';\n await new Promise((r) => setTimeout(r, 1000));\n continue;\n }\n this.status = 'error';\n throw new SphereError(`IndexedDB not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n }\n\n async disconnect(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDB',` disconnect: db=${this.dbName} connId=${cid} wasConnected=${!!this.db}`);\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.db !== null;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.chainPubkey);\n }\n\n async get(key: string): Promise<string | null> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n const result = await this.idbGet(fullKey);\n return result?.v ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n await this.idbPut({ k: fullKey, v: value });\n }\n\n async remove(key: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n await this.idbDelete(fullKey);\n }\n\n async has(key: string): Promise<boolean> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n const count = await this.idbCount(fullKey);\n return count > 0;\n }\n\n async keys(prefix?: string): Promise<string[]> {\n this.ensureConnected();\n const basePrefix = this.getFullKey('');\n const searchPrefix = prefix ? this.getFullKey(prefix) : basePrefix;\n const allEntries = await this.idbGetAll();\n const result: string[] = [];\n\n for (const entry of allEntries) {\n if (entry.k.startsWith(searchPrefix)) {\n // Return key without the base prefix\n result.push(entry.k.slice(basePrefix.length));\n }\n }\n\n return result;\n }\n\n async clear(prefix?: string): Promise<void> {\n if (!prefix) {\n // Clear the object store contents instead of deleteDatabase().\n // deleteDatabase() gets blocked by leaked IDB connections (e.g. React\n // StrictMode double-mount starts async init, cleanup destroys the\n // instance but the first init's connections keep draining in background).\n // A blocked deleteDatabase also blocks ALL subsequent open() calls,\n // bricking the wallet until the user refreshes the page.\n const t0 = Date.now();\n const prevConnId = this.connId;\n logger.debug('IndexedDB',` clear: starting db=${this.dbName} connId=${prevConnId} status=${this.status} hasDb=${!!this.db}`);\n\n try {\n // Ensure we have a connection to clear the store\n if (!this.db || this.status !== 'connected') {\n if (this.db) {\n logger.debug('IndexedDB',` clear: closing stale handle connId=${prevConnId}`);\n this.db.close();\n this.db = null;\n }\n logger.debug('IndexedDB',` clear: opening fresh connection for wipe`);\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('open timed out')), 3000),\n ),\n ]);\n this.status = 'connected';\n }\n\n // Clear all data from the store — cannot be blocked by other connections\n await this.idbClear();\n logger.debug('IndexedDB',` clear: store cleared db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n } catch (err) {\n logger.warn('IndexedDB',` clear: failed db=${this.dbName} (${Date.now() - t0}ms)`, err);\n } finally {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n return;\n }\n\n this.ensureConnected();\n const keysToRemove = await this.keys(prefix);\n for (const key of keysToRemove) {\n await this.remove(key);\n }\n }\n\n async saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void> {\n await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify({ version: 1, addresses: entries }));\n }\n\n async loadTrackedAddresses(): Promise<TrackedAddressEntry[]> {\n const data = await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);\n if (!data) return [];\n try {\n const parsed = JSON.parse(data);\n return parsed.addresses ?? [];\n } catch {\n return [];\n }\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /**\n * Get JSON data\n */\n async getJSON<T>(key: string): Promise<T | null> {\n const value = await this.get(key);\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Set JSON data\n */\n async setJSON<T>(key: string, value: T): Promise<void> {\n await this.set(key, JSON.stringify(value));\n }\n\n // ===========================================================================\n // Private: Key Scoping\n // ===========================================================================\n\n private getFullKey(key: string): string {\n const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n const id = this.identity.chainPubkey;\n return `${this.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected' || !this.db) {\n throw new SphereError('IndexedDBStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n // ===========================================================================\n // Private: IndexedDB Operations\n // ===========================================================================\n\n private openDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n\n request.onsuccess = () => {\n const db = request.result;\n const cid = ++connectionSeq;\n this.connId = cid;\n\n // Auto-close when another context requests version change or deletion.\n // Prevents leaked connections (e.g. React StrictMode double-mount)\n // from blocking deleteDatabase() or version upgrades.\n db.onversionchange = () => {\n logger.debug('IndexedDB',` onversionchange: auto-closing db=${this.dbName} connId=${cid}`);\n db.close();\n if (this.db === db) {\n this.db = null;\n this.status = 'disconnected';\n }\n };\n resolve(db);\n };\n\n // onblocked fires when another connection (e.g. other tab) holds\n // the database at a lower version. Log it — onsuccess will follow\n // once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDB',` open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'k' });\n }\n };\n });\n }\n\n private idbGet(key: string): Promise<{ k: string; v: string } | undefined> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? undefined);\n });\n }\n\n private idbPut(entry: { k: string; v: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.put(entry);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbDelete(key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.delete(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbCount(key: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.count(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n });\n }\n\n private idbGetAll(): Promise<{ k: string; v: string }[]> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.getAll();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private idbClear(): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.clear();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('IndexedDB', message, ...args);\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createIndexedDBStorageProvider(\n config?: IndexedDBStorageProviderConfig\n): IndexedDBStorageProvider {\n return new IndexedDBStorageProvider(config);\n}\n","/**\n * Nostr Transport Provider\n * Platform-independent implementation using Nostr protocol for P2P messaging\n *\n * Uses @unicitylabs/nostr-js-sdk for:\n * - Real secp256k1 event signing\n * - NIP-04 encryption/decryption\n * - Event ID calculation\n * - NostrClient for reliable connection management (ping, reconnect, NIP-42)\n *\n * WebSocket is injected via factory for cross-platform support\n */\n\nimport { Buffer } from 'buffer';\nimport { sha256 as sha256Noble } from '@noble/hashes/sha2.js';\nimport {\n NostrKeyManager,\n NIP04,\n NIP17,\n NIP44,\n Event as NostrEventClass,\n EventKinds,\n decryptNametag,\n NostrClient,\n Filter,\n isChatMessage,\n isReadReceipt,\n} from '@unicitylabs/nostr-js-sdk';\nimport type { BindingInfo } from '@unicitylabs/nostr-js-sdk';\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';\nimport { logger } from '../core/logger';\nimport type { ProviderStatus, FullIdentity } from '../types';\nimport { SphereError } from '../core/errors';\nimport type {\n TransportProvider,\n MessageHandler,\n ComposingHandler,\n BroadcastHandler,\n IncomingMessage,\n IncomingBroadcast,\n TransportEvent,\n TransportEventCallback,\n PeerInfo,\n ReadReceiptHandler,\n IncomingReadReceipt,\n TypingIndicatorHandler,\n IncomingTypingIndicator,\n} from './transport-provider';\nimport type { WebSocketFactory, UUIDGenerator } from './websocket';\nimport { defaultUUIDGenerator } from './websocket';\nimport {\n DEFAULT_NOSTR_RELAYS,\n NOSTR_EVENT_KINDS,\n STORAGE_KEYS_GLOBAL,\n TIMEOUTS,\n} from '../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/**\n * Minimal key-value storage interface for transport persistence.\n * Used to persist the last processed event timestamp across sessions.\n */\nexport interface TransportStorageAdapter {\n get(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n}\n\nexport interface NostrTransportProviderConfig {\n /** Nostr relay URLs */\n relays?: string[];\n /** Connection timeout (ms) */\n timeout?: number;\n /** Auto-reconnect on disconnect */\n autoReconnect?: boolean;\n /** Reconnect delay (ms) */\n reconnectDelay?: number;\n /** Max reconnect attempts */\n maxReconnectAttempts?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** WebSocket factory (required for platform support) */\n createWebSocket: WebSocketFactory;\n /** UUID generator (optional, defaults to crypto.randomUUID) */\n generateUUID?: UUIDGenerator;\n /** Optional storage adapter for persisting subscription timestamps */\n storage?: TransportStorageAdapter;\n}\n\nconst COMPOSING_INDICATOR_KIND = 25050;\nconst TIMESTAMP_RANDOMIZATION = 2 * 24 * 60 * 60;\n\n// Alias for backward compatibility\nconst EVENT_KINDS = NOSTR_EVENT_KINDS;\n\n/**\n * The network a RAW SIGNED binding event declares in its content, or undefined —\n * read-if-present, never invented (a stamped guess is what made the §5.6 money\n * guard dead, #733).\n *\n * It takes the EVENT, not a parsed shape, and the parameter type is the point:\n * nostr-js-sdk's `parseBindingInfo` whitelists the binding content\n * (`public_key`/`l1_address`/`direct_address`/`proxy_address`/`nametag`) and\n * drops everything else, so a `BindingInfo` can never carry a network — handing\n * one to this function would compile into a value that is `undefined` for all\n * time, which is exactly the dead read the #735 review caught. A `BindingInfo`\n * is not a `NostrEvent`; that mistake is now a type error, not a comment.\n *\n * Nothing declares a network yet either: the publish side\n * (`IdentityBindingParams` + `createBindingEvent`/`createIdentityBindingEvent`)\n * whitelists the same fields, so the claim cannot ride today's publish API at\n * all — hence sends to an unproven peer are SIGNALLED, not refused (#734).\n */\nfunction declaredNetwork(event: NostrEvent): string | undefined {\n let value: unknown;\n try {\n value = (JSON.parse(event.content) as { network?: unknown } | null)?.network;\n } catch {\n return undefined;\n }\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class NostrTransportProvider implements TransportProvider {\n readonly id = 'nostr';\n readonly name = 'Nostr Transport';\n readonly type = 'p2p' as const;\n readonly description = 'P2P messaging via Nostr protocol';\n\n private config: Required<Omit<NostrTransportProviderConfig, 'createWebSocket' | 'generateUUID' | 'storage'>> & {\n createWebSocket: WebSocketFactory;\n generateUUID: UUIDGenerator;\n };\n private storage: TransportStorageAdapter | null = null;\n /** In-memory max event timestamp to avoid read-before-write races in updateLastEventTimestamp. */\n private lastEventTs: number = 0;\n /** In-memory max DM (gift-wrap) event timestamp. */\n private lastDmEventTs: number = 0;\n /** Fallback 'since' timestamp for first-time address subscriptions (consumed once). */\n private fallbackSince: number | null = null;\n /** Fallback 'since' timestamp for DM (gift-wrap) subscriptions (consumed once). */\n private fallbackDmSince: number | null = null;\n private identity: FullIdentity | null = null;\n private keyManager: NostrKeyManager | null = null;\n private status: ProviderStatus = 'disconnected';\n\n // NostrClient from nostr-js-sdk handles all WebSocket management,\n // keepalive pings, reconnection, and NIP-42 authentication\n private nostrClient: NostrClient | null = null;\n private mainSubscriptionId: string | null = null;\n\n // Event handlers\n private processedEventIds = new Set<string>();\n private messageHandlers: Set<MessageHandler> = new Set();\n private readReceiptHandlers: Set<ReadReceiptHandler> = new Set();\n private typingIndicatorHandlers: Set<TypingIndicatorHandler> = new Set();\n private composingHandlers: Set<ComposingHandler> = new Set();\n private pendingMessages: IncomingMessage[] = [];\n private broadcastHandlers: Map<string, Set<BroadcastHandler>> = new Map();\n private eventCallbacks: Set<TransportEventCallback> = new Set();\n\n constructor(config: NostrTransportProviderConfig) {\n this.config = {\n relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],\n timeout: config.timeout ?? TIMEOUTS.WEBSOCKET_CONNECT,\n autoReconnect: config.autoReconnect ?? true,\n reconnectDelay: config.reconnectDelay ?? TIMEOUTS.NOSTR_RECONNECT_DELAY,\n maxReconnectAttempts: config.maxReconnectAttempts ?? TIMEOUTS.MAX_RECONNECT_ATTEMPTS,\n debug: config.debug ?? false,\n createWebSocket: config.createWebSocket,\n generateUUID: config.generateUUID ?? defaultUUIDGenerator,\n };\n this.storage = config.storage ?? null;\n }\n\n /**\n * Get the WebSocket factory (used by MultiAddressTransportMux to share the same factory).\n */\n getWebSocketFactory(): WebSocketFactory {\n return this.config.createWebSocket;\n }\n\n /**\n * Get the configured relay URLs.\n */\n getConfiguredRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get the storage adapter.\n */\n getStorageAdapter(): TransportStorageAdapter | null {\n return this.storage;\n }\n\n /**\n * Get the underlying NostrClient (or null if not yet connected).\n *\n * Exposed so {@link MultiAddressTransportMux} can share the same\n * client/socket pair instead of opening a duplicate WebSocket per\n * relay (#123). The transport owns the client's lifecycle — callers\n * MUST NOT call {@code disconnect()} on the returned instance.\n */\n getNostrClient(): NostrClient | null {\n return this.nostrClient;\n }\n\n /**\n * Suppress event subscriptions — unsubscribe wallet/chat filters\n * but keep the connection alive for resolve/identity-binding operations.\n * Used when MultiAddressTransportMux takes over event handling.\n */\n suppressSubscriptions(): void {\n if (!this.nostrClient) return;\n\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Prevent re-subscription on reconnect by marking subscriptions as suppressed\n this._subscriptionsSuppressed = true;\n logger.debug('Nostr', 'Subscriptions suppressed — mux handles event routing');\n }\n\n // Flag to prevent re-subscription after suppressSubscriptions()\n private _subscriptionsSuppressed = false;\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Ensure keyManager exists for NostrClient\n if (!this.keyManager) {\n // Create a temporary key manager - will be replaced when setIdentity is called\n const tempKey = Buffer.alloc(32);\n crypto.getRandomValues(tempKey);\n this.keyManager = NostrKeyManager.fromPrivateKey(tempKey);\n }\n\n // Create NostrClient with robust connection handling:\n // - autoReconnect: automatic reconnection with exponential backoff\n // - pingIntervalMs: keepalive pings to detect stale connections\n // - NIP-42 AUTH handling built-in\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16, // exponential backoff cap\n pingIntervalMs: 15000, // 15 second keepalive pings (more aggressive to prevent drops)\n // Bump query timeout from the SDK default of 5s to 20s.\n // Real-world testnet observation (2026-05-01): under transient\n // relay overload, kind:30078 (nametag binding) queries take 5-7s\n // to return EVENT messages, even though the binding is on the\n // relay. The default 5s timeout fires before the event arrives,\n // resolveNametag returns null, and downstream sendDM throws\n // INVALID_RECIPIENT — causing every np.propose_deal to fail. 20s\n // gives the slow path enough headroom to complete while still\n // bailing reasonably fast on a truly broken relay.\n queryTimeoutMs: 20000,\n });\n\n // Add connection event listener for logging\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n this.emitEvent({ type: 'transport:reconnecting', timestamp: Date.now() });\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n // Re-establish subscriptions — the relay drops them on disconnect.\n this.subscribeToEvents().catch((err) => {\n logger.error('Nostr', 'Failed to re-subscribe after reconnect:', err);\n });\n },\n });\n\n // Connect to all relays (with timeout to prevent indefinite hang)\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport connection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n\n // Need at least one successful connection\n if (!this.nostrClient.isConnected()) {\n throw new SphereError('Failed to connect to any relay', 'TRANSPORT_ERROR');\n }\n\n this.status = 'connected';\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n logger.debug('Nostr', 'Connected to', this.nostrClient.getConnectedRelays().size, 'relays');\n\n // Set up subscriptions\n if (this.identity) {\n await this.subscribeToEvents();\n }\n } catch (error) {\n this.status = 'error';\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (this.nostrClient) {\n this.nostrClient.disconnect();\n this.nostrClient = null;\n }\n this.mainSubscriptionId = null;\n this.walletSubscriptionId = null;\n this.chatSubscriptionId = null;\n this.chatEoseFired = false;\n this.status = 'disconnected';\n this.emitEvent({ type: 'transport:disconnected', timestamp: Date.now() });\n logger.debug('Nostr', 'Disconnected from all relays');\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.nostrClient?.isConnected() === true;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // Dynamic Relay Management\n // ===========================================================================\n\n /**\n * Get list of configured relay URLs\n */\n getRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get list of currently connected relay URLs\n */\n getConnectedRelays(): string[] {\n if (!this.nostrClient) return [];\n return Array.from(this.nostrClient.getConnectedRelays());\n }\n\n /**\n * Add a new relay dynamically\n * Will connect immediately if provider is already connected\n */\n async addRelay(relayUrl: string): Promise<boolean> {\n // Check if already configured\n if (this.config.relays.includes(relayUrl)) {\n logger.debug('Nostr', 'Relay already configured:', relayUrl);\n return false;\n }\n\n // Add to config\n this.config.relays.push(relayUrl);\n\n // Connect if provider is connected\n if (this.status === 'connected' && this.nostrClient) {\n try {\n await this.nostrClient.connect(relayUrl);\n logger.debug('Nostr', 'Added and connected to relay:', relayUrl);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: true },\n });\n return true;\n } catch (error) {\n logger.debug('Nostr', 'Failed to connect to new relay:', relayUrl, error);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false, error: String(error) },\n });\n return false;\n }\n }\n\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false },\n });\n return true;\n }\n\n /**\n * Remove a relay dynamically\n * Will disconnect from the relay if connected\n * NOTE: NostrClient doesn't support removing individual relays at runtime.\n * We remove from config so it won't be used on next connect().\n */\n async removeRelay(relayUrl: string): Promise<boolean> {\n const index = this.config.relays.indexOf(relayUrl);\n if (index === -1) {\n logger.debug('Nostr', 'Relay not found:', relayUrl);\n return false;\n }\n\n // Remove from config\n this.config.relays.splice(index, 1);\n logger.debug('Nostr', 'Removed relay from config:', relayUrl);\n\n this.emitEvent({\n type: 'transport:relay_removed',\n timestamp: Date.now(),\n data: { relay: relayUrl },\n });\n\n // Check if we still have connections\n if (this.nostrClient && !this.nostrClient.isConnected() && this.status === 'connected') {\n this.status = 'error';\n this.emitEvent({\n type: 'transport:error',\n timestamp: Date.now(),\n data: { error: 'No connected relays remaining' },\n });\n }\n\n return true;\n }\n\n /**\n * Check if a relay is configured\n */\n hasRelay(relayUrl: string): boolean {\n return this.config.relays.includes(relayUrl);\n }\n\n /**\n * Check if a relay is currently connected\n */\n isRelayConnected(relayUrl: string): boolean {\n if (!this.nostrClient) return false;\n return this.nostrClient.getConnectedRelays().has(relayUrl);\n }\n\n // ===========================================================================\n // TransportProvider Implementation\n // ===========================================================================\n\n async setIdentity(identity: FullIdentity): Promise<void> {\n this.identity = identity;\n\n // Clear per-address state so stale dedup entries from previous address\n // don't block legitimate events for the new address.\n this.processedEventIds.clear();\n this.lastEventTs = 0;\n this.lastDmEventTs = 0;\n this.fallbackDmSince = null;\n\n // Create NostrKeyManager from private key\n const secretKey = Buffer.from(identity.privateKey, 'hex');\n this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);\n\n // Use Nostr-format pubkey (32 bytes / 64 hex chars) from keyManager\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Identity set, Nostr pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // If we already have a NostrClient with a temp key, we need to reconnect with the real key\n // NostrClient doesn't support changing key at runtime\n if (this.nostrClient && this.status === 'connected') {\n logger.debug('Nostr', 'Identity changed while connected - recreating NostrClient');\n const oldClient = this.nostrClient;\n\n // Create new client with real identity\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16,\n pingIntervalMs: 15000, // 15 second keepalive pings\n queryTimeoutMs: 20000, // see same option above for rationale\n });\n\n // Add connection event listener\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n },\n });\n\n // Connect with new identity, set up subscriptions, then disconnect old client\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport reconnection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n await this.subscribeToEvents();\n oldClient.disconnect();\n } else if (this.isConnected()) {\n // Already connected with right key, just subscribe\n await this.subscribeToEvents();\n }\n }\n\n setFallbackSince(sinceSeconds: number): void {\n this.fallbackSince = sinceSeconds;\n }\n\n setFallbackDmSince(sinceSeconds: number): void {\n this.fallbackDmSince = sinceSeconds;\n }\n\n /**\n * Get the Nostr-format public key (32 bytes / 64 hex chars)\n * This is the x-coordinate only, without the 02/03 prefix.\n */\n getNostrPubkey(): string {\n if (!this.keyManager) {\n throw new SphereError('KeyManager not initialized - call setIdentity first', 'NOT_INITIALIZED');\n }\n return this.keyManager.getPublicKeyHex();\n }\n\n async sendMessage(recipientPubkey: string, content: string): Promise<string> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Wrap content with sender nametag for Sphere app compatibility\n const senderNametag = this.identity?.nametag;\n const wrappedContent = senderNametag\n ? JSON.stringify({ senderNametag, text: content })\n : content;\n\n // Create NIP-17 gift-wrapped message (kind 1059) for recipient\n const giftWrap = NIP17.createGiftWrap(this.keyManager!, nostrRecipient, wrappedContent);\n\n await this.publishWithVerification(giftWrap, 3, 'dm');\n\n // NIP-17 self-wrap: send a copy to ourselves so relay can replay sent messages.\n // Content includes recipientPubkey and originalId for dedup against the live-sent record.\n const selfWrapContent = JSON.stringify({\n selfWrap: true,\n originalId: giftWrap.id,\n recipientPubkey,\n senderNametag,\n text: content,\n });\n const selfPubkey = this.keyManager!.getPublicKeyHex();\n const selfGiftWrap = NIP17.createGiftWrap(this.keyManager!, selfPubkey, selfWrapContent);\n this.publishEvent(selfGiftWrap).catch(err => {\n logger.debug('Nostr', 'Self-wrap publish failed:', err);\n });\n\n this.emitEvent({\n type: 'message:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return giftWrap.id;\n }\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.add(handler);\n\n // Flush any messages that arrived before this handler was registered\n if (this.pendingMessages.length > 0) {\n const pending = this.pendingMessages;\n this.pendingMessages = [];\n logger.debug('Nostr', 'Flushing', pending.length, 'buffered messages to new handler');\n for (const message of pending) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error (buffered):', error);\n }\n }\n }\n\n return () => this.messageHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Read Receipts\n // ===========================================================================\n\n async sendReadReceipt(recipientTransportPubkey: string, messageEventId: string): Promise<void> {\n if (!this.keyManager) throw new SphereError('Not initialized', 'NOT_INITIALIZED');\n\n // NIP-17 uses x-only pubkeys (64 hex chars, no 02/03 prefix)\n const nostrRecipient = recipientTransportPubkey.length === 66\n ? recipientTransportPubkey.slice(2)\n : recipientTransportPubkey;\n\n const event = NIP17.createReadReceipt(this.keyManager, nostrRecipient, messageEventId);\n await this.publishEvent(event);\n logger.debug('Nostr', 'Sent read receipt for:', messageEventId, 'to:', nostrRecipient.slice(0, 16));\n }\n\n onReadReceipt(handler: ReadReceiptHandler): () => void {\n this.readReceiptHandlers.add(handler);\n return () => this.readReceiptHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Typing Indicators\n // ===========================================================================\n\n async sendTypingIndicator(recipientTransportPubkey: string): Promise<void> {\n if (!this.keyManager) throw new SphereError('Not initialized', 'NOT_INITIALIZED');\n\n const nostrRecipient = recipientTransportPubkey.length === 66\n ? recipientTransportPubkey.slice(2)\n : recipientTransportPubkey;\n\n const content = JSON.stringify({\n type: 'typing',\n senderNametag: this.identity?.nametag,\n });\n const event = NIP17.createGiftWrap(this.keyManager, nostrRecipient, content);\n await this.publishEvent(event);\n }\n\n onTypingIndicator(handler: TypingIndicatorHandler): () => void {\n this.typingIndicatorHandlers.add(handler);\n return () => this.typingIndicatorHandlers.delete(handler);\n }\n\n onChatReady(handler: () => void): () => void {\n // If EOSE already fired, invoke immediately\n if (this.chatEoseFired) {\n try { handler(); } catch { /* ignore */ }\n return () => {};\n }\n this.chatEoseHandlers.push(handler);\n return () => {\n this.chatEoseHandlers = this.chatEoseHandlers.filter(h => h !== handler);\n };\n }\n\n // ===========================================================================\n // Composing Indicators (NIP-59 kind 25050)\n // ===========================================================================\n\n onComposing(handler: ComposingHandler): () => void {\n this.composingHandlers.add(handler);\n return () => this.composingHandlers.delete(handler);\n }\n\n async sendComposingIndicator(recipientPubkey: string, content: string): Promise<void> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Build NIP-17 gift wrap with kind 25050 rumor (instead of kind 14 for DMs).\n // We replicate the three-layer NIP-59 envelope because NIP17.createGiftWrap\n // hardcodes kind 14 for the inner rumor.\n const giftWrap = this.createCustomKindGiftWrap(nostrRecipient, content, COMPOSING_INDICATOR_KIND);\n await this.publishEvent(giftWrap);\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Routes to the appropriate specific resolve method based on identifier format.\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n // @nametag\n if (identifier.startsWith('@')) {\n return this.resolveNametagInfo(identifier.slice(1));\n }\n\n // DIRECT:// address\n if (identifier.startsWith('DIRECT:')) {\n return this.resolveAddressInfo(identifier);\n }\n\n // 66-char hex starting with 02/03 → compressed chain pubkey (33 bytes)\n if (/^0[23][0-9a-f]{64}$/i.test(identifier)) {\n return this.resolveAddressInfo(identifier);\n }\n\n // 64-char hex string → transport pubkey\n if (/^[0-9a-f]{64}$/i.test(identifier)) {\n return this.resolveTransportPubkeyInfo(identifier);\n }\n\n // Fallback: treat as bare nametag\n return this.resolveNametagInfo(identifier);\n }\n\n async resolveNametag(nametag: string): Promise<string | null> {\n await this.ensureConnectedForResolve();\n // Delegate to nostr-js-sdk, which resolves via UNIP-01 (prefers the\n // relay-vetted single-owner binding; see unicity-tokens-relay/docs/UNIP-01.md)\n return this.nostrClient!.queryPubkeyByNametag(nametag);\n }\n\n async resolveNametagInfo(nametag: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n // Delegate to nostr-js-sdk, which resolves via UNIP-01 (prefers the\n // relay-vetted single-owner binding; see unicity-tokens-relay/docs/UNIP-01.md)\n const binding = await this.nostrClient!.queryBindingByNametag(nametag);\n if (!binding) {\n logger.debug('Nostr', `resolveNametagInfo: no binding events found for Unicity ID \"${nametag}\"`);\n return null;\n }\n\n return this.bindingInfoToPeerInfo(binding, nametag);\n }\n\n /**\n * Resolve a DIRECT:// address to full peer info.\n * Performs reverse lookup via nostr-js-sdk (UNIP-01 marker-preferring resolution).\n */\n async resolveAddressInfo(address: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const binding = await this.nostrClient!.queryBindingByAddress(address);\n if (!binding) return null;\n\n return this.bindingInfoToPeerInfo(binding);\n }\n\n /**\n * Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).\n *\n * `network` IS DELIBERATELY ABSENT HERE — a live limitation, not an oversight\n * (#734). `queryBindingByNametag`/`queryBindingByAddress` are the whole public\n * resolution API for `@nametag` and `DIRECT://`, and both hardcode\n * `parseBindingInfo` inside the SDK's PRIVATE `queryWithFirstSeenWins`: the\n * caller never sees the signed event that resolution selected, and the parse\n * has already dropped every content field outside its whitelist. Re-querying\n * raw would mean re-deriving UNIP-01 ownership ourselves (marker preference,\n * cross-author first-seen-wins, ambiguity→null, multi-relay settle — all\n * private), and a second resolver that can disagree with the SDK's is worse\n * than a missing field on a MONEY guard. Nothing can declare a network today\n * regardless: the publish builders whitelist the same fields.\n *\n * CONSEQUENCE, stated plainly: for `@nametag` and `DIRECT://` recipients the\n * §5.6 cross-network guard can only SIGNAL (`recipient:network-unverified`),\n * never refuse. Upstream #734 must add `network` to the binding builders AND\n * to `BindingInfo`; the tripwire in\n * tests/unit/payments-v2/recipient-network.transport.test.ts fails the day it\n * lands, so this wiring cannot be forgotten. Do not \"fix\" this by inventing a\n * value — a stamped network is what made the guard unfalsifiable (#733).\n */\n private async bindingInfoToPeerInfo(binding: BindingInfo, nametag?: string): Promise<PeerInfo> {\n return {\n nametag: nametag || binding.nametag,\n transportPubkey: binding.transportPubkey,\n chainPubkey: binding.publicKey || '',\n directAddress: binding.directAddress || '',\n timestamp: binding.timestamp,\n };\n }\n\n /**\n * Resolve transport pubkey (Nostr pubkey) to full peer info.\n * Queries binding events authored by the given pubkey.\n */\n async resolveTransportPubkeyInfo(transportPubkey: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [transportPubkey],\n limit: 5,\n });\n\n if (events.length === 0) return null;\n\n // Sort by timestamp descending and take the most recent\n events.sort((a, b) => b.created_at - a.created_at);\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n // This route parses the signed event itself, so a declared network is\n // readable here — unlike the SDK-parsed @nametag/DIRECT:// routes above.\n const network = declaredNetwork(bindingEvent);\n\n return {\n nametag: content.nametag || undefined,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key || '',\n directAddress: content.direct_address || '',\n ...(network !== undefined ? { network } : {}),\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n return {\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n directAddress: '',\n timestamp: bindingEvent.created_at * 1000,\n };\n }\n }\n\n /**\n * Batch-resolve multiple transport pubkeys to peer info.\n * Used for HD address discovery — single relay query with multi-author filter.\n */\n async discoverAddresses(transportPubkeys: string[]): Promise<PeerInfo[]> {\n await this.ensureConnectedForResolve();\n\n if (transportPubkeys.length === 0) return [];\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: transportPubkeys,\n limit: transportPubkeys.length * 2,\n });\n\n if (events.length === 0) return [];\n\n // Group by author, take most recent per author\n const byAuthor = new Map<string, NostrEvent>();\n for (const event of events) {\n const existing = byAuthor.get(event.pubkey);\n if (!existing || event.created_at > existing.created_at) {\n byAuthor.set(event.pubkey, event);\n }\n }\n\n const results: PeerInfo[] = [];\n for (const [pubkey, event] of byAuthor) {\n try {\n const content = JSON.parse(event.content);\n const network = declaredNetwork(event);\n results.push({\n nametag: content.nametag || undefined,\n transportPubkey: pubkey,\n chainPubkey: content.public_key || '',\n directAddress: content.direct_address || '',\n ...(network !== undefined ? { network } : {}),\n timestamp: event.created_at * 1000,\n });\n } catch {\n // Skip unparseable events\n }\n }\n\n return results;\n }\n\n /**\n * Recover nametag for the current identity by searching for encrypted nametag events\n * Used after wallet import to recover associated nametag\n * @returns Decrypted nametag or null if none found\n */\n async recoverNametag(): Promise<string | null> {\n await this.ensureConnectedForResolve();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n if (!this.identity || !this.keyManager) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n logger.debug('Nostr', 'Searching for nametag events for pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // Query for nametag binding events authored by this pubkey\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [nostrPubkey],\n limit: 10, // Get recent events in case of updates\n });\n\n if (events.length === 0) {\n logger.debug('Nostr', 'No nametag events found for this pubkey');\n return null;\n }\n\n // Sort by timestamp descending to get most recent\n events.sort((a, b) => b.created_at - a.created_at);\n\n // Try to decrypt nametag from events\n for (const event of events) {\n try {\n const content = JSON.parse(event.content);\n if (content.encrypted_nametag) {\n const decrypted = await decryptNametag(\n content.encrypted_nametag,\n this.identity.privateKey\n );\n if (decrypted) {\n logger.debug('Nostr', 'Recovered Unicity ID:', decrypted);\n return decrypted;\n }\n }\n } catch {\n // Try next event\n continue;\n }\n }\n\n logger.debug('Nostr', 'Could not decrypt Unicity ID from any event');\n return null;\n }\n\n /**\n * Publish identity binding event on Nostr.\n * Without nametag: publishes base binding (chainPubkey, directAddress)\n * using a per-identity d-tag for address discovery.\n * With nametag: delegates to nostr-js-sdk's publishNametagBinding which handles\n * conflict detection (UNIP-01 single-owner), encryption, and indexed tags.\n *\n * @returns true if successful, false if nametag is taken by another pubkey\n */\n async publishIdentityBinding(\n chainPubkey: string,\n directAddress: string,\n nametag?: string,\n ): Promise<boolean> {\n this.ensureReady();\n\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n\n if (nametag) {\n // Delegate to nostr-js-sdk — handles conflict detection, encryption, and event creation\n try {\n const success = await this.nostrClient!.publishNametagBinding(\n nametag,\n nostrPubkey,\n {\n publicKey: chainPubkey,\n directAddress,\n },\n );\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding with Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n } catch (error) {\n // publishNametagBinding throws if nametag is already claimed\n if (error instanceof Error && error.message.includes('already claimed')) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag);\n return false;\n }\n throw error;\n }\n }\n\n // No nametag — delegate to nostr-js-sdk for base identity binding\n const success = await this.nostrClient!.publishIdentityBinding({\n publicKey: chainPubkey,\n directAddress,\n });\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding (no Unicity ID) for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n }\n\n // Track broadcast subscriptions\n private broadcastSubscriptions: Map<string, string> = new Map(); // key -> subId\n\n subscribeToBroadcast(tags: string[], handler: BroadcastHandler): () => void {\n const key = tags.sort().join(':');\n\n if (!this.broadcastHandlers.has(key)) {\n this.broadcastHandlers.set(key, new Set());\n\n // Subscribe to relay\n if (this.isConnected() && this.nostrClient) {\n this.subscribeToTags(tags);\n }\n }\n\n this.broadcastHandlers.get(key)!.add(handler);\n\n return () => {\n this.broadcastHandlers.get(key)?.delete(handler);\n if (this.broadcastHandlers.get(key)?.size === 0) {\n this.broadcastHandlers.delete(key);\n // Unsubscribe from relay\n const subId = this.broadcastSubscriptions.get(key);\n if (subId && this.nostrClient) {\n this.nostrClient.unsubscribe(subId);\n this.broadcastSubscriptions.delete(key);\n }\n }\n };\n }\n\n async publishBroadcast(content: string, tags?: string[]): Promise<string> {\n this.ensureReady();\n\n const eventTags = tags?.map((t) => ['t', t]) ?? [];\n const event = await this.createEvent(EVENT_KINDS.BROADCAST, content, eventTags);\n\n await this.publishEvent(event);\n return event.id;\n }\n\n // ===========================================================================\n // Event Subscription\n // ===========================================================================\n\n onEvent(callback: TransportEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n // ===========================================================================\n // Private: Message Handling\n // ===========================================================================\n\n private async handleEvent(event: NostrEvent): Promise<void> {\n // Dedup: skip events already processed by another subscription\n if (event.id && this.processedEventIds.has(event.id)) {\n return;\n }\n if (event.id) {\n this.processedEventIds.add(event.id);\n }\n\n logger.debug('Nostr', 'Processing event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n try {\n switch (event.kind) {\n case EVENT_KINDS.DIRECT_MESSAGE:\n await this.handleDirectMessage(event);\n break;\n case EventKinds.GIFT_WRAP:\n logger.debug('Nostr', 'Handling gift wrap (NIP-17 DM)');\n await this.handleGiftWrap(event);\n break;\n case EVENT_KINDS.BROADCAST:\n this.handleBroadcast(event);\n break;\n }\n\n // Persist the latest event timestamp for resumption on reconnect.\n // Only update for wallet event kinds (not chat/broadcast).\n if (event.created_at && this.storage && this.keyManager) {\n if (event.kind === EVENT_KINDS.DIRECT_MESSAGE) {\n this.updateLastEventTimestamp(event.created_at);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle event:', error);\n }\n }\n\n /**\n * Save the max event timestamp to storage (fire-and-forget, no await needed by caller).\n * Uses in-memory `lastEventTs` to avoid read-before-write race conditions\n * when multiple events arrive in quick succession.\n */\n private updateLastEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastEventTs) return;\n\n this.lastEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last event timestamp:', err);\n });\n }\n\n /** Persist the max DM (gift-wrap) event timestamp for the since filter on next connect. */\n private updateLastDmEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastDmEventTs) return;\n\n this.lastDmEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last DM event timestamp:', err);\n });\n }\n\n private async handleDirectMessage(event: NostrEvent): Promise<void> {\n // NIP-04 (kind 4) is deprecated for DMs - only used for legacy token transfers\n // DMs should come through NIP-17 (kind 1059 gift wrap) via handleGiftWrap\n // This handler is kept for backwards compatibility but does NOT dispatch to messageHandlers\n logger.debug('Nostr', 'Ignoring NIP-04 kind 4 event (DMs use NIP-17):', event.id?.slice(0, 12));\n }\n\n private async handleGiftWrap(event: NostrEvent): Promise<void> {\n if (!this.identity || !this.keyManager) {\n logger.debug('Nostr', 'handleGiftWrap: no identity/keyManager');\n return;\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pm = NIP17.unwrap(event as any, this.keyManager);\n\n // Persist DM timestamp after successful unwrap so failed decryptions\n // do not advance the since filter and permanently skip events.\n // Use real wall-clock time, NOT event.created_at — NIP-17 gift wraps\n // randomize created_at by ±2 days for privacy, so it can be in the future.\n this.updateLastDmEventTimestamp(Math.floor(Date.now() / 1000));\n\n logger.debug('Nostr', 'Gift wrap unwrapped, sender:', pm.senderPubkey?.slice(0, 16), 'kind:', pm.kind);\n\n // Handle self-wrap (sent message copy for relay replay)\n if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.selfWrap && parsed.recipientPubkey) {\n logger.debug('Nostr', 'Self-wrap replay for recipient:', parsed.recipientPubkey?.slice(0, 16));\n const message: IncomingMessage = {\n id: parsed.originalId || pm.eventId,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n recipientTransportPubkey: parsed.recipientPubkey,\n content: parsed.text ?? '',\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n isSelfWrap: true,\n };\n for (const handler of this.messageHandlers) {\n try { handler(message); } catch (e) { logger.debug('Nostr', 'Self-wrap handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON self-wrap\n }\n logger.debug('Nostr', 'Skipping own non-self-wrap message');\n return;\n }\n\n // Handle read receipts (kind 15)\n if (isReadReceipt(pm)) {\n logger.debug('Nostr', 'Read receipt from:', pm.senderPubkey?.slice(0, 16), 'for:', pm.replyToEventId);\n if (pm.replyToEventId) {\n const receipt: IncomingReadReceipt = {\n senderTransportPubkey: pm.senderPubkey,\n messageEventId: pm.replyToEventId,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.readReceiptHandlers) {\n try { handler(receipt); } catch (e) { logger.debug('Nostr', 'Read receipt handler error:', e); }\n }\n }\n return;\n }\n\n // Handle composing indicators (kind 25050)\n if (pm.kind === COMPOSING_INDICATOR_KIND) {\n let senderNametag: string | undefined;\n let expiresIn = 30000;\n try {\n const parsed = JSON.parse(pm.content);\n senderNametag = parsed.senderNametag || undefined;\n expiresIn = parsed.expiresIn ?? 30000;\n } catch {\n // Payload parse failed — use defaults\n }\n const indicator = {\n senderPubkey: pm.senderPubkey,\n senderNametag,\n expiresIn,\n };\n logger.debug('Nostr', 'Composing indicator from:', indicator.senderNametag || pm.senderPubkey?.slice(0, 16));\n for (const handler of this.composingHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Composing handler error:', e); }\n }\n return;\n }\n\n // Handle typing indicators (JSON content with type: 'typing')\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.type === 'typing') {\n logger.debug('Nostr', 'Typing indicator from:', pm.senderPubkey?.slice(0, 16));\n const indicator: IncomingTypingIndicator = {\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.typingIndicatorHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Typing handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON — continue to chat message handling\n }\n\n if (!isChatMessage(pm)) {\n logger.debug('Nostr', 'Skipping unknown message kind:', pm.kind);\n return;\n }\n\n // Sphere app wraps DM content as JSON: {senderNametag, text}\n let content = pm.content;\n let senderNametag: string | undefined;\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed === 'object' && parsed.text !== undefined) {\n content = parsed.text;\n senderNametag = parsed.senderNametag || undefined;\n }\n } catch {\n // Plain text — use as-is\n }\n\n logger.debug('Nostr', 'DM received from:', senderNametag || pm.senderPubkey?.slice(0, 16), 'content:', content?.slice(0, 50));\n\n const message: IncomingMessage = {\n // Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.\n // This ensures read receipts reference an ID the sender recognizes.\n id: event.id,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag,\n content,\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n };\n\n this.emitEvent({ type: 'message:received', timestamp: Date.now() });\n\n if (this.messageHandlers.size === 0) {\n logger.debug('Nostr', 'No message handlers registered, buffering message for later delivery');\n this.pendingMessages.push(message);\n } else {\n logger.debug('Nostr', 'Dispatching to', this.messageHandlers.size, 'handlers');\n for (const handler of this.messageHandlers) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error:', error);\n }\n }\n }\n } catch (err) {\n // Expected for gift wraps meant for other recipients\n logger.debug('Nostr', 'Gift wrap decrypt failed (expected if not for us):', (err as Error)?.message?.slice(0, 50));\n }\n }\n\n private handleBroadcast(event: NostrEvent): void {\n const tags = event.tags\n .filter((t: string[]) => t[0] === 't')\n .map((t: string[]) => t[1]);\n\n const broadcast: IncomingBroadcast = {\n id: event.id,\n authorTransportPubkey: event.pubkey,\n content: event.content,\n tags,\n timestamp: event.created_at * 1000,\n };\n\n // Find matching handlers\n for (const [key, handlers] of this.broadcastHandlers) {\n const subscribedTags = key.split(':');\n if (tags.some((t) => subscribedTags.includes(t))) {\n for (const handler of handlers) {\n try {\n handler(broadcast);\n } catch (error) {\n logger.debug('Nostr', 'Broadcast handler error:', error);\n }\n }\n }\n }\n }\n\n // ===========================================================================\n // Private: Event Creation & Publishing\n // ===========================================================================\n\n private async createEvent(\n kind: number,\n content: string,\n tags: string[][]\n ): Promise<NostrEvent> {\n if (!this.identity) throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Create and sign event using SDK\n const signedEvent = NostrEventClass.create(this.keyManager, {\n kind,\n content,\n tags,\n });\n\n // Convert to our interface\n const event: NostrEvent = {\n id: signedEvent.id,\n kind: signedEvent.kind,\n content: signedEvent.content,\n tags: signedEvent.tags,\n pubkey: signedEvent.pubkey,\n created_at: signedEvent.created_at,\n sig: signedEvent.sig,\n };\n\n return event;\n }\n\n private async createEncryptedEvent(\n kind: number,\n content: string,\n tags: string[][]\n ): Promise<NostrEvent> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Extract recipient pubkey from tags (first 'p' tag)\n const recipientTag = tags.find((t) => t[0] === 'p');\n if (!recipientTag || !recipientTag[1]) {\n throw new SphereError('No recipient pubkey in tags for encryption', 'VALIDATION_ERROR');\n }\n const recipientPubkey = recipientTag[1];\n\n // Encrypt content with NIP-04 (using hex variant for string keys)\n const encrypted = await NIP04.encryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n recipientPubkey\n );\n\n return this.createEvent(kind, encrypted, tags);\n }\n\n private async publishEvent(event: NostrEvent): Promise<void> {\n if (!this.nostrClient) {\n throw new SphereError('NostrClient not initialized', 'NOT_INITIALIZED');\n }\n\n const MAX_ATTEMPTS = 3;\n const RETRY_BASE_DELAY_MS = 500;\n const RETRY_JITTER_MS = 200;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n // Snapshot this.nostrClient at the top of each iteration. Using a local\n // reference (instead of `this.nostrClient.publishEvent(...)`) prevents a\n // concurrent disconnect() during the subsequent await from turning the\n // call into a TypeError on null.\n const client = this.nostrClient;\n if (!client) {\n throw new SphereError('Transport disconnected during retry', 'TRANSPORT_ERROR');\n }\n\n try {\n // Re-create SDK event each attempt to avoid reusing the same event ID\n // in nostr-js-sdk's pendingOks map (which would leak timers from prior attempts)\n const sdkEvent = NostrEventClass.fromJSON(event);\n await client.publishEvent(sdkEvent);\n return;\n } catch (err: unknown) {\n lastError = err;\n const rawMessage = err instanceof Error ? err.message : String(err);\n\n // Only retry relay-level rejections (nostr-js-sdk wraps these as \"Event rejected: <reason>\")\n // or relay broadcast failures (\"sent 0 of N\"). All other errors (disconnected, closed,\n // no connected relays) are non-transient and should fail immediately.\n // Locale-invariant match with `en-US` to avoid Turkish-locale surprises\n // where `'I'.toLowerCase()` yields `'ı'` (dotless i).\n const lowered = rawMessage.toLocaleLowerCase('en-US');\n const isRelayRejection =\n lowered.startsWith('event rejected:') ||\n lowered.startsWith('sent 0 of');\n\n if (!isRelayRejection || attempt === MAX_ATTEMPTS) {\n break;\n }\n\n // Add jitter to desynchronize retries across concurrent clients\n const delay = RETRY_BASE_DELAY_MS + Math.floor(Math.random() * RETRY_JITTER_MS);\n logger.debug(\n 'Nostr',\n `publishEvent attempt ${attempt}/${MAX_ATTEMPTS} failed (${rawMessage}); retrying in ${delay}ms`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n const reason = lastError instanceof Error ? lastError.message : String(lastError);\n logger.error('Nostr', `publishEvent failed after ${MAX_ATTEMPTS} attempts: ${reason}`);\n // Chain the original error as `cause` to preserve context for debugging\n throw new SphereError(\n `Failed to publish event: ${reason}`,\n 'TRANSPORT_ERROR',\n lastError\n );\n }\n\n /**\n * Publish an event with verification: after publishing, query the relay to\n * confirm the event was stored. Retries up to `maxAttempts` times on failure.\n *\n * This is critical for token transfers and DMs where silent loss means\n * funds or messages disappear. The nostr-js-sdk's publishEvent resolves on\n * a 5s timeout even without relay confirmation, so verification is needed.\n */\n private async publishWithVerification(\n event: NostrEvent,\n maxAttempts: number = 3,\n label: string = 'event',\n ): Promise<void> {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await this.publishEvent(event);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Permanent rejection — don't retry (e.g., \"Event rejected: invalid signature\")\n if (msg.includes('Event rejected') && !msg.includes('rate') && !msg.includes('limit')) {\n throw err;\n }\n if (attempt === maxAttempts) throw err;\n logger.debug('Nostr', `${label} publish attempt ${attempt} failed (${msg}), retrying in ${attempt}s...`);\n await new Promise(r => setTimeout(r, 1000 * attempt));\n continue;\n }\n\n // Verify: query the relay for this specific event by ID.\n // Jittered delay to let relay index + reduce temporal fingerprinting.\n await new Promise(r => setTimeout(r, 300 + Math.random() * 1200));\n try {\n const found = await this.queryEvents({\n ids: [event.id],\n limit: 1,\n });\n if (found.length > 0) {\n if (attempt > 1) {\n logger.debug('Nostr', `${label} verified on relay after ${attempt} attempt(s)`);\n }\n return; // confirmed on relay\n }\n } catch {\n // Query failed — can't verify. If this is the last attempt,\n // accept the publish as best-effort.\n if (attempt === maxAttempts) {\n logger.debug('Nostr', `${label} verification query failed — accepting publish as best-effort`);\n return;\n }\n }\n\n // Event not found on relay — retry with increasing backoff\n if (attempt < maxAttempts) {\n const delay = Math.min(2000 * attempt, 10000);\n logger.debug('Nostr', `${label} not found on relay, retrying in ${delay}ms (attempt ${attempt}/${maxAttempts})...`);\n await new Promise(r => setTimeout(r, delay));\n } else {\n throw new SphereError(\n `${label} not verified on relay after ${maxAttempts} attempts — delivery failed`,\n 'TRANSPORT_ERROR',\n );\n }\n }\n }\n\n async fetchPendingEvents(): Promise<void> {\n if (!this.nostrClient?.isConnected() || !this.keyManager) {\n throw new SphereError('Transport not connected', 'TRANSPORT_ERROR');\n }\n\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EventKinds.GIFT_WRAP, // NIP-17 gift-wrapped DMs\n ];\n walletFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have randomized created_at (±172800 s). Extend the\n // catch-up window by the full randomization range so events sent recently\n // but timestamped far in the past are not missed.\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400 - 172800;\n\n // Capture client reference after the guard to avoid a null-deref race:\n // a concurrent disconnect() can set this.nostrClient = null between the\n // isConnected() check above and the subscribe() call below.\n const client = this.nostrClient;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n // Declared outside the Promise so it's available for cleanup after resolve.\n let subId: string | undefined;\n\n await new Promise<void>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n\n // settle() only resolves the Promise — unsubscribe happens AFTER the Promise\n // resolves (below), where subId is guaranteed to be the real subscription ID.\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n };\n\n try {\n subId = client.subscribe(walletFilter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n if (!settled) {\n timeout = setTimeout(() => settle(), 5000);\n }\n });\n\n // Unsubscribe AFTER the Promise resolves — subId is now the real subscription ID.\n // This also ensures onEvent cannot fire while we iterate events below.\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n\n // Process collected events sequentially (dedup skips already-processed ones)\n for (const event of events) {\n await this.handleEvent(event);\n }\n }\n\n private async queryEvents(filterObj: NostrFilter): Promise<NostrEvent[]> {\n if (!this.nostrClient || !this.nostrClient.isConnected()) {\n throw new SphereError('No connected relays', 'TRANSPORT_ERROR');\n }\n\n // Capture client reference after the guard — same disconnect() race as fetchPendingEvents.\n const client = this.nostrClient;\n\n const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n // Declared outside the Promise so unsubscription can happen after resolve(),\n // where subId is guaranteed to be the real subscription ID (not in TDZ).\n let subId: string | undefined;\n\n return new Promise((resolve) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n if (subId) {\n try { client.unsubscribe(subId); } catch { /* disconnected */ }\n }\n logger.warn('Nostr', `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 15000);\n\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n resolve(events);\n };\n\n try {\n subId = client.subscribe(filter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch {\n clearTimeout(timeout);\n resolve(events);\n return;\n }\n\n });\n }\n\n // ===========================================================================\n // Private: Subscriptions\n // ===========================================================================\n\n // Track subscription IDs for cleanup\n private walletSubscriptionId: string | null = null;\n private chatSubscriptionId: string | null = null;\n\n // Chat EOSE handlers — fired once when relay finishes delivering stored DMs\n private chatEoseHandlers: Array<() => void> = [];\n private chatEoseFired = false;\n\n private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\n if (this._subscriptionsSuppressed) {\n logger.debug('Nostr', 'subscribeToEvents: suppressed — mux handles event routing');\n return;\n }\n if (!this.identity || !this.keyManager || !this.nostrClient) {\n logger.debug('Nostr', 'subscribeToEvents: skipped - no identity, keyManager, or nostrClient');\n return;\n }\n\n // Unsubscribe from previous subscriptions if any\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Use 32-byte Nostr pubkey (x-coordinate only), not 33-byte compressed key\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Subscribing with Nostr pubkey:', nostrPubkey);\n\n // Determine 'since' filter from persisted last event timestamp.\n // - Existing wallet: resume from last processed event (inclusive >=, dedup handles replays)\n // - Fresh wallet / no storage: use current time (no historical replay)\n let since: number;\n if (this.storage) {\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(storageKey);\n if (stored) {\n since = parseInt(stored, 10);\n this.lastEventTs = since; // Seed in-memory tracker from storage\n this.fallbackSince = null; // Stored value takes priority\n logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else if (this.fallbackSince !== null) {\n // No stored timestamp but caller provided a fallback (e.g. address creation time).\n // This ensures events sent while the address was inactive are not missed.\n since = this.fallbackSince;\n this.lastEventTs = since;\n this.fallbackSince = null; // Consume once\n logger.debug('Nostr', 'Using fallback since timestamp:', since);\n } else {\n // No stored timestamp, no fallback = fresh wallet, start from now\n since = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored timestamp, starting from now:', since);\n }\n } catch (err) {\n logger.debug('Nostr', 'Failed to read last event timestamp, falling back to now:', err);\n since = Math.floor(Date.now() / 1000);\n this.fallbackSince = null;\n }\n } else {\n // No storage adapter — fallback to last 24h (legacy behavior)\n since = Math.floor(Date.now() / 1000) - 86400;\n logger.debug('Nostr', 'No storage adapter, using 24h fallback');\n }\n\n // Subscribe to wallet events (legacy kind-4 DMs) with since filter\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n ];\n walletFilter['#p'] = [nostrPubkey];\n walletFilter.since = since;\n\n this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received wallet event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Wallet subscription ready (EOSE)');\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Wallet subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Wallet subscription created, subId:', this.walletSubscriptionId);\n\n // Determine 'since' for DM (gift-wrap) subscription independently from wallet events.\n let dmSince: number;\n if (this.storage) {\n const dmStorageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(dmStorageKey);\n const parsed = stored ? parseInt(stored, 10) : NaN;\n if (Number.isFinite(parsed)) {\n dmSince = parsed;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Stored value takes priority\n logger.debug('Nostr', 'DM resuming from stored timestamp:', dmSince);\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Consume once\n logger.debug('Nostr', 'DM using fallback since timestamp:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored DM timestamp, starting from now:', dmSince);\n }\n } catch (err) {\n if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'Storage read failed, using DM fallback since:', dmSince, err);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'Failed to read last DM event timestamp, falling back to now:', err);\n }\n }\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'No storage adapter for DM, using fallback since:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No storage adapter for DM, starting from now:', dmSince);\n }\n\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have created_at randomized ±2 days for privacy.\n // Without this offset, ~50% of messages are silently dropped by the relay\n // because their randomized timestamp lands before the `since` filter.\n // Math.max(0, ...) prevents negative timestamps when dmSince is small.\n chatFilter.since = Math.max(0, dmSince - TIMESTAMP_RANDOMIZATION);\n\n this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received chat event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Chat subscription ready (EOSE)');\n if (!this.chatEoseFired) {\n this.chatEoseFired = true;\n for (const handler of this.chatEoseHandlers) {\n try { handler(); } catch { /* ignore */ }\n }\n this.chatEoseHandlers = [];\n }\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Chat subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Chat subscription created, subId:', this.chatSubscriptionId);\n }\n\n private subscribeToTags(tags: string[]): void {\n if (!this.nostrClient) return;\n\n const key = tags.sort().join(':');\n const filter = new Filter({\n kinds: [EVENT_KINDS.BROADCAST],\n '#t': tags,\n since: Math.floor(Date.now() / 1000) - 3600, // Last hour\n });\n\n const subId = this.nostrClient.subscribe(filter, {\n onEvent: (event) => {\n this.handleBroadcast({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n });\n\n this.broadcastSubscriptions.set(key, subId);\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private async decryptContent(content: string, senderPubkey: string): Promise<string> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Decrypt content using NIP-04 (using hex variant for string keys)\n const decrypted = await NIP04.decryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n senderPubkey\n );\n\n // Strip known prefixes for compatibility with nostr-js-sdk\n return this.stripContentPrefix(decrypted);\n }\n\n /**\n * Strip known content prefixes (nostr-js-sdk compatibility)\n * Handles: payment_request:, token_transfer:, etc.\n */\n private stripContentPrefix(content: string): string {\n const prefixes = [\n 'payment_request:',\n 'token_transfer:',\n 'payment_response:',\n ];\n\n for (const prefix of prefixes) {\n if (content.startsWith(prefix)) {\n return content.slice(prefix.length);\n }\n }\n\n return content;\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (!this.isConnected()) {\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n }\n\n /**\n * Async version of ensureConnected — reconnects if the original transport\n * lost its WebSocket while subscriptions are suppressed (mux handles events).\n * Used by resolve methods which are always async.\n */\n private async ensureConnectedForResolve(): Promise<void> {\n if (this.isConnected()) return;\n\n // When mux is active, the original transport can lose its idle WebSocket.\n // Reconnect transparently so resolve operations still work.\n if (this._subscriptionsSuppressed && this.nostrClient) {\n logger.debug('Nostr', 'Suppressed transport disconnected — reconnecting for resolve');\n try {\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('reconnect timeout')), 5000)\n ),\n ]);\n if (this.nostrClient.isConnected()) {\n this.status = 'connected';\n return;\n }\n } catch {\n // Fall through to throw\n }\n }\n\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n\n private ensureReady(): void {\n this.ensureConnected();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n }\n\n private emitEvent(event: TransportEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n logger.debug('Nostr', 'Event callback error:', error);\n }\n }\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom inner rumor kind.\n * Replicates the three-layer NIP-59 envelope (rumor → seal → gift wrap)\n * because NIP17.createGiftWrap hardcodes kind 14 for the inner rumor.\n */\n private createCustomKindGiftWrap(recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n return NostrTransportProvider.createCustomKindGiftWrap(this.keyManager!, recipientPubkeyHex, content, rumorKind);\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom rumor kind.\n * Shared between NostrTransportProvider and MultiAddressTransportMux.\n */\n static createCustomKindGiftWrap(keyManager: NostrKeyManager, recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n const senderPubkey = keyManager.getPublicKeyHex();\n const now = Math.floor(Date.now() / 1000);\n\n // 1. Create Rumor (unsigned inner event with custom kind)\n const rumorTags: string[][] = [['p', recipientPubkeyHex]];\n const rumorSerialized = JSON.stringify([0, senderPubkey, now, rumorKind, rumorTags, content]);\n const rumorId = bytesToHex(sha256Noble(new TextEncoder().encode(rumorSerialized)));\n const rumor = { id: rumorId, pubkey: senderPubkey, created_at: now, kind: rumorKind, tags: rumorTags, content };\n\n // 2. Create Seal (kind 13, signed by sender, encrypts rumor)\n const recipientPubkeyBytes = hexToBytes(recipientPubkeyHex);\n const encryptedRumor = NIP44.encrypt(JSON.stringify(rumor), keyManager.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(keyManager, {\n kind: EventKinds.SEAL,\n tags: [],\n content: encryptedRumor,\n created_at: sealTimestamp,\n });\n\n // 3. Create Gift Wrap (kind 1059, signed by ephemeral key, encrypts seal)\n const ephemeralKeys = NostrKeyManager.generate();\n const encryptedSeal = NIP44.encrypt(JSON.stringify(seal.toJSON()), ephemeralKeys.getPrivateKey(), recipientPubkeyBytes);\n const wrapTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const giftWrap = NostrEventClass.create(ephemeralKeys, {\n kind: EventKinds.GIFT_WRAP,\n tags: [['p', recipientPubkeyHex]],\n content: encryptedSeal,\n created_at: wrapTimestamp,\n });\n ephemeralKeys.clear();\n\n return giftWrap;\n }\n\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface NostrEvent {\n id: string;\n kind: number;\n content: string;\n tags: string[][];\n pubkey: string;\n created_at: number;\n sig: string;\n}\n\ninterface NostrFilter {\n ids?: string[];\n authors?: string[];\n kinds?: number[];\n '#p'?: string[];\n '#t'?: string[];\n '#d'?: string[];\n since?: number;\n until?: number;\n limit?: number;\n}\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 §5.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 §6.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n","/**\n * WebSocket Abstraction\n * Platform-independent WebSocket interface for cross-platform support\n */\n\n// =============================================================================\n// WebSocket Interface\n// =============================================================================\n\n/**\n * Minimal WebSocket interface compatible with browser and Node.js\n */\nexport interface IWebSocket {\n readonly readyState: number;\n\n send(data: string): void;\n close(code?: number, reason?: string): void;\n\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: IMessageEvent) => void) | null;\n}\n\nexport interface IMessageEvent {\n data: string;\n}\n\n/**\n * WebSocket ready states (same as native WebSocket)\n */\nexport const WebSocketReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Factory function to create WebSocket instances\n * Different implementations for browser (native) vs Node.js (ws package)\n */\nexport type WebSocketFactory = (url: string) => IWebSocket;\n\n// =============================================================================\n// UUID Generator\n// =============================================================================\n\n/**\n * Generate a unique ID (platform-independent)\n * Browser: crypto.randomUUID()\n * Node: crypto.randomUUID() or uuid package\n */\nexport type UUIDGenerator = () => string;\n\n/**\n * Default UUID generator using crypto.randomUUID\n * Works in modern browsers and Node 19+\n */\nexport function defaultUUIDGenerator(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","/**\n * Browser Transport Exports\n * Re-exports shared transport with browser-specific WebSocket factory\n */\n\nimport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export shared types and classes\nexport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\n\nexport {\n type IWebSocket,\n type IMessageEvent,\n type WebSocketFactory,\n type UUIDGenerator,\n WebSocketReadyState,\n defaultUUIDGenerator,\n} from '../../../transport/websocket';\n\n// =============================================================================\n// Browser WebSocket Factory\n// =============================================================================\n\n/**\n * Browser WebSocket factory using native WebSocket\n * Cast to IWebSocket since native WebSocket is a superset\n */\nexport function createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create NostrTransportProvider with browser WebSocket\n * Convenience factory that injects browser-native WebSocket\n */\nexport function createNostrTransportProvider(\n config?: Omit<NostrTransportProviderConfig, 'createWebSocket'>\n): NostrTransportProvider {\n return new NostrTransportProvider({\n ...config,\n createWebSocket: createBrowserWebSocket,\n });\n}\n","/**\n * Unicity Aggregator Provider\n *\n * Post v1-cutover this is a thin NETWORK-CONFIG provider for the v2 token\n * engine: it loads the root trust base (JSON) via the injected platform loader\n * and exposes the gateway URL + API key. The engine (token-engine/) builds its\n * own SDK clients from these — no state-transition SDK objects live here.\n *\n * TrustBaseLoader is injected for platform-specific loading:\n * - Browser: fetch from URL\n * - Node.js: read from file\n */\n\nimport { logger } from '../core/logger';\nimport type { ProviderStatus } from '../types';\nimport type {\n OracleProvider,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT } from '../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator (gateway) URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base loading (dev only) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n /** Trust base loader (platform-specific) */\n trustBaseLoader?: TrustBaseLoader;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class UnicityAggregatorProvider implements OracleProvider {\n readonly id = 'unicity-aggregator';\n readonly name = 'Unicity Aggregator';\n readonly type = 'network' as const;\n readonly description = 'Unicity gateway network config (trust base + URL + API key) for the v2 token engine';\n\n private config: Required<Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'>> & {\n trustBaseLoader?: TrustBaseLoader;\n };\n private status: ProviderStatus = 'disconnected';\n private eventCallbacks: Set<OracleEventCallback> = new Set();\n\n /** Raw trust-base JSON as loaded (the v2 token engine parses it itself). */\n private trustBaseJson: unknown | null = null;\n\n\n constructor(config: UnicityAggregatorProviderConfig) {\n this.config = {\n url: config.url,\n apiKey: config.apiKey ?? '',\n timeout: config.timeout ?? DEFAULT_AGGREGATOR_TIMEOUT,\n skipVerification: config.skipVerification ?? false,\n debug: config.debug ?? false,\n trustBaseLoader: config.trustBaseLoader,\n };\n }\n\n // ===========================================================================\n // v2 token-engine config surface\n // ===========================================================================\n\n /** Raw trust-base JSON (for constructing the v2 token engine); null when unavailable. */\n getTrustBaseJson(): unknown | null {\n return this.trustBaseJson;\n }\n\n /** The gateway URL this provider is configured against. */\n getAggregatorUrl(): string {\n return this.config.url;\n }\n\n /** The gateway API key (for the v2 token engine); undefined when none is configured. */\n getApiKey(): string | undefined {\n return this.config.apiKey || undefined;\n }\n\n /**\n * Update the API key on this live provider. The v2 token engine snapshots the\n * key at build time, so this alone does NOT change money operations already\n * wired into a running engine — `Sphere.setOracleApiKey()` rebuilds the engine\n * for that. Paths that read `getApiKey()` fresh (e.g. the Unicity-ID minter)\n * pick up the new value immediately.\n */\n setApiKey(apiKey: string): void {\n this.config.apiKey = apiKey ?? '';\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n // Mark as connected — actual connectivity is verified on first operation.\n this.status = 'connected';\n this.emitEvent({ type: 'oracle:connected', timestamp: Date.now() });\n this.log('Connected to oracle:', this.config.url);\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.emitEvent({ type: 'oracle:disconnected', timestamp: Date.now() });\n this.log('Disconnected from oracle');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // OracleProvider Implementation\n // ===========================================================================\n\n async initialize(trustBaseJson?: unknown): Promise<void> {\n if (trustBaseJson) {\n this.trustBaseJson = trustBaseJson;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n try {\n const loaded = await this.config.trustBaseLoader.load();\n if (loaded) {\n this.trustBaseJson = loaded;\n }\n } catch (error) {\n this.log('Failed to load trust base:', error);\n }\n }\n\n await this.connect();\n this.log('Initialized with trust base JSON:', !!this.trustBaseJson);\n }\n\n // ===========================================================================\n // Event Subscription\n // ===========================================================================\n\n onEvent(callback: OracleEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private emitEvent(event: OracleEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n this.log('Event callback error:', error);\n }\n }\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('Aggregator', message, ...args);\n }\n}\n\n// =============================================================================\n// Backward Compatibility Aliases (Oracle -> Aggregator)\n// =============================================================================\n\n/** @deprecated Use UnicityAggregatorProvider instead */\nexport const UnicityOracleProvider = UnicityAggregatorProvider;\n/** @deprecated Use UnicityAggregatorProviderConfig instead */\nexport type UnicityOracleProviderConfig = UnicityAggregatorProviderConfig;\n","/**\n * Embedded Trust Base Data\n * Pre-loaded trust base for different networks\n */\n\nexport const TRUSTBASE_TESTNET = {\n version: 1,\n networkId: 3,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU',\n sigKey: '0x039afb2acb65f5fbc272d8907f763d0a5d189aadc9b97afdcc5897ea4dd112e68b',\n stake: 1,\n },\n ],\n quorumThreshold: 1,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU':\n '0xf157c9fdd8a378e3ca70d354ccc4475ab2cd8de360127bc46b0aeab4b453a80f07fd9136a5843b60a8babaff23e20acc8879861f7651440a5e2829f7541b31f100',\n },\n};\n\n// Testnet2 (networkId 4) — embedded copy of bft-trustbase.testnet2.json\nexport const TRUSTBASE_TESTNET2 = {\n version: 1,\n networkId: 4,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy',\n sigKey: '0x02b2b1daf4dee562ef5ed5b89e1e0e6cf0bf952ce3af12be6531e40f8bd1a7e682',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M',\n sigKey: '0x02822d7ce59d2ff4f2da4cef361d5a80a3a74c351fe61a8a2c908a54841930cee0',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC',\n sigKey: '0x0352ef027d60921e252839bea60e2f1810473fb0ad8cb89044b101aecdea5f356a',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF',\n sigKey: '0x02ef71c49c77ca9510ad84596522b887e4df0b3262fce80192ddcfb6276efdf901',\n stake: 1,\n },\n ],\n quorumThreshold: 3,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy':\n '0xe9289562b9faf283ae264ce42ef641133237bca6b7f232e1e293305f0cf994d3262cee1ca9c8de93fc5b7da1466486123b6219e5308843b8ead173472dad35e501',\n '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M':\n '0x45e26943305c264d5dc5c3069cd5dbd2d43feb35d56d580656b80b33f4126e2a735b0c8eace0ef638128d9dbf5eb854dd9ad3ba42f9eafded60251bf7edb454501',\n '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC':\n '0xe2263c1cb24b6d933ead249901c7fd83fa884d95b935a63f1489b07ea342281071cbf2117ad27be29cde9207d8d87e8b331630718d92d644faf2fccbc9dba4c000',\n '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF':\n '0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301',\n },\n};\n\n// Mainnet trust base (TODO: add when available)\nexport const TRUSTBASE_MAINNET = null;\n\n// Dev trust base (same as testnet for now)\nexport const TRUSTBASE_DEV = TRUSTBASE_TESTNET;\n","/**\n * Shared TrustBase Loader Logic\n * Common embedded trustbase data and base loader\n */\n\nimport { TRUSTBASE_TESTNET2, TRUSTBASE_MAINNET, TRUSTBASE_DEV } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\nimport { SphereError } from '../../core/errors';\n\nexport interface TrustBaseLoader {\n load(): Promise<unknown | null>;\n}\n\n/**\n * Get embedded trustbase data by network\n */\nexport function getEmbeddedTrustBase(network: NetworkType): unknown | null {\n switch (network) {\n case 'mainnet':\n return TRUSTBASE_MAINNET;\n // v1 cutover: 'testnet' is now an alias of testnet2 (NETWORKS.testnet points\n // at the testnet2 gateway), so it MUST resolve the testnet2 trust base —\n // a mismatched trust base would make the engine reject every proof.\n case 'testnet':\n case 'testnet2':\n return TRUSTBASE_TESTNET2;\n case 'dev':\n return TRUSTBASE_DEV;\n default:\n // Fail loud: an unknown network must not silently resolve to the testnet trust base.\n throw new SphereError(`getEmbeddedTrustBase: unknown network \"${network}\"`, 'INVALID_CONFIG');\n }\n}\n\n/**\n * Base TrustBase loader with embedded fallback\n */\nexport abstract class BaseTrustBaseLoader implements TrustBaseLoader {\n protected network: NetworkType;\n\n constructor(network: NetworkType = 'testnet') {\n this.network = network;\n }\n\n /**\n * Try to load from external source (file, URL, etc.)\n * Override in subclass\n */\n protected abstract loadFromExternal(): Promise<unknown | null>;\n\n async load(): Promise<unknown | null> {\n // Try external source first\n const external = await this.loadFromExternal();\n if (external) {\n return external;\n }\n\n // Fallback to embedded data\n return getEmbeddedTrustBase(this.network);\n }\n}\n","/**\n * Browser Oracle Exports\n * Re-exports shared oracle with browser-specific TrustBaseLoader\n */\n\nimport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\nimport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\nimport { BaseTrustBaseLoader } from '../../shared/trustbase-loader';\nimport type { NetworkType } from '../../../constants';\nimport { SphereError } from '../../../core/errors';\n\n// Re-export shared types and classes\nexport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n UnicityOracleProvider,\n type UnicityOracleProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\n\nexport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\n\n// =============================================================================\n// Browser TrustBase Loader\n// =============================================================================\n\n/**\n * Browser TrustBase loader - fetches from URL or uses embedded data\n */\nexport class BrowserTrustBaseLoader extends BaseTrustBaseLoader {\n private url?: string;\n\n /**\n * @param networkOrUrl - a NetworkType, or a URL to fetch the trust base from\n * @param fallbackNetwork - network used for the embedded fallback when a URL is\n * supplied (and the fetch fails). Required when networkOrUrl is a URL — we must\n * never silently fall back to testnet on a different network.\n */\n constructor(networkOrUrl?: NetworkType | string, fallbackNetwork?: NetworkType) {\n if (!networkOrUrl) {\n throw new SphereError('BrowserTrustBaseLoader: network or trustBaseUrl is required.', 'INVALID_CONFIG');\n }\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n if (!fallbackNetwork) {\n throw new SphereError(\n 'BrowserTrustBaseLoader: fallbackNetwork is required when a trustBaseUrl is supplied.',\n 'INVALID_CONFIG',\n );\n }\n super(fallbackNetwork);\n this.url = networkOrUrl;\n } else {\n super(networkOrUrl as NetworkType);\n }\n }\n\n protected async loadFromExternal(): Promise<unknown | null> {\n if (!this.url) return null;\n\n try {\n const response = await fetch(this.url);\n if (response.ok) {\n return await response.json();\n }\n } catch {\n // Fall through to embedded\n }\n return null;\n }\n}\n\n/**\n * Create browser TrustBase loader\n */\nexport function createBrowserTrustBaseLoader(\n networkOrUrl?: NetworkType | string,\n fallbackNetwork?: NetworkType,\n): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl, fallbackNetwork);\n}\n\n// =============================================================================\n// Browser Factory\n// =============================================================================\n\n/**\n * Create UnicityAggregatorProvider with browser TrustBase loader\n */\nexport function createUnicityAggregatorProvider(\n config: Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'> & {\n trustBaseUrl?: string;\n network?: NetworkType;\n }\n): UnicityAggregatorProvider {\n const { trustBaseUrl, network, ...restConfig } = config;\n // Fail loud: without a network we cannot pick the right embedded trust base.\n if (!trustBaseUrl && !network) {\n throw new SphereError(\n 'createUnicityAggregatorProvider: network or trustBaseUrl required.',\n 'INVALID_CONFIG',\n );\n }\n return new UnicityAggregatorProvider({\n ...restConfig,\n // When a URL is supplied, `network` (if any) is the embedded-fallback network.\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network, network),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\n","// The wallet-api composition preset: a plain CONFIG (WalletApiTransportConfig)\n// that Sphere.init consumes directly and fails closed without.\n\nimport type { StorageProvider } from '../../../storage';\nimport type { TransportProvider } from '../../../transport';\nimport type { OracleProvider } from '../../../oracle';\nimport type { WalletApiTransportConfig } from '../../../core/payments-v2-wiring';\n\nexport type { WalletApiTransportConfig } from '../../../core/payments-v2-wiring';\n\n/** The minimum any platform base bundle provides (browser/node factories do). */\nexport interface SphereBaseProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n}\n\nexport interface SphereProviderPorts {\n /** Engine/aggregator config port override */\n engine?: OracleProvider;\n}\n\n/** Compose a bundle with the engine port selected independently. */\nexport function createSphereProviders<B extends SphereBaseProviders>(\n base: B,\n ports: SphereProviderPorts = {}\n): B {\n return {\n ...base,\n ...(ports.engine ? { oracle: ports.engine } : {}),\n };\n}\n\nexport interface WalletApiCompositionConfig {\n baseUrl: string;\n network: string;\n deviceId?: string;\n fetchFn?: unknown;\n webSocketFactory?: unknown;\n paymentsV2Transport?: WalletApiTransportConfig['paymentsV2Transport'];\n}\n\n/** Attach the transport config the payments vertical is composed from; pass the result to `Sphere.init`. */\nexport function createWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & { walletApi: WalletApiTransportConfig } {\n const walletApi: WalletApiTransportConfig = {\n network: config.network,\n baseUrl: config.baseUrl,\n ...(config.deviceId !== undefined ? { deviceId: config.deviceId } : {}),\n ...(config.fetchFn !== undefined ? { fetchFn: config.fetchFn } : {}),\n ...(config.webSocketFactory !== undefined ? { webSocketFactory: config.webSocketFactory } : {}),\n ...(config.paymentsV2Transport !== undefined ? { paymentsV2Transport: config.paymentsV2Transport } : {}),\n };\n return { ...base, walletApi };\n}\n","/**\n * Browser Download Utilities\n * Functions for downloading wallet backups as files\n */\n\nimport type { Sphere } from '../../core/Sphere';\nimport type { WalletJSON, WalletJSONExportOptions } from '../../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DownloadTextOptions {\n /** Password for encryption */\n password?: string;\n /** Number of addresses to include */\n addressCount?: number;\n /** Custom filename (without extension) */\n filename?: string;\n}\n\nexport interface DownloadJSONOptions extends WalletJSONExportOptions {\n /** Custom filename (without extension) */\n filename?: string;\n /** Pretty print JSON (default: true) */\n pretty?: boolean;\n}\n\n// =============================================================================\n// Core Download Function\n// =============================================================================\n\n/**\n * Download content as a file in the browser\n */\nexport function downloadFile(\n content: string | Blob,\n filename: string,\n mimeType: string = 'text/plain'\n): void {\n const blob = content instanceof Blob\n ? content\n : new Blob([content], { type: mimeType });\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n\n link.href = url;\n link.download = filename;\n link.click();\n\n // Clean up the URL after a short delay\n setTimeout(() => URL.revokeObjectURL(url), 100);\n}\n\n/**\n * Download text content as a .txt file\n */\nexport function downloadTextFile(content: string, filename: string): void {\n downloadFile(content, filename, 'text/plain');\n}\n\n/**\n * Download JSON content as a .json file\n */\nexport function downloadJSONFile(content: object | string, filename: string): void {\n const jsonString = typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2);\n downloadFile(jsonString, filename, 'application/json');\n}\n\n// =============================================================================\n// Wallet Download Functions\n// =============================================================================\n\n/**\n * Download wallet backup as text file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletText(sphere);\n *\n * // Download encrypted backup\n * downloadWalletText(sphere, { password: 'secret' });\n *\n * // Custom filename\n * downloadWalletText(sphere, { filename: 'my-backup' });\n * ```\n */\nexport function downloadWalletText(sphere: Sphere, options: DownloadTextOptions = {}): void {\n const content = sphere.exportToTxt({\n password: options.password,\n addressCount: options.addressCount,\n });\n\n const filename = options.filename\n ? `${options.filename}.txt`\n : `sphere-wallet-${Date.now()}.txt`;\n\n downloadTextFile(content, filename);\n}\n\n/**\n * Download wallet backup as JSON file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletJSON(sphere);\n *\n * // Download encrypted backup\n * downloadWalletJSON(sphere, { password: 'secret' });\n *\n * // Include multiple addresses\n * downloadWalletJSON(sphere, { addressCount: 5 });\n * ```\n */\nexport function downloadWalletJSON(sphere: Sphere, options: DownloadJSONOptions = {}): void {\n const json = sphere.exportToJSON({\n password: options.password,\n addressCount: options.addressCount,\n includeMnemonic: options.includeMnemonic,\n });\n\n const filename = options.filename\n ? `${options.filename}.json`\n : `sphere-wallet-${Date.now()}.json`;\n\n const jsonString = options.pretty !== false\n ? JSON.stringify(json, null, 2)\n : JSON.stringify(json);\n\n downloadFile(jsonString, filename, 'application/json');\n}\n\n/**\n * Download pre-built WalletJSON as file\n */\nexport function downloadWalletJSONData(json: WalletJSON, filename?: string): void {\n const name = filename || `sphere-wallet-${Date.now()}.json`;\n downloadJSONFile(json, name.endsWith('.json') ? name : `${name}.json`);\n}\n\n// =============================================================================\n// File Reading Utilities\n// =============================================================================\n\n/**\n * Read a file as text\n */\nexport function readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\n/**\n * Read a file as ArrayBuffer (for binary files like .dat)\n */\nexport function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(file);\n });\n}\n\n/**\n * Read a file as Uint8Array (for binary files like .dat)\n */\nexport async function readFileAsUint8Array(file: File): Promise<Uint8Array> {\n const buffer = await readFileAsArrayBuffer(file);\n return new Uint8Array(buffer);\n}\n","/**\n * Centralized network-config resolution + a startup consistency assertion.\n *\n * Principle: resolve a network's full configuration in ONE place, and refuse to\n * run a network whose configuration is provably broken (e.g. a null or mismatched\n * trust base would silently accept unverified tokens — a funds risk).\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport { getEmbeddedTrustBase } from './trustbase-loader';\nimport { SphereError } from '../../core/errors';\n\n/**\n * Known trust-base networkId per network. The embedded trust base for a network\n * MUST carry this id; a mismatch means the wrong trust base is wired to a network\n * (e.g. the testnet trust base under testnet2), which would verify tokens against\n * the wrong chain. mainnet/dev have no pinned id yet (mainnet is not onboarded).\n */\nconst EXPECTED_NETWORK_ID: Partial<Record<NetworkType, number>> = {\n // v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),\n // so both expect the testnet2 trust base (networkId 4).\n testnet: 4,\n testnet2: 4,\n};\n\nexport interface ResolvedNetworkConfig {\n readonly network: NetworkType;\n readonly config: NetworkConfig;\n readonly trustBase: unknown | null;\n readonly networkId: number | undefined;\n}\n\n/** Resolve a network's full config + embedded trust base in one place. */\nexport function resolveNetworkConfig(network: NetworkType): ResolvedNetworkConfig {\n const config = NETWORKS[network];\n const trustBase = getEmbeddedTrustBase(network);\n const networkId = (trustBase as { networkId?: number } | null)?.networkId;\n return { network, config, trustBase, networkId };\n}\n\n/**\n * Fail loud if a network is unsafe to run. Conservative on purpose: it rejects\n * only provably-broken configs, so currently-valid networks (testnet/testnet2/dev)\n * keep working while mainnet — which has no embedded trust base — is blocked until\n * onboarded (otherwise it would silently accept unverified tokens). The stricter\n * hygiene checks (e.g. no cross-network registry reuse) live in the per-network CI\n * test, not at runtime, so they can't break a valid deployment.\n */\nexport function assertNetworkConsistency(network: NetworkType): void {\n const { trustBase, networkId } = resolveNetworkConfig(network);\n if (trustBase == null) {\n throw new SphereError(\n `Network \"${network}\" has no embedded trust base — refusing to run (tokens would be unverified).`,\n 'INVALID_CONFIG',\n );\n }\n const expected = EXPECTED_NETWORK_ID[network];\n if (expected !== undefined && networkId !== expected) {\n throw new SphereError(\n `Network \"${network}\" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,\n 'INVALID_CONFIG',\n );\n }\n}\n","/**\n * Abort / timeout signal helpers.\n *\n * Kept in a dedicated, dependency-free module (no node:crypto fallback like\n * core/utils.ts) so importing it never pulls a Node builtin into the\n * browser-platform bundles (impl/browser/*, impl/shared/wallet-api).\n */\n\n/**\n * Create an {@link AbortSignal} that aborts after `ms` milliseconds.\n *\n * Prefers the native `AbortSignal.timeout` (Chrome 103+, Firefox 100+,\n * Safari 16+ / iOS 16+). Falls back to an `AbortController` + `setTimeout` on\n * runtimes that lack it — older mobile Safari (iOS 15), older Android WebViews,\n * and in-app/embedded browsers — which would otherwise throw\n * `AbortSignal.timeout is not a function` the moment a timed fetch or engine\n * op runs (sphere-sdk#617: Swap / Top Up crash). The native fast-path is kept\n * for modern runtimes; the fallback aborts with a `TimeoutError` to match\n * native semantics.\n */\nexport function timeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n return AbortSignal.timeout(ms);\n }\n const controller = new AbortController();\n setTimeout(() => {\n controller.abort(\n typeof DOMException !== 'undefined'\n ? new DOMException('The operation timed out.', 'TimeoutError')\n : new Error('The operation timed out.'),\n );\n }, ms);\n return controller.signal;\n}\n","/**\n * CoinGecko Price Provider\n *\n * Fetches token prices from CoinGecko API with internal caching.\n * Supports both free and pro API tiers.\n * Optionally persists cache to StorageProvider for survival across page reloads.\n */\n\nimport { logger } from '../core/logger';\nimport { SphereError } from '../core/errors';\nimport { timeoutSignal } from '../core/timeout';\nimport { STORAGE_KEYS_GLOBAL } from '../constants';\nimport type { StorageProvider } from '../storage';\nimport type { PriceProvider, PricePlatform, TokenPrice, PriceProviderConfig } from './price-provider';\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface CacheEntry {\n price: TokenPrice;\n expiresAt: number;\n}\n\n/** Serializable format for persistent storage */\ninterface PersistedPriceCache {\n [tokenName: string]: TokenPrice;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * CoinGecko price provider\n *\n * @example\n * ```ts\n * // Free tier (no API key)\n * const provider = new CoinGeckoPriceProvider();\n *\n * // Pro tier\n * const provider = new CoinGeckoPriceProvider({ apiKey: 'CG-xxx' });\n *\n * // With persistent cache (survives page reloads)\n * const provider = new CoinGeckoPriceProvider({ storage: myStorageProvider });\n *\n * const prices = await provider.getPrices(['bitcoin', 'ethereum']);\n * console.log(prices.get('bitcoin')?.priceUsd);\n * ```\n */\nexport class CoinGeckoPriceProvider implements PriceProvider {\n readonly platform: PricePlatform = 'coingecko';\n\n private readonly cache: Map<string, CacheEntry> = new Map();\n private readonly apiKey?: string;\n private readonly cacheTtlMs: number;\n private readonly timeout: number;\n private readonly debug: boolean;\n private readonly baseUrl: string;\n private readonly storage: StorageProvider | null;\n\n /** In-flight fetch promise for deduplication of concurrent getPrices() calls */\n private fetchPromise: Promise<Map<string, TokenPrice>> | null = null;\n /** Token names being fetched in the current in-flight request */\n private fetchNames: Set<string> | null = null;\n /** Whether persistent cache has been loaded into memory */\n private persistentCacheLoaded = false;\n /** Promise for loading persistent cache (deduplication) */\n private loadCachePromise: Promise<void> | null = null;\n\n constructor(config?: Omit<PriceProviderConfig, 'platform'>) {\n this.apiKey = config?.apiKey;\n this.cacheTtlMs = config?.cacheTtlMs ?? 60_000;\n this.timeout = config?.timeout ?? 10_000;\n this.debug = config?.debug ?? false;\n this.storage = config?.storage ?? null;\n\n this.baseUrl = config?.baseUrl\n ?? (this.apiKey\n ? 'https://pro-api.coingecko.com/api/v3'\n : 'https://api.coingecko.com/api/v3');\n }\n\n async getPrices(tokenNames: string[]): Promise<Map<string, TokenPrice>> {\n if (tokenNames.length === 0) {\n return new Map();\n }\n\n // Load persistent cache on first call (once)\n if (!this.persistentCacheLoaded && this.storage) {\n await this.loadFromStorage();\n }\n\n const now = Date.now();\n const result = new Map<string, TokenPrice>();\n const uncachedNames: string[] = [];\n\n // Check cache first\n for (const name of tokenNames) {\n const cached = this.cache.get(name);\n if (cached && cached.expiresAt > now) {\n result.set(name, cached.price);\n } else {\n uncachedNames.push(name);\n }\n }\n\n // All cached — return immediately\n if (uncachedNames.length === 0) {\n return result;\n }\n\n // Deduplicate concurrent calls: if an in-flight fetch covers all needed tokens, reuse it\n if (this.fetchPromise && this.fetchNames) {\n const allCovered = uncachedNames.every((n) => this.fetchNames!.has(n));\n if (allCovered) {\n if (this.debug) {\n logger.debug('CoinGecko', 'Deduplicating request, reusing in-flight fetch');\n }\n const fetched = await this.fetchPromise;\n for (const name of uncachedNames) {\n const price = fetched.get(name);\n if (price) {\n result.set(name, price);\n }\n }\n return result;\n }\n }\n\n // Fetch uncached prices\n const fetchPromise = this.doFetch(uncachedNames);\n this.fetchPromise = fetchPromise;\n this.fetchNames = new Set(uncachedNames);\n\n try {\n const fetched = await fetchPromise;\n for (const [name, price] of fetched) {\n result.set(name, price);\n }\n } finally {\n // Clear in-flight state only if this is still the current request\n if (this.fetchPromise === fetchPromise) {\n this.fetchPromise = null;\n this.fetchNames = null;\n }\n }\n\n return result;\n }\n\n private async doFetch(uncachedNames: string[]): Promise<Map<string, TokenPrice>> {\n const result = new Map<string, TokenPrice>();\n const now = Date.now();\n\n try {\n const ids = uncachedNames.join(',');\n const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;\n\n const headers: Record<string, string> = { Accept: 'application/json' };\n if (this.apiKey) {\n headers['x-cg-pro-api-key'] = this.apiKey;\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Fetching prices for: ${uncachedNames.join(', ')}`);\n }\n\n const response = await fetch(url, {\n headers,\n signal: timeoutSignal(this.timeout),\n });\n\n if (!response.ok) {\n // On rate-limit, extend existing cache entries to avoid hammering the API\n if (response.status === 429) {\n this.extendCacheOnRateLimit(uncachedNames);\n }\n throw new SphereError(`CoinGecko API error: ${response.status} ${response.statusText}`, 'NETWORK_ERROR');\n }\n\n const data = await response.json() as Record<string, Record<string, number>>;\n\n // Parse and cache response\n for (const [name, values] of Object.entries(data)) {\n if (values && typeof values === 'object') {\n const price: TokenPrice = {\n tokenName: name,\n priceUsd: values.usd ?? 0,\n priceEur: values.eur,\n change24h: values.usd_24h_change,\n timestamp: now,\n };\n this.cache.set(name, { price, expiresAt: now + this.cacheTtlMs });\n result.set(name, price);\n }\n }\n\n // Tokens not found on CoinGecko: cache with zero prices to avoid re-requesting\n // and to persist in storage (so page reloads don't trigger unnecessary API calls)\n for (const name of uncachedNames) {\n if (!result.has(name)) {\n const zeroPrice: TokenPrice = {\n tokenName: name,\n priceUsd: 0,\n priceEur: 0,\n change24h: 0,\n timestamp: now,\n };\n this.cache.set(name, { price: zeroPrice, expiresAt: now + this.cacheTtlMs });\n result.set(name, zeroPrice);\n }\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Fetched ${result.size} prices`);\n }\n\n // Persist to storage (fire-and-forget)\n this.saveToStorage();\n } catch (error) {\n if (this.debug) {\n logger.warn('CoinGecko', 'Fetch failed, using stale cache:', error);\n }\n\n // On error, return stale cached data if available\n for (const name of uncachedNames) {\n const stale = this.cache.get(name);\n if (stale) {\n result.set(name, stale.price);\n }\n }\n }\n\n return result;\n }\n\n // ===========================================================================\n // Persistent Storage\n // ===========================================================================\n\n /**\n * Load cached prices from StorageProvider into in-memory cache.\n * Only loads entries that are still within cacheTtlMs.\n */\n private async loadFromStorage(): Promise<void> {\n // Deduplicate concurrent loads\n if (this.loadCachePromise) {\n return this.loadCachePromise;\n }\n this.loadCachePromise = this.doLoadFromStorage();\n try {\n await this.loadCachePromise;\n } finally {\n this.loadCachePromise = null;\n }\n }\n\n private async doLoadFromStorage(): Promise<void> {\n this.persistentCacheLoaded = true;\n if (!this.storage) return;\n\n try {\n const [cached, cachedTs] = await Promise.all([\n this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE),\n this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS),\n ]);\n\n if (!cached || !cachedTs) return;\n\n const ts = parseInt(cachedTs, 10);\n if (isNaN(ts)) return;\n\n // Only use if within TTL\n const age = Date.now() - ts;\n if (age > this.cacheTtlMs) return;\n\n const data: PersistedPriceCache = JSON.parse(cached);\n const expiresAt = ts + this.cacheTtlMs;\n\n for (const [name, price] of Object.entries(data)) {\n // Only populate if not already in memory (in-memory is always fresher)\n if (!this.cache.has(name)) {\n this.cache.set(name, { price, expiresAt });\n }\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Loaded ${Object.keys(data).length} prices from persistent cache`);\n }\n } catch {\n // Cache load failure is non-critical\n }\n }\n\n /**\n * Save current prices to StorageProvider (fire-and-forget).\n */\n private saveToStorage(): void {\n if (!this.storage) return;\n\n const data: PersistedPriceCache = {};\n for (const [name, entry] of this.cache) {\n data[name] = entry.price;\n }\n\n // Fire-and-forget\n Promise.all([\n this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),\n this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now())),\n ]).catch((err) => logger.debug('Price', 'Cache save failed (non-critical)', err));\n }\n\n // ===========================================================================\n // Rate-limit handling\n // ===========================================================================\n\n /**\n * On 429 rate-limit, extend stale cache entries so subsequent calls\n * don't immediately retry and hammer the API.\n */\n private extendCacheOnRateLimit(names: string[]): void {\n const backoffMs = 60_000; // 1 minute backoff on rate-limit\n const extendedExpiry = Date.now() + backoffMs;\n\n for (const name of names) {\n const existing = this.cache.get(name);\n if (existing) {\n existing.expiresAt = Math.max(existing.expiresAt, extendedExpiry);\n }\n }\n\n if (this.debug) {\n logger.warn('CoinGecko', `Rate-limited (429), extended cache TTL by ${backoffMs / 1000}s`);\n }\n }\n\n async getPrice(tokenName: string): Promise<TokenPrice | null> {\n const prices = await this.getPrices([tokenName]);\n return prices.get(tokenName) ?? null;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n","/**\n * Price Provider\n *\n * Token market price abstraction with CoinGecko implementation.\n */\n\nexport type {\n PriceProvider,\n PricePlatform,\n TokenPrice,\n PriceProviderConfig,\n} from './price-provider';\n\nexport { CoinGeckoPriceProvider } from './CoinGeckoPriceProvider';\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nimport type { PriceProviderConfig, PriceProvider } from './price-provider';\nimport { CoinGeckoPriceProvider } from './CoinGeckoPriceProvider';\nimport { SphereError } from '../core/errors';\n\n/**\n * Create a price provider based on platform configuration\n *\n * @example\n * ```ts\n * const provider = createPriceProvider({ platform: 'coingecko', apiKey: 'CG-xxx' });\n * ```\n */\nexport function createPriceProvider(config: PriceProviderConfig): PriceProvider {\n switch (config.platform) {\n case 'coingecko':\n return new CoinGeckoPriceProvider(config);\n default:\n throw new SphereError(`Unsupported price platform: ${String(config.platform)}`, 'INVALID_CONFIG');\n }\n}\n","/**\n * Token Registry\n *\n * Provides token definitions (metadata) for known tokens on the Unicity network.\n * Fetches from a remote URL, caches in StorageProvider, and refreshes periodically.\n */\n\nimport { logger } from '../core/logger';\nimport { TOKEN_REGISTRY_REFRESH_INTERVAL, STORAGE_KEYS_GLOBAL } from '../constants';\nimport type { StorageProvider } from '../storage';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Icon entry for token\n */\nexport interface TokenIcon {\n url: string;\n}\n\n/**\n * Token definition with full metadata\n */\nexport interface TokenDefinition {\n /** Network identifier (e.g., \"unicity:testnet\") */\n network: string;\n /** Asset kind - fungible or non-fungible */\n assetKind: 'fungible' | 'non-fungible';\n /** Token name (e.g., \"bitcoin\", \"ethereum\") */\n name: string;\n /** Token symbol (e.g., \"BTC\", \"ETH\") - only for fungible tokens */\n symbol?: string;\n /** Decimal places for display - only for fungible tokens */\n decimals?: number;\n /** Human-readable description */\n description: string;\n /** Icon URLs array */\n icons?: TokenIcon[];\n /** Hex-encoded coin ID (64 characters) */\n id: string;\n}\n\n/**\n * Network type for registry lookup\n */\nexport type RegistryNetwork = 'testnet' | 'mainnet' | 'dev';\n\n/**\n * Configuration options for remote registry refresh\n */\nexport interface TokenRegistryConfig {\n /** Remote URL to fetch token definitions from */\n remoteUrl?: string;\n /** StorageProvider for persistent caching */\n storage?: StorageProvider;\n /** Refresh interval in ms (default: 1 hour) */\n refreshIntervalMs?: number;\n /** Start auto-refresh immediately (default: true) */\n autoRefresh?: boolean;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst FETCH_TIMEOUT_MS = 10_000;\n\n// =============================================================================\n// Registry Implementation\n// =============================================================================\n\n/**\n * Token Registry service\n *\n * Provides lookup functionality for token definitions by coin ID.\n * Uses singleton pattern for efficient memory usage.\n *\n * Data flow:\n * 1. On `configure()`: load cached definitions from StorageProvider (if fresh)\n * 2. Fetch from remote URL in background\n * 3. On successful fetch: update in-memory maps + persist to StorageProvider\n * 4. Repeat every `refreshIntervalMs` (default 1 hour)\n *\n * If no cache and no network — registry is empty (lookup methods return fallbacks).\n *\n * @example\n * ```ts\n * import { TokenRegistry } from '@unicitylabs/sphere-sdk';\n *\n * // Usually called automatically by createBrowserProviders / createNodeProviders\n * TokenRegistry.configure({\n * remoteUrl: 'https://raw.githubusercontent.com/.../unicity-ids.testnet.json',\n * storage: myStorageProvider,\n * });\n *\n * const registry = TokenRegistry.getInstance();\n * const def = registry.getDefinition('455ad87...');\n * console.log(def?.symbol); // 'UCT'\n * ```\n */\nexport class TokenRegistry {\n private static instance: TokenRegistry | null = null;\n\n private readonly definitionsById: Map<string, TokenDefinition>;\n private readonly definitionsBySymbol: Map<string, TokenDefinition>;\n private readonly definitionsByName: Map<string, TokenDefinition>;\n\n // Remote refresh state\n private remoteUrl: string | null = null;\n private storage: StorageProvider | null = null;\n private refreshIntervalMs: number = TOKEN_REGISTRY_REFRESH_INTERVAL;\n private refreshTimer: ReturnType<typeof setInterval> | null = null;\n private lastRefreshAt: number = 0;\n private refreshPromise: Promise<boolean> | null = null;\n private initialLoadPromise: Promise<boolean> | null = null;\n\n private constructor() {\n this.definitionsById = new Map();\n this.definitionsBySymbol = new Map();\n this.definitionsByName = new Map();\n }\n\n /**\n * Get singleton instance of TokenRegistry\n */\n static getInstance(): TokenRegistry {\n if (!TokenRegistry.instance) {\n TokenRegistry.instance = new TokenRegistry();\n }\n return TokenRegistry.instance;\n }\n\n /**\n * Configure remote registry refresh with persistent caching.\n *\n * On first call:\n * 1. Loads cached data from StorageProvider (if available and fresh)\n * 2. Starts periodic remote fetch (if autoRefresh is true, which is default)\n *\n * @param options - Configuration options\n * @param options.remoteUrl - Remote URL to fetch definitions from\n * @param options.storage - StorageProvider for persistent caching\n * @param options.refreshIntervalMs - Refresh interval in ms (default: 1 hour)\n * @param options.autoRefresh - Start auto-refresh immediately (default: true)\n */\n static configure(options: TokenRegistryConfig): void {\n const instance = TokenRegistry.getInstance();\n\n if (options.remoteUrl !== undefined) {\n instance.remoteUrl = options.remoteUrl;\n }\n if (options.storage !== undefined) {\n instance.storage = options.storage;\n }\n if (options.refreshIntervalMs !== undefined) {\n instance.refreshIntervalMs = options.refreshIntervalMs;\n }\n\n const autoRefresh = options.autoRefresh ?? true;\n\n // Perform initial load (cache → remote fallback) and store the promise\n // so consumers can await readiness via TokenRegistry.waitForReady()\n instance.initialLoadPromise = instance.performInitialLoad(autoRefresh);\n }\n\n /**\n * Reset the singleton instance (useful for testing).\n * Stops auto-refresh if running.\n */\n static resetInstance(): void {\n if (TokenRegistry.instance) {\n TokenRegistry.instance.stopAutoRefresh();\n }\n TokenRegistry.instance = null;\n }\n\n /**\n * Destroy the singleton: stop auto-refresh and reset.\n */\n static destroy(): void {\n TokenRegistry.resetInstance();\n }\n\n /**\n * Wait for the initial data load (cache or remote) to complete.\n * Returns true if data was loaded, false if not (timeout or no data source).\n *\n * @param timeoutMs - Maximum wait time in ms (default: 10s). Set to 0 for no timeout.\n */\n static async waitForReady(timeoutMs: number = 10_000): Promise<boolean> {\n const instance = TokenRegistry.getInstance();\n if (!instance.initialLoadPromise) {\n return instance.definitionsById.size > 0;\n }\n if (timeoutMs <= 0) {\n return instance.initialLoadPromise;\n }\n return Promise.race([\n instance.initialLoadPromise,\n new Promise<boolean>((resolve) => setTimeout(() => resolve(false), timeoutMs)),\n ]);\n }\n\n // ===========================================================================\n // Initial Load\n // ===========================================================================\n\n /**\n * Perform initial data load: try cache first, fall back to remote fetch.\n * After initial data is available, start periodic auto-refresh if configured.\n */\n private async performInitialLoad(autoRefresh: boolean): Promise<boolean> {\n // Step 1: Try loading from cache\n let loaded = false;\n if (this.storage) {\n loaded = await this.loadFromCache();\n }\n\n if (loaded) {\n // Cache hit — start auto-refresh in background (includes immediate remote fetch)\n if (autoRefresh && this.remoteUrl) {\n this.startAutoRefresh();\n }\n return true;\n }\n\n // Step 2: Cache miss — wait for first remote fetch (only when auto-refresh is enabled)\n if (autoRefresh && this.remoteUrl) {\n loaded = await this.refreshFromRemote();\n // Start periodic refresh (skip immediate since we just fetched)\n this.stopAutoRefresh();\n this.refreshTimer = setInterval(() => {\n this.refreshFromRemote();\n }, this.refreshIntervalMs);\n return loaded;\n }\n\n return false;\n }\n\n // ===========================================================================\n // Cache (StorageProvider)\n // ===========================================================================\n\n /**\n * Per-network cache keys. The registry cache MUST be namespaced by remoteUrl:\n * testnet and testnet2 ship DIFFERENT coinIds for the same symbol, so a single\n * global key let a stale testnet snapshot be served for a testnet2 request —\n * wrong/missing icons AND self-mint resolving the wrong-network coinId. Keying\n * by remoteUrl isolates the snapshots so a network can never read another's.\n */\n private cacheKeys(): { data: string; ts: string } {\n const base = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE;\n const baseTs = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS;\n // No remoteUrl configured (local-only usage): keep the legacy bare key.\n if (!this.remoteUrl) return { data: base, ts: baseTs };\n // Namespace by remoteUrl so testnet and testnet2 snapshots never collide.\n return { data: `${base}:${this.remoteUrl}`, ts: `${baseTs}:${this.remoteUrl}` };\n }\n\n /**\n * Load definitions from StorageProvider cache.\n * Only applies if cache exists and is fresh (within refreshIntervalMs).\n */\n private async loadFromCache(): Promise<boolean> {\n if (!this.storage) return false;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n const [cached, cachedTs] = await Promise.all([\n this.storage.get(dataKey),\n this.storage.get(tsKey),\n ]);\n\n if (!cached || !cachedTs) return false;\n\n const ts = parseInt(cachedTs, 10);\n if (isNaN(ts)) return false;\n\n // Check freshness\n const age = Date.now() - ts;\n if (age > this.refreshIntervalMs) return false;\n\n // Don't overwrite data from a more recent remote fetch\n if (this.lastRefreshAt > ts) return false;\n\n const data: unknown = JSON.parse(cached);\n if (!this.isValidDefinitionsArray(data)) return false;\n\n this.applyDefinitions(data as TokenDefinition[]);\n this.lastRefreshAt = ts;\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Save definitions to StorageProvider cache.\n */\n private async saveToCache(definitions: TokenDefinition[]): Promise<void> {\n if (!this.storage) return;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n await Promise.all([\n this.storage.set(dataKey, JSON.stringify(definitions)),\n this.storage.set(tsKey, String(Date.now())),\n ]);\n } catch {\n // Cache save failure is non-critical\n }\n }\n\n // ===========================================================================\n // Remote Refresh\n // ===========================================================================\n\n /**\n * Apply an array of token definitions to the internal maps.\n * Clears existing data before applying.\n */\n private applyDefinitions(definitions: TokenDefinition[]): void {\n this.definitionsById.clear();\n this.definitionsBySymbol.clear();\n this.definitionsByName.clear();\n\n for (const def of definitions) {\n const idLower = def.id.toLowerCase();\n this.definitionsById.set(idLower, def);\n\n if (def.symbol) {\n this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);\n }\n\n this.definitionsByName.set(def.name.toLowerCase(), def);\n }\n }\n\n /**\n * Validate that data is an array of objects with 'id' field\n */\n private isValidDefinitionsArray(data: unknown): boolean {\n return Array.isArray(data) && data.every((item) => item && typeof item === 'object' && 'id' in item);\n }\n\n /**\n * Fetch token definitions from the remote URL and update the registry.\n * On success, also persists to StorageProvider cache.\n * Returns true on success, false on failure. On failure, existing data is preserved.\n * Concurrent calls are deduplicated — only one fetch runs at a time.\n */\n async refreshFromRemote(): Promise<boolean> {\n if (!this.remoteUrl) {\n return false;\n }\n\n // Deduplicate concurrent calls\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doRefresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doRefresh(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(this.remoteUrl!, {\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n logger.warn('TokenRegistry', `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);\n return false;\n }\n\n const data: unknown = await response.json();\n\n if (!this.isValidDefinitionsArray(data)) {\n logger.warn('TokenRegistry', 'Remote data is not a valid token definitions array');\n return false;\n }\n\n const definitions = data as TokenDefinition[];\n this.applyDefinitions(definitions);\n this.lastRefreshAt = Date.now();\n\n // Persist to cache (fire-and-forget)\n this.saveToCache(definitions);\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn('TokenRegistry', `Remote refresh failed: ${message}`);\n return false;\n }\n }\n\n /**\n * Start periodic auto-refresh from the remote URL.\n * Does an immediate fetch, then repeats at the configured interval.\n */\n startAutoRefresh(intervalMs?: number): void {\n this.stopAutoRefresh();\n\n if (intervalMs !== undefined) {\n this.refreshIntervalMs = intervalMs;\n }\n\n // Immediate first fetch (fire-and-forget)\n this.refreshFromRemote();\n\n this.refreshTimer = setInterval(() => {\n this.refreshFromRemote();\n }, this.refreshIntervalMs);\n }\n\n /**\n * Stop periodic auto-refresh\n */\n stopAutoRefresh(): void {\n if (this.refreshTimer !== null) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n /**\n * Timestamp of the last successful remote refresh (0 if never refreshed)\n */\n getLastRefreshAt(): number {\n return this.lastRefreshAt;\n }\n\n // ===========================================================================\n // Lookup Methods\n // ===========================================================================\n\n /**\n * Get token definition by hex coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined if not found\n */\n getDefinition(coinId: string): TokenDefinition | undefined {\n if (!coinId) return undefined;\n return this.definitionsById.get(coinId.toLowerCase());\n }\n\n /**\n * Get token definition by symbol (e.g., \"UCT\", \"BTC\")\n * @param symbol - Token symbol (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionBySymbol(symbol: string): TokenDefinition | undefined {\n if (!symbol) return undefined;\n return this.definitionsBySymbol.get(symbol.toUpperCase());\n }\n\n /**\n * Get token definition by name (e.g., \"bitcoin\", \"ethereum\")\n * @param name - Token name (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionByName(name: string): TokenDefinition | undefined {\n if (!name) return undefined;\n return this.definitionsByName.get(name.toLowerCase());\n }\n\n /**\n * Get token symbol for a coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol (e.g., \"UCT\") or truncated ID if not found\n */\n getSymbol(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.symbol) {\n return def.symbol;\n }\n // Fallback: return first 6 chars of ID uppercased\n return coinId.slice(0, 6).toUpperCase();\n }\n\n /**\n * Get token name for a coin ID\n * @param coinId - 64-character hex string\n * @returns Name (e.g., \"Bitcoin\") or coin ID if not found\n */\n getName(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.name) {\n // Capitalize first letter\n return def.name.charAt(0).toUpperCase() + def.name.slice(1);\n }\n return coinId;\n }\n\n /**\n * Get decimal places for a coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0 if not found\n */\n getDecimals(coinId: string): number {\n const def = this.getDefinition(coinId);\n return def?.decimals ?? 0;\n }\n\n /**\n * Get icon URL for a coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG format over SVG\n * @returns Icon URL or null if not found\n */\n getIconUrl(coinId: string, preferPng = true): string | null {\n const def = this.getDefinition(coinId);\n if (!def?.icons || def.icons.length === 0) {\n return null;\n }\n\n if (preferPng) {\n const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes('.png'));\n if (pngIcon) return pngIcon.url;\n }\n\n return def.icons[0].url;\n }\n\n /**\n * Check if a coin ID is known in the registry\n * @param coinId - 64-character hex string\n * @returns true if the coin is in the registry\n */\n isKnown(coinId: string): boolean {\n return this.definitionsById.has(coinId.toLowerCase());\n }\n\n /**\n * Get all token definitions\n * @returns Array of all token definitions\n */\n getAllDefinitions(): TokenDefinition[] {\n return Array.from(this.definitionsById.values());\n }\n\n /**\n * Get all fungible token definitions\n * @returns Array of fungible token definitions\n */\n getFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'fungible');\n }\n\n /**\n * Get all non-fungible token definitions\n * @returns Array of non-fungible token definitions\n */\n getNonFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'non-fungible');\n }\n\n /**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdBySymbol(symbol: string): string | undefined {\n const def = this.getDefinitionBySymbol(symbol);\n return def?.id;\n }\n\n /**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdByName(name: string): string | undefined {\n const def = this.getDefinitionByName(name);\n return def?.id;\n }\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Get token definition by coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined\n */\nexport function getTokenDefinition(coinId: string): TokenDefinition | undefined {\n return TokenRegistry.getInstance().getDefinition(coinId);\n}\n\n/**\n * Get token symbol by coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol or truncated ID\n */\nexport function getTokenSymbol(coinId: string): string {\n return TokenRegistry.getInstance().getSymbol(coinId);\n}\n\n/**\n * Get token name by coin ID\n * @param coinId - 64-character hex string\n * @returns Name or coin ID\n */\nexport function getTokenName(coinId: string): string {\n return TokenRegistry.getInstance().getName(coinId);\n}\n\n/**\n * Get token decimals by coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0\n */\nexport function getTokenDecimals(coinId: string): number {\n return TokenRegistry.getInstance().getDecimals(coinId);\n}\n\n/**\n * Get token icon URL by coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG over SVG\n * @returns Icon URL or null\n */\nexport function getTokenIconUrl(coinId: string, preferPng = true): string | null {\n return TokenRegistry.getInstance().getIconUrl(coinId, preferPng);\n}\n\n/**\n * Check if coin ID is in registry\n * @param coinId - 64-character hex string\n * @returns true if known\n */\nexport function isKnownToken(coinId: string): boolean {\n return TokenRegistry.getInstance().isKnown(coinId);\n}\n\n/**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdBySymbol(symbol: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdBySymbol(symbol);\n}\n\n/**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdByName(name: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdByName(name);\n}\n\n/**\n * Normalize a coin identifier to its canonical hash coinId.\n *\n * Accepts both symbolic names (\"BTC\", \"ETH\") and hash coinIds (64-char hex).\n * If the input is a short symbol, resolves it via the TokenRegistry.\n * Returns the input unchanged if it's already a hash coinId or unknown.\n *\n * @public\n *\n * @remarks\n * Heuristic: inputs matching `length <= 20 && /^[A-Za-z0-9]+$/` are treated\n * as symbolic names and looked up in the registry. Long inputs, hyphenated\n * inputs (\"TOKEN-123\"), or dotted inputs (\"BTC.CASH\") pass through unchanged.\n *\n * **Stability:** This function depends on `TokenRegistry` content. Adding a\n * new symbol that shadows a previously-unknown short alphanumeric coinId\n * changes normalization semantics retroactively. Consumers building stable\n * keys against this output should be aware that registry growth is a\n * non-breaking change *for unknown inputs* but a breaking change *for inputs\n * that newly resolve*.\n *\n * @param coinId - A symbolic name or hash coinId.\n * @returns The canonical hash coinId, or the original string if not resolvable.\n */\nexport function normalizeCoinId(coinId: string): string {\n // Short alphanumeric strings are likely symbolic names (BTC, ETH, USDU, etc.)\n if (coinId.length <= 20 && /^[A-Za-z0-9]+$/.test(coinId)) {\n const resolved = getCoinIdBySymbol(coinId);\n if (resolved) return resolved;\n }\n return coinId;\n}\n\n/**\n * Compare two coin identifiers for equality, normalizing both sides.\n *\n * Handles mixed-format comparisons: \"BTC\" vs hash coinId, or two hash coinIds.\n * Both values are normalized to hash coinIds via the TokenRegistry before comparison.\n *\n * @public\n *\n * @remarks\n * Reflexive byte-equality short-circuit comes first; otherwise both sides\n * are normalized via {@link normalizeCoinId} and compared. Inherits the\n * registry-dependence caveat from `normalizeCoinId`. Two distinct symbols\n * that both fail to resolve will compare unequal even if they're semantic\n * aliases — register them as aliases in `TokenRegistry` for matching.\n *\n * @param a - First coin identifier (symbol or hash coinId).\n * @param b - Second coin identifier (symbol or hash coinId).\n * @returns true if both resolve to the same canonical coinId.\n */\nexport function coinIdsMatch(a: string, b: string): boolean {\n if (a === b) return true;\n return normalizeCoinId(a) === normalizeCoinId(b);\n}\n","/**\n * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n ResolvedTransportConfig,\n ResolvedOracleConfig,\n} from './config';\nimport type { PriceProviderConfig } from '../../price';\nimport type { StorageProvider } from '../../storage';\nimport type { GroupChatModuleConfig } from '../../modules/groupchat';\nimport type { MarketModuleConfig } from '../../modules/market';\n\n// =============================================================================\n// Network Resolution\n// =============================================================================\n\n/**\n * Get network configuration by type\n */\nexport function getNetworkConfig(network: NetworkType = 'mainnet'): NetworkConfig {\n return NETWORKS[network];\n}\n\n// =============================================================================\n// Transport Resolution\n// =============================================================================\n\n/**\n * Resolve transport configuration with extend/override pattern\n *\n * Priority:\n * 1. `relays` - replaces defaults entirely\n * 2. `additionalRelays` - extends network defaults\n * 3. Network defaults\n *\n * @example\n * ```ts\n * // Use network defaults\n * resolveTransportConfig('testnet', undefined);\n *\n * // Replace relays entirely\n * resolveTransportConfig('testnet', { relays: ['wss://custom.relay'] });\n *\n * // Extend defaults\n * resolveTransportConfig('testnet', { additionalRelays: ['wss://extra.relay'] });\n * ```\n */\nexport function resolveTransportConfig(\n network: NetworkType,\n config?: BaseTransportConfig & { reconnectDelay?: number; maxReconnectAttempts?: number }\n): ResolvedTransportConfig {\n const networkConfig = getNetworkConfig(network);\n\n // Resolve relays with extend/override pattern\n let relays: string[];\n if (config?.relays) {\n // Explicit relays - replace entirely\n relays = config.relays;\n } else {\n // Start with network defaults\n relays = [...networkConfig.nostrRelays] as string[];\n // Add additional relays if specified\n if (config?.additionalRelays) {\n relays = [...relays, ...config.additionalRelays];\n }\n }\n\n return {\n relays,\n timeout: config?.timeout,\n autoReconnect: config?.autoReconnect,\n debug: config?.debug,\n // Browser-specific\n reconnectDelay: config?.reconnectDelay,\n maxReconnectAttempts: config?.maxReconnectAttempts,\n };\n}\n\n// =============================================================================\n// Oracle Resolution\n// =============================================================================\n\n/**\n * Resolve oracle configuration with override pattern\n *\n * Uses network default URL if not explicitly provided\n *\n * @example\n * ```ts\n * // Use network default\n * resolveOracleConfig('testnet', undefined);\n *\n * // Override URL\n * resolveOracleConfig('testnet', { url: 'https://custom.aggregator' });\n * ```\n */\nexport function resolveOracleConfig(\n network: NetworkType,\n config?: BaseOracleConfig & { trustBasePath?: string }\n): ResolvedOracleConfig {\n const networkConfig = getNetworkConfig(network);\n\n return {\n url: config?.url ?? networkConfig.aggregatorUrl,\n apiKey: config?.apiKey,\n timeout: config?.timeout,\n skipVerification: config?.skipVerification,\n debug: config?.debug,\n // Node.js-specific\n trustBasePath: config?.trustBasePath,\n };\n}\n\n// =============================================================================\n// Price Resolution\n// =============================================================================\n\n/**\n * Resolve price provider configuration\n *\n * Returns undefined if no price config is provided (price is optional).\n *\n * @example\n * ```ts\n * // No price config\n * resolvePriceConfig(undefined); // undefined\n *\n * // Minimal config (defaults to coingecko)\n * resolvePriceConfig({}); // { platform: 'coingecko' }\n *\n * // With API key\n * resolvePriceConfig({ apiKey: 'CG-xxx' }); // { platform: 'coingecko', apiKey: 'CG-xxx' }\n * ```\n */\nexport function resolvePriceConfig(\n config?: BasePriceConfig,\n storage?: StorageProvider,\n): PriceProviderConfig | undefined {\n if (config === undefined) {\n return undefined;\n }\n\n return {\n platform: config.platform ?? 'coingecko',\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n cacheTtlMs: config.cacheTtlMs,\n timeout: config.timeout,\n debug: config.debug,\n storage,\n };\n}\n\n// =============================================================================\n// Array Extension Helper\n// =============================================================================\n\n/**\n * Resolve array with extend/override pattern\n *\n * @param defaults - Default values from network config\n * @param replace - Values that replace defaults entirely\n * @param additional - Values to add to defaults\n * @returns Resolved array\n *\n * @example\n * ```ts\n * // Use defaults\n * resolveArrayConfig(['a', 'b'], undefined, undefined); // ['a', 'b']\n *\n * // Replace\n * resolveArrayConfig(['a', 'b'], ['x'], undefined); // ['x']\n *\n * // Extend\n * resolveArrayConfig(['a', 'b'], undefined, ['c']); // ['a', 'b', 'c']\n * ```\n */\nexport function resolveArrayConfig<T>(\n defaults: readonly T[],\n replace?: T[],\n additional?: T[]\n): T[] {\n if (replace) {\n return replace;\n }\n\n const result = [...defaults];\n if (additional) {\n return [...result, ...additional];\n }\n\n return result;\n}\n\n// =============================================================================\n// Group Chat Resolution\n// =============================================================================\n\n/**\n * Resolve group chat configuration for provider factories.\n * @param network - Network type for default relay URLs\n * @param config - User-provided group chat config (true, object, or undefined)\n * @returns Resolved GroupChatModuleConfig or undefined if disabled\n */\nexport function resolveGroupChatConfig(\n network: NetworkType,\n config?: { enabled?: boolean; relays?: string[] } | boolean,\n): GroupChatModuleConfig | undefined {\n if (!config) return undefined;\n\n if (config === true) {\n const netConfig = getNetworkConfig(network);\n return { relays: [...netConfig.groupRelays] };\n }\n\n if (typeof config === 'object' && config.enabled === false) {\n return undefined;\n }\n\n const netConfig = getNetworkConfig(network);\n return {\n relays: config.relays ?? [...netConfig.groupRelays],\n };\n}\n\n// =============================================================================\n// Market Resolution\n// =============================================================================\n\n/**\n * Resolve market module configuration.\n * @param config - User-provided market config (true, object, or undefined)\n * @returns Resolved MarketModuleConfig or undefined if disabled\n */\nexport function resolveMarketConfig(\n config?: BaseMarketConfig | boolean,\n): MarketModuleConfig | undefined {\n if (!config) return undefined;\n if (config === true) return {};\n return { apiUrl: config.apiUrl, timeout: config.timeout };\n}\n"],"mappings":"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","names":["Buffer","success","message","senderNametag","netConfig","Buffer"]}
|
|
1
|
+
{"version":3,"sources":["../../../impl/browser/index.ts","../../../core/logger.ts","../../../core/global-cell.ts","../../../core/errors.ts","../../../constants.ts","../../../storage/tracked-addresses.ts","../../../impl/browser/storage/LocalStorageProvider.ts","../../../impl/browser/storage/IndexedDBStorageProvider.ts","../../../transport/NostrTransportProvider.ts","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha2.ts","../../../transport/websocket.ts","../../../impl/browser/transport/index.ts","../../../oracle/UnicityAggregatorProvider.ts","../../../assets/trustbase.ts","../../../impl/shared/trustbase-loader.ts","../../../impl/browser/oracle/index.ts","../../../impl/shared/wallet-api/index.ts","../../../impl/browser/download.ts","../../../impl/shared/network.ts","../../../core/timeout.ts","../../../price/CoinGeckoPriceProvider.ts","../../../price/index.ts","../../../impl/shared/resolvers.ts"],"sourcesContent":["/**\n * Browser-specific implementations\n * All platform-dependent code lives here\n */\n\n// Polyfill Buffer for browser environment\n// Many crypto libraries depend on Node.js Buffer API\nimport { Buffer } from 'buffer';\nif (typeof globalThis.Buffer === 'undefined') {\n globalThis.Buffer = Buffer;\n}\n\nexport * from './storage';\nexport * from './transport';\nexport * from './oracle';\n// Wallet-api storage provider (platform-neutral, sdk-changes S2)\nexport * from '../shared/wallet-api';\n\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport { assertNetworkConsistency } from '../shared/network';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig } from './storage';\nimport { createNostrTransportProvider } from './transport';\nimport { createUnicityAggregatorProvider } from './oracle';\nimport type { StorageProvider } from '../../storage';\nimport type { TransportProvider } from '../../transport';\nimport type { OracleProvider } from '../../oracle';\nimport type { NetworkType } from '../../constants';\nimport type { GroupChatModuleConfig } from '../../modules/groupchat';\nimport type { MarketModuleConfig } from '../../modules/market';\nimport type { PriceProvider } from '../../price';\nimport { createPriceProvider } from '../../price';\nimport {\n type BaseTransportConfig,\n type BaseOracleConfig,\n type BasePriceConfig,\n type BaseMarketConfig,\n type BrowserTransportExtensions,\n resolveTransportConfig,\n resolveOracleConfig,\n resolvePriceConfig,\n resolveGroupChatConfig,\n resolveMarketConfig,\n} from '../shared';\n\n// =============================================================================\n// Browser-Specific Configuration Extensions\n// =============================================================================\n\n/**\n * Browser transport configuration\n * Extends base with browser-specific options\n */\nexport type TransportConfig = BaseTransportConfig & BrowserTransportExtensions;\n\n/**\n * Browser oracle configuration\n * Same as base (no browser-specific extensions)\n */\nexport type OracleConfig = BaseOracleConfig;\n\n// =============================================================================\n// Token Sync Backend Configurations\n// =============================================================================\n\n// =============================================================================\n// Browser Providers Configuration\n// =============================================================================\n\nexport interface BrowserProvidersConfig {\n /** Network preset: mainnet, testnet or testnet2. Sets default URLs for all services */\n network?: NetworkType;\n /** Enable debug logging globally for all providers (default: false). Per-provider debug flags override this. */\n debug?: boolean;\n /** Storage configuration (IndexedDB) */\n storage?: IndexedDBStorageProviderConfig;\n /** Transport (Nostr) configuration - supports extend/override pattern */\n transport?: TransportConfig;\n /** Oracle (Aggregator) configuration - supports extend/override pattern */\n oracle?: OracleConfig;\n /**\n * Token sync backends configuration\n * Supports multiple backends: IPFS, file, cloud (future)\n * Each backend can be enabled/disabled independently\n */\n /** Price provider configuration (optional — enables fiat value display) */\n price?: BasePriceConfig;\n /** Group chat (NIP-29) configuration. true = enable with defaults, object = custom config */\n groupChat?: { enabled?: boolean; relays?: string[] } | boolean;\n /** Market module configuration. true = enable with defaults, object = custom config */\n market?: BaseMarketConfig | boolean;\n}\n\nexport interface BrowserProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n /** Group chat config (resolved, for passing to Sphere.init) */\n groupChat?: GroupChatModuleConfig | boolean;\n /** Market module config (resolved, for passing to Sphere.init) */\n market?: MarketModuleConfig | boolean;\n}\n\n// =============================================================================\n// Token Sync Resolution\n// =============================================================================\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Create all browser providers with default configuration\n *\n * Supports extend/override pattern for flexible configuration:\n * - Use `network` preset for quick setup (mainnet/testnet/testnet2)\n * - Override specific values (e.g., `oracle.url` replaces default)\n * - Extend arrays with `additional*` (e.g., `additionalRelays` adds to defaults)\n *\n * @example\n * ```ts\n * // Simple - uses mainnet defaults\n * const providers = createBrowserProviders();\n *\n * // Testnet - all services use testnet URLs\n * const providers = createBrowserProviders({ network: 'testnet' });\n *\n * // Add extra relays to testnet defaults\n * const providers = createBrowserProviders({\n * network: 'testnet',\n * transport: {\n * additionalRelays: ['wss://my-relay.com', 'wss://backup-relay.com'],\n * },\n * });\n *\n * // Replace relays entirely (ignores network defaults)\n * const providers = createBrowserProviders({\n * network: 'testnet',\n * transport: {\n * relays: ['wss://only-this-relay.com'],\n * },\n * });\n *\n * // Use with Sphere.init (add the wallet-api transport config)\n * const { sphere } = await Sphere.init({\n * ...createWalletApiProviders(providers, { baseUrl, network: 'testnet' }),\n * autoGenerate: true,\n * });\n * ```\n */\nexport function createBrowserProviders(config?: BrowserProvidersConfig): BrowserProviders {\n // Fail loud: a missing network would silently load the wrong-network providers.\n if (!config?.network) {\n throw new SphereError('createBrowserProviders: config.network is required.', 'INVALID_CONFIG');\n }\n const network = config.network;\n // Refuse provably-broken networks (e.g. a null/mismatched trust base would\n // silently accept unverified tokens) before building any provider.\n assertNetworkConsistency(network);\n\n // Configure global logger: top-level debug enables all, per-provider overrides are additive.\n // Only override global debug flag when explicitly provided — don't reset a previously-configured value.\n if (config?.debug !== undefined) {\n sdkLogger.configure({ debug: config.debug });\n }\n if (config?.transport?.debug) sdkLogger.setTagDebug('Nostr', true);\n if (config?.oracle?.debug) sdkLogger.setTagDebug('Aggregator', true);\n if (config?.price?.debug) sdkLogger.setTagDebug('Price', true);\n\n // Resolve configurations using shared utilities\n const transportConfig = resolveTransportConfig(network, config?.transport);\n const oracleConfig = resolveOracleConfig(network, config?.oracle);\n\n const storage = createIndexedDBStorageProvider({ ...config?.storage, network });\n const priceConfig = resolvePriceConfig(config?.price, storage);\n\n // Resolve group chat config\n const groupChat = resolveGroupChatConfig(network, config?.groupChat);\n\n // Resolve market config\n const market = resolveMarketConfig(config?.market);\n\n return {\n storage,\n groupChat,\n market,\n transport: createNostrTransportProvider({\n relays: transportConfig.relays,\n timeout: transportConfig.timeout,\n autoReconnect: transportConfig.autoReconnect,\n reconnectDelay: transportConfig.reconnectDelay,\n maxReconnectAttempts: transportConfig.maxReconnectAttempts,\n debug: transportConfig.debug,\n storage,\n }),\n oracle: createUnicityAggregatorProvider({\n url: oracleConfig.url,\n apiKey: oracleConfig.apiKey,\n timeout: oracleConfig.timeout,\n skipVerification: oracleConfig.skipVerification,\n debug: oracleConfig.debug,\n network,\n }),\n price: priceConfig ? createPriceProvider(priceConfig) : undefined,\n };\n}\n","/**\n * Centralized SDK Logger\n *\n * A lightweight singleton logger that works across all tsup bundles\n * by storing state on globalThis. Supports three log levels:\n * - debug: detailed messages (only shown when debug=true)\n * - warn: important warnings (ALWAYS shown regardless of debug flag)\n * - error: critical errors (ALWAYS shown regardless of debug flag)\n *\n * Global debug flag enables all logging. Per-tag overrides allow\n * granular control (e.g., only transport debug).\n *\n * @example\n * ```ts\n * import { logger } from '@unicitylabs/sphere-sdk';\n *\n * // Enable all debug logging\n * logger.configure({ debug: true });\n *\n * // Enable only specific tags\n * logger.setTagDebug('Nostr', true);\n *\n * // Usage in SDK classes\n * logger.debug('Payments', 'Transfer started', { amount, recipient });\n * logger.warn('Nostr', 'queryEvents timed out after 5s');\n * logger.error('Sphere', 'Critical failure', error);\n * ```\n */\n\nexport type LogLevel = 'debug' | 'warn' | 'error';\n\nexport type LogHandler = (level: LogLevel, tag: string, message: string, ...args: unknown[]) => void;\n\nexport interface LoggerConfig {\n /** Enable debug logging globally (default: false). When false, only warn and error messages are shown. */\n debug?: boolean;\n /** Custom log handler. If provided, replaces console output. Useful for tests or custom log sinks. */\n handler?: LogHandler | null;\n}\n\n// Use a unique symbol-like key on globalThis to share logger state across tsup bundles\nconst LOGGER_KEY = '__sphere_sdk_logger__';\n\ninterface LoggerState {\n debug: boolean;\n tags: Record<string, boolean>;\n handler: LogHandler | null;\n}\n\nfunction getState(): LoggerState {\n const g = globalThis as unknown as Record<string, unknown>;\n if (!g[LOGGER_KEY]) {\n g[LOGGER_KEY] = { debug: false, tags: {}, handler: null } satisfies LoggerState;\n }\n return g[LOGGER_KEY] as LoggerState;\n}\n\nfunction isEnabled(tag: string): boolean {\n const state = getState();\n // Per-tag override takes priority\n if (tag in state.tags) return state.tags[tag];\n // Fall back to global flag\n return state.debug;\n}\n\nexport const logger = {\n /**\n * Configure the logger. Can be called multiple times (last write wins).\n * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().\n */\n configure(config: LoggerConfig): void {\n const state = getState();\n if (config.debug !== undefined) state.debug = config.debug;\n if (config.handler !== undefined) state.handler = config.handler;\n },\n\n /**\n * Enable/disable debug logging for a specific tag.\n * Per-tag setting overrides the global debug flag.\n *\n * @example\n * ```ts\n * logger.setTagDebug('Nostr', true); // enable only Nostr logs\n * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true\n * ```\n */\n setTagDebug(tag: string, enabled: boolean): void {\n getState().tags[tag] = enabled;\n },\n\n /**\n * Clear per-tag override, falling back to global debug flag.\n */\n clearTagDebug(tag: string): void {\n delete getState().tags[tag];\n },\n\n /** Returns true if debug mode is enabled for the given tag (or globally). */\n isDebugEnabled(tag?: string): boolean {\n if (tag) return isEnabled(tag);\n return getState().debug;\n },\n\n /**\n * Debug-level log. Only shown when debug is enabled (globally or for this tag).\n * Use for detailed operational information.\n */\n debug(tag: string, message: string, ...args: unknown[]): void {\n if (!isEnabled(tag)) return;\n const state = getState();\n if (state.handler) {\n state.handler('debug', tag, message, ...args);\n } else {\n console.log(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Warning-level log. ALWAYS shown regardless of debug flag.\n * Use for important but non-critical issues (timeouts, retries, degraded state).\n */\n warn(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('warn', tag, message, ...args);\n } else {\n console.warn(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Error-level log. ALWAYS shown regardless of debug flag.\n * Use for critical failures that should never be silenced.\n */\n error(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('error', tag, message, ...args);\n } else {\n console.error(`[${tag}]`, message, ...args);\n }\n },\n\n /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */\n reset(): void {\n const g = globalThis as unknown as Record<string, unknown>;\n delete g[LOGGER_KEY];\n },\n};\n","/**\n * Identity state that must not split when tsup bundles each subpath export separately\n * (`splitting: false`) or when ESM and CJS both load: globalThis, VERSIONED key,\n * validated on read, never key material. Why: tests/unit/core/global-cell.test.ts.\n */\nconst CELLS_KEY = '__sphere_sdk_cells_v1__';\n\ntype CellBag = Record<string, unknown>;\n\n/** Set only when globalThis refuses the bag; cells then degrade to this bundle. */\nlet bundleLocalBag: CellBag | null = null;\n\nfunction isObject(value: unknown): value is object {\n return typeof value === 'object' && value !== null;\n}\n\nfunction installBag(host: CellBag, fresh: CellBag): boolean {\n try {\n Object.defineProperty(host, CELLS_KEY, {\n value: fresh,\n writable: true,\n configurable: true,\n enumerable: false,\n });\n return host[CELLS_KEY] === fresh;\n } catch {\n return false;\n }\n}\n\nfunction cellBag(): CellBag {\n if (bundleLocalBag) return bundleLocalBag;\n const host = globalThis as unknown as CellBag;\n const found = host[CELLS_KEY];\n if (isObject(found)) return found as CellBag;\n const fresh: CellBag = {};\n if (!installBag(host, fresh)) bundleLocalBag = fresh;\n return fresh;\n}\n\nfunction accepted(intact: (candidate: object) => boolean, candidate: object): boolean {\n try {\n return intact(candidate);\n } catch {\n return false;\n }\n}\n\nfunction put(bag: CellBag, name: string, cell: object): void {\n try {\n bag[name] = cell;\n if (bag[name] === cell) return;\n } catch { /* a frozen or trapped bag — this bundle keeps its cells locally */ }\n if (bag !== bundleLocalBag) bundleLocalBag = { [name]: cell };\n}\n\n/** The cell `name` (suffix its shape version, `@1`), created once and shared by every\n * bundle; `intact` rejects a foreign value rather than adopting it as SDK state. */\nexport function sharedCell<T extends object>(\n name: string,\n create: () => T,\n intact: (candidate: object) => boolean,\n): T {\n const bag = cellBag();\n const found = bag[name];\n if (isObject(found) && accepted(intact, found)) return found as T;\n const fresh = create();\n put(bag, name, fresh);\n return fresh;\n}\n","/**\n * SDK Error Types\n *\n * Structured error codes for programmatic error handling in UI.\n * UI can switch on error.code to show appropriate user-facing messages.\n *\n * @example\n * ```ts\n * import { SphereError } from '@unicitylabs/sphere-sdk';\n *\n * try {\n * await sphere.payments.send({ ... });\n * } catch (err) {\n * if (err instanceof SphereError) {\n * switch (err.code) {\n * case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;\n * case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;\n * case 'TRANSPORT_ERROR': showToast('Network connection issue'); break;\n * case 'TIMEOUT': showToast('Request timed out, try again'); break;\n * default: showToast(err.message);\n * }\n * }\n * }\n * ```\n */\n\nexport type SphereErrorCode =\n | 'NOT_INITIALIZED'\n | 'ALREADY_INITIALIZED'\n | 'INVALID_CONFIG'\n | 'INVALID_IDENTITY'\n | 'INSUFFICIENT_BALANCE'\n | 'INVALID_RECIPIENT'\n | 'TRANSFER_FAILED'\n // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n // The op's certification is INDETERMINATE (submit accepted / proof fetch\n // inconclusive) — raised as ProofUnconfirmedError; keep the intent OPEN (#631).\n | 'CERTIFICATION_UNCONFIRMED'\n // Split burn checkpoint (sdk-changes E.4, sphere-sdk#501) — all keep-open (token-engine/errors.ts):\n // burn certified but the checkpoint could not be persisted (no mint submitted);\n | 'CHECKPOINT_PERSIST_FAILED'\n // a certified mint leaf has no reproducing stored bytes, or a stored checkpoint fails byte-binding;\n | 'SPLIT_CHECKPOINT_LOST'\n // a byte-bound checkpoint proof no longer verifies against the current trust base (validator rotation).\n | 'CHECKPOINT_TRUSTBASE_MISMATCH'\n | 'STORAGE_ERROR'\n // #665: the on-chain spend committed but the post-commit wallet-api mirror\n // sync (inventory apply / blob upload / save) failed. NOT a lost payment —\n // the intent is kept open and resume converges the mirror (idempotent apply,\n // #664). Surfaced distinctly so a UI can reassure (\"sent — wallet catching\n // up\") instead of showing a hard send failure.\n | 'SEND_SYNC_PENDING'\n // #677: a multi-leg send lost a source to a concurrent transfer AFTER ≥1\n // earlier leg had already certified on-chain AND been journaled/delivered.\n // The delivered value has irreversibly left the wallet, so this is NOT a\n // plain, re-sendable failure: surfacing a bare TransferConflictError makes\n // the caller re-send the FULL amount and pay the delivered leg twice. Raised\n // as PartialSendConflictError; the caller MUST re-plan ONLY the remainder\n // under a NEW transferId, never the whole amount.\n | 'SEND_PARTIALLY_COMPLETED'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'INVALID_AMOUNT'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR'\n // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n /**\n * #441 deferred-paid linkage: the transferId of the possibly-committed send,\n * stamped by `sendOnce` before the error leaves so a payment-request consumer\n * can durably journal request→transfer and resolve 'paid' only when that\n * transfer actually completes (never optimistically). Present ONLY on\n * possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).\n */\n transferId?: string;\n\n constructor(message: string, code: SphereErrorCode, cause?: unknown) {\n super(message);\n this.name = 'SphereError';\n this.code = code;\n this.cause = cause;\n }\n}\n\n/**\n * #677: a send that PARTIALLY completed before losing a source to a concurrent\n * transfer. At least one earlier leg certified on-chain and was journaled for\n * delivery (its {@link committedTokenIds}) — that value has irreversibly left\n * the wallet — but a LATER leg raised `TransferConflictError` (a lost race), and\n * `send()` could NOT COMPLETE the {@link remainingAmount} from the remaining live\n * sources. This is the fallback after the internal remainder re-plan gave up —\n * either insufficient funds OR a transient/transport error during the re-plan\n * (the underlying reason is on `cause`), not necessarily an \"out of funds\" state.\n *\n * Distinct from `TransferConflictError` on purpose: a bare conflict makes the\n * caller re-send the FULL amount, paying the already-delivered leg a second\n * time. Catching THIS type (or `code === 'SEND_PARTIALLY_COMPLETED'`) tells the\n * caller/UI: the delivered legs are final, the conflicted intent stays OPEN\n * (resume converges its certified legs), and only the REMAINDER\n * ({@link remainingAmount}) may be re-planned — under a NEW transferId, never\n * re-sending the whole amount.\n *\n * NOT a subclass of `TransferConflictError` by design: existing conflict\n * handlers that re-send in full must NOT treat this as an ordinary conflict.\n */\nexport class PartialSendConflictError extends SphereError {\n /**\n * The FIRST partial attempt's transferId — its intent stays open for resume,\n * and it is the §6 handoff anchor. NOTE: `send()` may re-plan the remainder across\n * SEVERAL internal attempts before giving up, so {@link committedTokenIds} can\n * span MULTIPLE transferIds — they do NOT all map to this single id.\n */\n readonly transferId: string;\n /**\n * Source token ids whose spend already certified on-chain (and whose finished\n * output blob is journaled for delivery). Their value has already been\n * delivered; NEVER re-send these. May accumulate across several internal\n * remainder-re-plan attempts (so not all are journaled under {@link transferId}).\n */\n readonly committedTokenIds: readonly string[];\n /**\n * The still-undelivered portion (base units, decimal string) — `send()` could\n * not COMPLETE it from the remaining live sources (insufficient funds or a\n * transient error; see `cause`). Re-plan ONLY this amount, under a new\n * transferId; the delivered legs converge via the recipient's §6 claim.\n */\n readonly remainingAmount: string;\n\n constructor(\n message: string,\n transferId: string,\n committedTokenIds: readonly string[],\n remainingAmount: string,\n cause?: unknown,\n ) {\n super(message, 'SEND_PARTIALLY_COMPLETED', cause);\n this.name = 'PartialSendConflictError';\n this.transferId = transferId;\n this.committedTokenIds = [...committedTokenIds];\n this.remainingAmount = remainingAmount;\n }\n}\n\n/**\n * Error codes for a send outcome where money has (or may have) already left the\n * wallet on-chain, so the send is NOT a clean, re-sendable failure. A consumer\n * that persists a \"paid\" flag (e.g. a payment request) must keep the request\n * NON-payable on any of these — reverting to a re-payable state would double-pay\n * (#441). Covers the post-commit mirror-sync-pending / keep-open certification\n * states AND the partial-conflict outcome.\n */\nconst POSSIBLY_COMMITTED_SEND_CODES: ReadonlySet<SphereErrorCode> = new Set([\n 'SEND_SYNC_PENDING',\n 'CERTIFICATION_UNCONFIRMED',\n 'CHECKPOINT_PERSIST_FAILED',\n 'SPLIT_CHECKPOINT_LOST',\n 'CHECKPOINT_TRUSTBASE_MISMATCH',\n 'SEND_PARTIALLY_COMPLETED',\n]);\n\n/**\n * #441: true when a send failed with an outcome where the payment has (or may\n * have) irreversibly left the wallet — a post-commit sync-pending / keep-open\n * certification state, or a `PartialSendConflictError`. Such a send completes (or\n * has already partially completed) via resume/claim and MUST NOT be re-sent, so\n * a persisted \"paid\" state must stay non-payable. A `false` result is a clean\n * pre-commit failure — nothing left the wallet, safe to re-pay.\n */\nexport function isPossiblyCommittedSendOutcome(err: unknown): boolean {\n return isSphereError(err) && POSSIBLY_COMMITTED_SEND_CODES.has(err.code);\n}\n\n/**\n * Type guard to check if an error is a SphereError\n */\nexport function isSphereError(err: unknown): err is SphereError {\n return err instanceof SphereError;\n}\n","/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/**\n * Global storage keys (one per wallet, no address index)\n * Final key format: sphere_{key}\n */\nexport const STORAGE_KEYS_GLOBAL = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: 'mnemonic',\n /** Encrypted master private key */\n MASTER_KEY: 'master_key',\n /** BIP32 chain code */\n CHAIN_CODE: 'chain_code',\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: 'derivation_path',\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: 'base_path',\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: 'derivation_mode',\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: 'wallet_source',\n /** Wallet existence flag */\n WALLET_EXISTS: 'wallet_exists',\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: 'current_address_index',\n /** Nametag cache per address (separate from tracked addresses registry) */\n ADDRESS_NAMETAGS: 'address_nametags',\n /** Active addresses registry (JSON: TrackedAddressesStorage) */\n TRACKED_ADDRESSES: 'tracked_addresses',\n /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */\n LAST_WALLET_EVENT_TS: 'last_wallet_event_ts',\n /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data is server custody (wallet-api backend), not here; the\n * payments vertical's durable client state self-prefixes `pv2g2:{network}:{pubkey}:`.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Transfer outbox for this address (pre-flip key name; kept as the network-scoping witness) */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Group chat: joined groups for this address */\n GROUP_CHAT_GROUPS: 'group_chat_groups',\n /** Group chat: messages for this address */\n GROUP_CHAT_MESSAGES: 'group_chat_messages',\n /** Group chat: members for this address */\n GROUP_CHAT_MEMBERS: 'group_chat_members',\n /** Group chat: processed event IDs for deduplication */\n GROUP_CHAT_PROCESSED_EVENTS: 'group_chat_processed_events',\n /** Auto-return settings (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (pre-flip key name; kept as the network-scoping witness) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Per-swap key prefix (pre-flip key name; kept as the network-scoping witness) */\n SWAP_RECORD_PREFIX: 'swap:',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe. Chat/identity per-address keys (CONVERSATIONS/MESSAGES/\n * GROUP_CHAT_*) are network-AGNOSTIC and deliberately NOT listed. The payments vertical\n * self-prefixes `pv2g2:{network}:{pubkey}:` and never rides this mechanism; the entries left\n * here are the pre-flip key names the network-isolation tests pin the provider behavior\n * with (P11 stage-2 note: the full isNetworkScopedAddressKey cut needs an owner call on\n * those tests first).\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/**\n * Fallback Nostr relays, used only when no relay list is configured\n * (NostrTransportProvider / MultiAddressTransportMux). `relay.unicity.network`\n * was the v1 relay and no longer resolves, so it is not listed here.\n */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n/**\n * NIP-29 Event Kinds for relay-based group chat\n * https://github.com/nostr-protocol/nips/blob/master/29.md\n */\nexport const NIP29_KINDS = {\n /** Chat message sent to group */\n CHAT_MESSAGE: 9,\n /** Thread root message */\n THREAD_ROOT: 11,\n /** Thread reply message */\n THREAD_REPLY: 12,\n /** User join request */\n JOIN_REQUEST: 9021,\n /** User leave request */\n LEAVE_REQUEST: 9022,\n /** Admin: add/update user */\n PUT_USER: 9000,\n /** Admin: remove user */\n REMOVE_USER: 9001,\n /** Admin: edit group metadata */\n EDIT_METADATA: 9002,\n /** Admin: delete event */\n DELETE_EVENT: 9005,\n /** Admin: create group */\n CREATE_GROUP: 9007,\n /** Admin: delete group */\n DELETE_GROUP: 9008,\n /** Admin: create invite code */\n CREATE_INVITE: 9009,\n /** Relay-signed group metadata */\n GROUP_METADATA: 39000,\n /** Relay-signed group admins */\n GROUP_ADMINS: 39001,\n /** Relay-signed group members */\n GROUP_MEMBERS: 39002,\n /** Relay-signed group roles */\n GROUP_ROLES: 39003,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator request timeout (ms)\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n * There is no default aggregator URL: every live network names its own gateway.\n */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n\n// =============================================================================\n// Wallet Defaults\n// =============================================================================\n\n/** Default BIP32 base path (without chain/index) */\nexport const DEFAULT_BASE_PATH = \"m/44'/0'/0'\" as const;\n\n/** Default BIP32 derivation path (full path with chain/index) */\nexport const DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0` as const;\n\n/** Coin types */\nexport const COIN_TYPES = {\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// Token Registry Defaults\n// =============================================================================\n\n/** Default token registry refresh interval (ms) — 1 hour */\nexport const TOKEN_REGISTRY_REFRESH_INTERVAL = 3_600_000;\n\n// =============================================================================\n// Network Defaults\n// =============================================================================\n\n/** Testnet Nostr relays */\nexport const TEST_NOSTR_RELAYS = [\n 'wss://nostr-relay.testnet.unicity.network',\n] as const;\n\n/** Default group chat relays (NIP-29 Zooid relay) */\nexport const DEFAULT_GROUP_RELAYS = [\n 'wss://sphere-relay.unicity.network',\n] as const;\n\n/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n /** Canonical numeric network id (= RootTrustBase.networkId; testnet2 = 4).\n * Optional: only set for networks that have a live v2 trust base. */\n readonly networkId?: number;\n}\n\n/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n networkId: 1,\n // v3 state-transition gateway (networkId 1 comes from the trust base). apiKey is env-injected;\n // unlike testnet2's, a mainnet gateway key is a SECRET — never commit one.\n aggregatorUrl: 'https://gateway.mainnet.unicity.network',\n // Mainnet has no relay of its own yet — it shares the testnet relay until one is stood up.\n // Consequence while shared: nametag bindings for both networks live in ONE namespace, and\n // since bindings carry no network the cross-network recipient guard can only signal (#734).\n nostrRelays: TEST_NOSTR_RELAYS,\n groupRelays: DEFAULT_GROUP_RELAYS,\n // Published, but currently only the non-fungible base token type — no fungible\n // coins yet, so those still miss the registry (decimals 0, symbol falls back to\n // six hex chars). Presentation only: the money path treats coinId as opaque bytes.\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.mainnet.json',\n },\n // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n networkId: 4,\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs)\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n/**\n * A network descriptor — the canonical way to identify a Unicity network across\n * the SDK and the Connect protocol. `id` is the canonical key (= RootTrustBase\n * networkId, analogous to an EIP-155 chainId); `name` is human-readable metadata.\n * Richer fields (gateway/symbol/explorer/icon) and switch/add-network are deferred.\n */\nexport interface NetworkInfo {\n readonly id: number;\n readonly name?: string;\n}\n\n/**\n * Registry of known networks for dApps and wallets. Single-sourced from NETWORKS\n * so it cannot drift. Use as `network: SPHERE_NETWORKS.testnet2`. Custom networks\n * are the same shape: `network: { id, name }`. Only live v2 networks appear here;\n * the legacy `testnet` alias is intentionally not surfaced.\n */\nexport const SPHERE_NETWORKS = {\n mainnet: { id: NETWORKS.mainnet.networkId as number, name: 'mainnet' },\n testnet2: { id: NETWORKS.testnet2.networkId as number, name: 'testnet2' },\n} as const satisfies Record<string, NetworkInfo>;\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n// =============================================================================\n// Sphere Connect\n// =============================================================================\n\n/** Signal sent by wallet popup to dApp when ConnectHost is ready */\nexport const HOST_READY_TYPE = 'sphere-connect:host-ready' as const;\n\n/** Default timeout (ms) for waiting for the host-ready signal */\nexport const HOST_READY_TIMEOUT = 30_000;\n\n/** Validation limits */\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","import { SphereError } from '../core/errors';\nimport type { TrackedAddressEntry } from '../types';\n\n/** On-disk shape of the global `tracked_addresses` key. */\nexport interface TrackedAddressesFile {\n version: 1;\n addresses: TrackedAddressEntry[];\n}\n\n/** A BIP32 child number is a uint32 — see the port docstring. */\nexport function isDerivableIndex(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 0xffffffff;\n}\n\nfunction num(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\n/** Repair, don't drop — except an underivable index, which would alias a real address. */\nfunction toEntry(value: unknown): TrackedAddressEntry | null {\n if (typeof value !== 'object' || value === null) return null;\n const e = value as Record<string, unknown>;\n if (!isDerivableIndex(e.index)) return null;\n return {\n ...e,\n index: e.index,\n hidden: e.hidden === true,\n createdAt: num(e.createdAt, 0),\n updatedAt: num(e.updatedAt, 0),\n } as TrackedAddressEntry;\n}\n\n/** Tolerant read: unusable JSON and a wrong top-level shape both read as absent. */\nexport function parseTrackedAddresses(raw: string | null): TrackedAddressEntry[] {\n if (!raw) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) return [];\n const addresses = (parsed as { addresses?: unknown }).addresses;\n if (!Array.isArray(addresses)) return [];\n const entries: TrackedAddressEntry[] = [];\n for (const row of addresses) {\n const entry = toEntry(row);\n if (entry) entries.push(entry);\n }\n return entries;\n}\n\n/**\n * Union `incoming` (one writer's snapshot) into `onDisk` by `index`, sorted by index.\n * On a conflict the greater `updatedAt` supplies `hidden` (ties keep `incoming`) and the\n * earlier `createdAt` survives — safe because nothing removes a single entry; see\n * `StorageProvider.saveTrackedAddresses` (#766 item 5).\n * An underivable `incoming` index REJECTS the write; `onDisk` is filtered instead, since\n * it is read tolerantly and one bad stored row must not brick every later write.\n */\nexport function mergeTrackedAddresses(\n onDisk: readonly TrackedAddressEntry[],\n incoming: readonly TrackedAddressEntry[],\n): TrackedAddressEntry[] {\n const merged = new Map<number, TrackedAddressEntry>();\n for (const entry of onDisk) {\n if (isDerivableIndex(entry.index)) merged.set(entry.index, entry);\n }\n\n for (const entry of incoming) {\n if (!isDerivableIndex(entry.index)) {\n throw new SphereError(\n `Tracked address index ${String(entry.index)} is not a BIP32 child number: it must be an integer in 0…0xffffffff. Refusing the write — such a row derives another address's keys (1.5 parses to 1) instead of its own.`,\n 'VALIDATION_ERROR',\n );\n }\n const existing = merged.get(entry.index);\n if (!existing) {\n merged.set(entry.index, entry);\n continue;\n }\n const winner = entry.updatedAt >= existing.updatedAt ? entry : existing;\n merged.set(entry.index, {\n ...existing,\n ...winner,\n index: entry.index,\n createdAt: Math.min(existing.createdAt, entry.createdAt),\n });\n }\n\n return Array.from(merged.values()).sort((a, b) => a.index - b.index);\n}\n","/**\n * Browser LocalStorage Provider\n * Implements StorageProvider using browser localStorage\n */\n\nimport { logger } from '../../../core/logger';\nimport { sharedCell } from '../../../core/global-cell';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\nimport {\n mergeTrackedAddresses,\n parseTrackedAddresses,\n type TrackedAddressesFile,\n} from '../../../storage/tracked-addresses';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface LocalStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Custom storage instance (for testing/SSR) */\n storage?: Storage;\n /**\n * When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global/seed keys stay shared.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Per-`Storage` tags for `backingStoreId`. The prefix alone does not identify the\n * store: an SSR fallback mints a private in-memory `Storage` per provider, so two\n * providers with the same prefix over different objects hold unrelated data. Weak,\n * lazily assigned, and PROCESS-wide — a per-bundle counter gives the first unrelated\n * `Storage` of each copy the tag `1`, and `Sphere.clear()` erases the wrong wallet.\n * The write chains ride the same cell: two chains over one store lose an update.\n */\ninterface StorageIdentityCell {\n readonly tags: WeakMap<Storage, string>;\n readonly writeChains: Map<string, Promise<unknown>>;\n seq: number;\n}\n\nconst storeIdentity = sharedCell<StorageIdentityCell>(\n 'impl.browser.localStorage.identity@1',\n () => ({ tags: new WeakMap(), writeChains: new Map(), seq: 0 }),\n (cell) => {\n const c = cell as Partial<StorageIdentityCell>;\n return c.tags instanceof WeakMap && c.writeChains instanceof Map && typeof c.seq === 'number';\n },\n);\n\nfunction storageObjectTag(storage: Storage): string {\n let tag = storeIdentity.tags.get(storage);\n if (!tag) {\n tag = String(++storeIdentity.seq);\n storeIdentity.tags.set(storage, tag);\n }\n return tag;\n}\n\n/**\n * One write chain per BACKING STORE, not per provider object: `backingStoreId` exists\n * because two providers may address the same localStorage + prefix, and two per-instance\n * chains both read the old registry before either writes — the lost update again, one\n * level up. This coordinates a single JS realm only; a SECOND TAB writing the same store\n * is genuinely not covered, and no in-process lock can cover it.\n */\nconst trackedWriteChains = storeIdentity.writeChains;\n\nfunction serializeTrackedWrite(storeId: string, task: () => Promise<void>): Promise<void> {\n const previous = trackedWriteChains.get(storeId) ?? Promise.resolve();\n const run = previous.then(task);\n // Carried forward, one transient error would reject every later write without running it.\n const tail = run.then(() => undefined, () => undefined);\n trackedWriteChains.set(storeId, tail);\n // Drop the entry once nothing is queued behind it, so a per-request SSR storage does\n // not leave a chain behind forever.\n void tail.then(() => {\n if (trackedWriteChains.get(storeId) === tail) trackedWriteChains.delete(storeId);\n });\n return run;\n}\n\nexport class LocalStorageProvider implements StorageProvider {\n readonly id = 'localStorage';\n readonly name = 'Local Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser localStorage for single-device persistence';\n /** The `Storage` object + prefix — two providers over one pair share erasure (#766). */\n readonly backingStoreId: string;\n\n private config: Required<Pick<LocalStorageProviderConfig, 'prefix' | 'debug'>> & {\n storage: Storage;\n };\n private network?: NetworkType;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n\n constructor(config?: LocalStorageProviderConfig) {\n // SSR fallback: use in-memory storage if localStorage unavailable\n const storage = config?.storage ?? this.getStorageSafe();\n\n this.config = {\n prefix: config?.prefix ?? 'sphere_',\n storage,\n debug: config?.debug ?? false,\n };\n this.network = config?.network;\n this.backingStoreId =\n `localstorage:${storageObjectTag(storage)}:${encodeURIComponent(this.config.prefix)}`;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Test storage availability\n const testKey = `${this.config.prefix}_test`;\n this.config.storage.setItem(testKey, 'test');\n this.config.storage.removeItem(testKey);\n\n this.status = 'connected';\n this.log('Connected to localStorage');\n } catch (error) {\n this.status = 'error';\n throw new SphereError(`LocalStorage not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.log('Disconnected from localStorage');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.chainPubkey);\n }\n\n async get(key: string): Promise<string | null> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n return this.config.storage.getItem(fullKey);\n }\n\n async set(key: string, value: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n this.config.storage.setItem(fullKey, value);\n }\n\n async remove(key: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n this.config.storage.removeItem(fullKey);\n }\n\n async has(key: string): Promise<boolean> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n return this.config.storage.getItem(fullKey) !== null;\n }\n\n async keys(prefix?: string): Promise<string[]> {\n this.ensureConnected();\n const basePrefix = this.getFullKey('');\n const searchPrefix = prefix ? this.getFullKey(prefix) : basePrefix;\n const result: string[] = [];\n\n for (let i = 0; i < this.config.storage.length; i++) {\n const key = this.config.storage.key(i);\n if (key?.startsWith(searchPrefix)) {\n // Return key without the base prefix\n result.push(key.slice(basePrefix.length));\n }\n }\n\n return result;\n }\n\n async clear(prefix?: string): Promise<void> {\n this.ensureConnected();\n const keysToRemove = await this.keys(prefix);\n for (const key of keysToRemove) {\n await this.remove(key);\n }\n }\n\n /**\n * Persist the tracked-address registry by MERGING, never replacing.\n *\n * Every Sphere over this storage holds its own snapshot and writes it in\n * full, so a wholesale write drops the addresses this writer never saw\n * (#766 item 5 — a lost update, reproducible on one network). localStorage\n * offers no transaction, so the read-merge-write is serialized by BACKING\n * STORE — see `serializeTrackedWrite`, and the realm limit stated there.\n */\n async saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void> {\n await serializeTrackedWrite(this.backingStoreId, async () => {\n const onDisk = parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));\n const file: TrackedAddressesFile = {\n version: 1,\n addresses: mergeTrackedAddresses(onDisk, entries),\n };\n await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify(file));\n });\n }\n\n async loadTrackedAddresses(): Promise<TrackedAddressEntry[]> {\n return parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /**\n * Get JSON data\n */\n async getJSON<T>(key: string): Promise<T | null> {\n const value = await this.get(key);\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Set JSON data\n */\n async setJSON<T>(key: string, value: T): Promise<void> {\n await this.set(key, JSON.stringify(value));\n }\n\n // ===========================================================================\n // Private Methods\n // ===========================================================================\n\n private getFullKey(key: string): string {\n // Check if this is a per-address key\n const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n // Add address ID prefix for per-address data\n const id = this.identity.chainPubkey;\n return `${this.config.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.config.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('LocalStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n private getStorageSafe(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n\n // SSR fallback: in-memory storage\n return createInMemoryStorage();\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('LocalStorage', message, ...args);\n }\n}\n\n// =============================================================================\n// In-Memory Storage (SSR Fallback)\n// =============================================================================\n\nfunction createInMemoryStorage(): Storage {\n const data = new Map<string, string>();\n\n return {\n get length() {\n return data.size;\n },\n clear() {\n data.clear();\n },\n getItem(key: string) {\n return data.get(key) ?? null;\n },\n setItem(key: string, value: string) {\n data.set(key, value);\n },\n removeItem(key: string) {\n data.delete(key);\n },\n key(index: number) {\n return Array.from(data.keys())[index] ?? null;\n },\n };\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createLocalStorageProvider(\n config?: LocalStorageProviderConfig\n): LocalStorageProvider {\n return new LocalStorageProvider(config);\n}\n","/**\n * Browser IndexedDB Storage Provider\n * Implements StorageProvider using IndexedDB for large-capacity browser storage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\nimport {\n mergeTrackedAddresses,\n parseTrackedAddresses,\n type TrackedAddressesFile,\n} from '../../../storage/tracked-addresses';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-storage';\nconst DB_VERSION = 1;\nconst STORE_NAME = 'kv';\n\nexport interface IndexedDBStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Database name (default: 'sphere-storage') */\n dbName?: string;\n /**\n * Network. When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global (seed) keys stay shared across networks.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class IndexedDBStorageProvider implements StorageProvider {\n readonly id = 'indexeddb-storage';\n readonly name = 'IndexedDB Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser IndexedDB for large-capacity persistence';\n /** The database + prefix pair — two providers over one pair share erasure (#766). */\n readonly backingStoreId: string;\n\n private prefix: string;\n private dbName: string;\n private network?: NetworkType;\n private debug: boolean;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n private db: IDBDatabase | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBStorageProviderConfig) {\n this.prefix = config?.prefix ?? 'sphere_';\n this.dbName = config?.dbName ?? DB_NAME;\n this.network = config?.network;\n this.debug = config?.debug ?? false;\n this.backingStoreId =\n `indexeddb:${encodeURIComponent(this.dbName)}:${encodeURIComponent(this.prefix)}`;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected' && this.db) return;\n\n // Retry once — a pending deleteDatabase() from a prior clear() can\n // block open() until the deletion completes. A short delay is usually\n // enough for the browser to finish the deletion.\n for (let attempt = 0; attempt < 2; attempt++) {\n this.status = 'connecting';\n const t0 = Date.now();\n logger.debug('IndexedDB',` connect: opening db=${this.dbName}, attempt=${attempt + 1}/2`);\n\n try {\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('IndexedDB open timed out after 5s')), 5000),\n ),\n ]);\n this.status = 'connected';\n logger.debug('IndexedDB',` connect: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return;\n } catch (error) {\n logger.warn('IndexedDB',` connect: open failed db=${this.dbName} attempt=${attempt + 1} (${Date.now() - t0}ms):`, error);\n if (attempt === 0) {\n this.status = 'disconnected';\n await new Promise((r) => setTimeout(r, 1000));\n continue;\n }\n this.status = 'error';\n throw new SphereError(`IndexedDB not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n }\n\n async disconnect(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDB',` disconnect: db=${this.dbName} connId=${cid} wasConnected=${!!this.db}`);\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.db !== null;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.chainPubkey);\n }\n\n async get(key: string): Promise<string | null> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n const result = await this.idbGet(fullKey);\n return result?.v ?? null;\n }\n\n async set(key: string, value: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n await this.idbPut({ k: fullKey, v: value });\n }\n\n async remove(key: string): Promise<void> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n await this.idbDelete(fullKey);\n }\n\n async has(key: string): Promise<boolean> {\n this.ensureConnected();\n const fullKey = this.getFullKey(key);\n const count = await this.idbCount(fullKey);\n return count > 0;\n }\n\n async keys(prefix?: string): Promise<string[]> {\n this.ensureConnected();\n const basePrefix = this.getFullKey('');\n const searchPrefix = prefix ? this.getFullKey(prefix) : basePrefix;\n const allEntries = await this.idbGetAll();\n const result: string[] = [];\n\n for (const entry of allEntries) {\n if (entry.k.startsWith(searchPrefix)) {\n // Return key without the base prefix\n result.push(entry.k.slice(basePrefix.length));\n }\n }\n\n return result;\n }\n\n async clear(prefix?: string): Promise<void> {\n if (!prefix) {\n // Clear the object store contents instead of deleteDatabase().\n // deleteDatabase() gets blocked by leaked IDB connections (e.g. React\n // StrictMode double-mount starts async init, cleanup destroys the\n // instance but the first init's connections keep draining in background).\n // A blocked deleteDatabase also blocks ALL subsequent open() calls,\n // bricking the wallet until the user refreshes the page.\n const t0 = Date.now();\n const prevConnId = this.connId;\n logger.debug('IndexedDB',` clear: starting db=${this.dbName} connId=${prevConnId} status=${this.status} hasDb=${!!this.db}`);\n\n try {\n // Ensure we have a connection to clear the store\n if (!this.db || this.status !== 'connected') {\n if (this.db) {\n logger.debug('IndexedDB',` clear: closing stale handle connId=${prevConnId}`);\n this.db.close();\n this.db = null;\n }\n logger.debug('IndexedDB',` clear: opening fresh connection for wipe`);\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('open timed out')), 3000),\n ),\n ]);\n this.status = 'connected';\n }\n\n // Clear all data from the store — cannot be blocked by other connections\n await this.idbClear();\n logger.debug('IndexedDB',` clear: store cleared db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n } catch (err) {\n logger.warn('IndexedDB',` clear: failed db=${this.dbName} (${Date.now() - t0}ms)`, err);\n } finally {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n return;\n }\n\n this.ensureConnected();\n const keysToRemove = await this.keys(prefix);\n for (const key of keysToRemove) {\n await this.remove(key);\n }\n }\n\n /**\n * Persist the tracked-address registry by MERGING, never replacing.\n *\n * Every Sphere over this storage holds its own snapshot and writes it in\n * full, so a wholesale write drops the addresses this writer never saw\n * (#766 item 5 — a lost update, reproducible on one network). The read, the\n * merge and the write share ONE transaction: a lock on this object would\n * order only this object's calls, and `backingStoreId` exists precisely\n * because two provider objects — or two TABS — can address one database.\n * IndexedDB serializes overlapping readwrite transactions across every\n * connection, so both are covered.\n */\n async saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void> {\n this.ensureConnected();\n await this.idbMergeTrackedAddresses(\n this.getFullKey(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES),\n entries,\n );\n }\n\n async loadTrackedAddresses(): Promise<TrackedAddressEntry[]> {\n return parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));\n }\n\n // ===========================================================================\n // Helpers\n // ===========================================================================\n\n /**\n * Get JSON data\n */\n async getJSON<T>(key: string): Promise<T | null> {\n const value = await this.get(key);\n if (!value) return null;\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n\n /**\n * Set JSON data\n */\n async setJSON<T>(key: string, value: T): Promise<void> {\n await this.set(key, JSON.stringify(value));\n }\n\n // ===========================================================================\n // Private: Key Scoping\n // ===========================================================================\n\n private getFullKey(key: string): string {\n const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n const id = this.identity.chainPubkey;\n return `${this.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected' || !this.db) {\n throw new SphereError('IndexedDBStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n // ===========================================================================\n // Private: IndexedDB Operations\n // ===========================================================================\n\n private openDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n\n request.onsuccess = () => {\n const db = request.result;\n const cid = ++connectionSeq;\n this.connId = cid;\n\n // Auto-close when another context requests version change or deletion.\n // Prevents leaked connections (e.g. React StrictMode double-mount)\n // from blocking deleteDatabase() or version upgrades.\n db.onversionchange = () => {\n logger.debug('IndexedDB',` onversionchange: auto-closing db=${this.dbName} connId=${cid}`);\n db.close();\n if (this.db === db) {\n this.db = null;\n this.status = 'disconnected';\n }\n };\n resolve(db);\n };\n\n // onblocked fires when another connection (e.g. other tab) holds\n // the database at a lower version. Log it — onsuccess will follow\n // once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDB',` open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'k' });\n }\n };\n });\n }\n\n private idbGet(key: string): Promise<{ k: string; v: string } | undefined> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? undefined);\n });\n }\n\n private idbPut(entry: { k: string; v: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.put(entry);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbDelete(key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.delete(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbCount(key: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.count(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n });\n }\n\n private idbGetAll(): Promise<{ k: string; v: string }[]> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.getAll();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private idbMergeTrackedAddresses(key: string, entries: TrackedAddressEntry[]): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const read = store.get(key);\n read.onerror = () => reject(read.error);\n read.onsuccess = () => {\n const onDisk = parseTrackedAddresses((read.result as { v?: string } | undefined)?.v ?? null);\n const file: TrackedAddressesFile = {\n version: 1,\n addresses: mergeTrackedAddresses(onDisk, entries),\n };\n store.put({ k: key, v: JSON.stringify(file) });\n };\n // Resolve on the TRANSACTION, not the put: only completion means durable.\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n // A throw inside onsuccess aborts the transaction: this is what stops such a save\n // from resolving with nothing written.\n tx.onabort = () => reject(tx.error ?? new Error('tracked-address write aborted'));\n });\n }\n\n private idbClear(): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.clear();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('IndexedDB', message, ...args);\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createIndexedDBStorageProvider(\n config?: IndexedDBStorageProviderConfig\n): IndexedDBStorageProvider {\n return new IndexedDBStorageProvider(config);\n}\n","/**\n * Nostr Transport Provider\n * Platform-independent implementation using Nostr protocol for P2P messaging\n *\n * Uses @unicitylabs/nostr-js-sdk for:\n * - Real secp256k1 event signing\n * - NIP-04 encryption/decryption\n * - Event ID calculation\n * - NostrClient for reliable connection management (ping, reconnect, NIP-42)\n *\n * WebSocket is injected via factory for cross-platform support\n */\n\nimport { Buffer } from 'buffer';\nimport { sha256 as sha256Noble } from '@noble/hashes/sha2.js';\nimport {\n NostrKeyManager,\n NIP04,\n NIP17,\n NIP44,\n Event as NostrEventClass,\n EventKinds,\n decryptNametag,\n NostrClient,\n Filter,\n isChatMessage,\n isReadReceipt,\n} from '@unicitylabs/nostr-js-sdk';\nimport type { BindingInfo } from '@unicitylabs/nostr-js-sdk';\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';\nimport { logger } from '../core/logger';\nimport type { ProviderStatus, FullIdentity } from '../types';\nimport { SphereError } from '../core/errors';\nimport type {\n TransportProvider,\n MessageHandler,\n ComposingHandler,\n BroadcastHandler,\n IncomingMessage,\n IncomingBroadcast,\n TransportEvent,\n TransportEventCallback,\n PeerInfo,\n ReadReceiptHandler,\n IncomingReadReceipt,\n TypingIndicatorHandler,\n IncomingTypingIndicator,\n} from './transport-provider';\nimport type { WebSocketFactory, UUIDGenerator } from './websocket';\nimport { defaultUUIDGenerator } from './websocket';\nimport {\n DEFAULT_NOSTR_RELAYS,\n NOSTR_EVENT_KINDS,\n STORAGE_KEYS_GLOBAL,\n TIMEOUTS,\n} from '../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\n/**\n * Minimal key-value storage interface for transport persistence.\n * Used to persist the last processed event timestamp across sessions.\n */\nexport interface TransportStorageAdapter {\n get(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n}\n\nexport interface NostrTransportProviderConfig {\n /** Nostr relay URLs */\n relays?: string[];\n /** Connection timeout (ms) */\n timeout?: number;\n /** Auto-reconnect on disconnect */\n autoReconnect?: boolean;\n /** Reconnect delay (ms) */\n reconnectDelay?: number;\n /** Max reconnect attempts */\n maxReconnectAttempts?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** WebSocket factory (required for platform support) */\n createWebSocket: WebSocketFactory;\n /** UUID generator (optional, defaults to crypto.randomUUID) */\n generateUUID?: UUIDGenerator;\n /** Optional storage adapter for persisting subscription timestamps */\n storage?: TransportStorageAdapter;\n}\n\nconst COMPOSING_INDICATOR_KIND = 25050;\nconst TIMESTAMP_RANDOMIZATION = 2 * 24 * 60 * 60;\n\n// Alias for backward compatibility\nconst EVENT_KINDS = NOSTR_EVENT_KINDS;\n\n/**\n * The network a RAW SIGNED binding event declares in its content, or undefined —\n * read-if-present, never invented (a stamped guess is what made the §5.6 money\n * guard dead, #733).\n *\n * It takes the EVENT, not a parsed shape, and the parameter type is the point:\n * nostr-js-sdk's `parseBindingInfo` whitelists the binding content\n * (`public_key`/`l1_address`/`direct_address`/`proxy_address`/`nametag`) and\n * drops everything else, so a `BindingInfo` can never carry a network — handing\n * one to this function would compile into a value that is `undefined` for all\n * time, which is exactly the dead read the #735 review caught. A `BindingInfo`\n * is not a `NostrEvent`; that mistake is now a type error, not a comment.\n *\n * Nothing declares a network yet either: the publish side\n * (`IdentityBindingParams` + `createBindingEvent`/`createIdentityBindingEvent`)\n * whitelists the same fields, so the claim cannot ride today's publish API at\n * all — hence sends to an unproven peer are SIGNALLED, not refused (#734).\n */\nfunction declaredNetwork(event: NostrEvent): string | undefined {\n let value: unknown;\n try {\n value = (JSON.parse(event.content) as { network?: unknown } | null)?.network;\n } catch {\n return undefined;\n }\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class NostrTransportProvider implements TransportProvider {\n readonly id = 'nostr';\n readonly name = 'Nostr Transport';\n readonly type = 'p2p' as const;\n readonly description = 'P2P messaging via Nostr protocol';\n\n private config: Required<Omit<NostrTransportProviderConfig, 'createWebSocket' | 'generateUUID' | 'storage'>> & {\n createWebSocket: WebSocketFactory;\n generateUUID: UUIDGenerator;\n };\n private storage: TransportStorageAdapter | null = null;\n /** In-memory max event timestamp to avoid read-before-write races in updateLastEventTimestamp. */\n private lastEventTs: number = 0;\n /** In-memory max DM (gift-wrap) event timestamp. */\n private lastDmEventTs: number = 0;\n /** Fallback 'since' timestamp for first-time address subscriptions (consumed once). */\n private fallbackSince: number | null = null;\n /** Fallback 'since' timestamp for DM (gift-wrap) subscriptions (consumed once). */\n private fallbackDmSince: number | null = null;\n private identity: FullIdentity | null = null;\n private keyManager: NostrKeyManager | null = null;\n private status: ProviderStatus = 'disconnected';\n\n // NostrClient from nostr-js-sdk handles all WebSocket management,\n // keepalive pings, reconnection, and NIP-42 authentication\n private nostrClient: NostrClient | null = null;\n private mainSubscriptionId: string | null = null;\n\n // Event handlers\n private processedEventIds = new Set<string>();\n private messageHandlers: Set<MessageHandler> = new Set();\n private readReceiptHandlers: Set<ReadReceiptHandler> = new Set();\n private typingIndicatorHandlers: Set<TypingIndicatorHandler> = new Set();\n private composingHandlers: Set<ComposingHandler> = new Set();\n private pendingMessages: IncomingMessage[] = [];\n private broadcastHandlers: Map<string, Set<BroadcastHandler>> = new Map();\n private eventCallbacks: Set<TransportEventCallback> = new Set();\n\n constructor(config: NostrTransportProviderConfig) {\n this.config = {\n relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],\n timeout: config.timeout ?? TIMEOUTS.WEBSOCKET_CONNECT,\n autoReconnect: config.autoReconnect ?? true,\n reconnectDelay: config.reconnectDelay ?? TIMEOUTS.NOSTR_RECONNECT_DELAY,\n maxReconnectAttempts: config.maxReconnectAttempts ?? TIMEOUTS.MAX_RECONNECT_ATTEMPTS,\n debug: config.debug ?? false,\n createWebSocket: config.createWebSocket,\n generateUUID: config.generateUUID ?? defaultUUIDGenerator,\n };\n this.storage = config.storage ?? null;\n }\n\n /**\n * Get the WebSocket factory (used by MultiAddressTransportMux to share the same factory).\n */\n getWebSocketFactory(): WebSocketFactory {\n return this.config.createWebSocket;\n }\n\n /**\n * Get the configured relay URLs.\n */\n getConfiguredRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get the storage adapter.\n */\n getStorageAdapter(): TransportStorageAdapter | null {\n return this.storage;\n }\n\n /**\n * Get the underlying NostrClient (or null if not yet connected).\n *\n * Exposed so {@link MultiAddressTransportMux} can share the same\n * client/socket pair instead of opening a duplicate WebSocket per\n * relay (#123). The transport owns the client's lifecycle — callers\n * MUST NOT call {@code disconnect()} on the returned instance.\n */\n getNostrClient(): NostrClient | null {\n return this.nostrClient;\n }\n\n /**\n * Suppress event subscriptions — unsubscribe wallet/chat filters\n * but keep the connection alive for resolve/identity-binding operations.\n * Used when MultiAddressTransportMux takes over event handling.\n */\n suppressSubscriptions(): void {\n if (!this.nostrClient) return;\n\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Prevent re-subscription on reconnect by marking subscriptions as suppressed\n this._subscriptionsSuppressed = true;\n logger.debug('Nostr', 'Subscriptions suppressed — mux handles event routing');\n }\n\n // Flag to prevent re-subscription after suppressSubscriptions()\n private _subscriptionsSuppressed = false;\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Ensure keyManager exists for NostrClient\n if (!this.keyManager) {\n // Create a temporary key manager - will be replaced when setIdentity is called\n const tempKey = Buffer.alloc(32);\n crypto.getRandomValues(tempKey);\n this.keyManager = NostrKeyManager.fromPrivateKey(tempKey);\n }\n\n // Create NostrClient with robust connection handling:\n // - autoReconnect: automatic reconnection with exponential backoff\n // - pingIntervalMs: keepalive pings to detect stale connections\n // - NIP-42 AUTH handling built-in\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16, // exponential backoff cap\n pingIntervalMs: 15000, // 15 second keepalive pings (more aggressive to prevent drops)\n // Bump query timeout from the SDK default of 5s to 20s.\n // Real-world testnet observation (2026-05-01): under transient\n // relay overload, kind:30078 (nametag binding) queries take 5-7s\n // to return EVENT messages, even though the binding is on the\n // relay. The default 5s timeout fires before the event arrives,\n // resolveNametag returns null, and downstream sendDM throws\n // INVALID_RECIPIENT — causing every np.propose_deal to fail. 20s\n // gives the slow path enough headroom to complete while still\n // bailing reasonably fast on a truly broken relay.\n queryTimeoutMs: 20000,\n });\n\n // Add connection event listener for logging\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n this.emitEvent({ type: 'transport:reconnecting', timestamp: Date.now() });\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n // Re-establish subscriptions — the relay drops them on disconnect.\n this.subscribeToEvents().catch((err) => {\n logger.error('Nostr', 'Failed to re-subscribe after reconnect:', err);\n });\n },\n });\n\n // Connect to all relays (with timeout to prevent indefinite hang)\n await this.connectWithDeadline(\n this.nostrClient,\n `Transport connection timed out after ${this.config.timeout}ms`\n );\n\n // Need at least one successful connection\n if (!this.nostrClient.isConnected()) {\n throw new SphereError('Failed to connect to any relay', 'TRANSPORT_ERROR');\n }\n\n this.status = 'connected';\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n logger.debug('Nostr', 'Connected to', this.nostrClient.getConnectedRelays().size, 'relays');\n\n // Set up subscriptions\n if (this.identity) {\n await this.subscribeToEvents();\n }\n } catch (error) {\n this.status = 'error';\n throw error;\n }\n }\n\n /**\n * Race one client's relay connect against the configured timeout.\n *\n * The timer is cleared in a `finally` on EVERY path. `Promise.race` does not\n * cancel the loser: an un-cleared `setTimeout` keeps Node's event loop pinned\n * for the whole `config.timeout` after the call has already returned (and\n * then rejects a promise nobody is listening to any more).\n */\n private async connectWithDeadline(client: NostrClient, timeoutMessage: string): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n client.connect(...this.config.relays),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(timeoutMessage)), this.config.timeout);\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n async disconnect(): Promise<void> {\n if (this.nostrClient) {\n this.nostrClient.disconnect();\n this.nostrClient = null;\n }\n this.mainSubscriptionId = null;\n this.walletSubscriptionId = null;\n this.chatSubscriptionId = null;\n this.chatEoseFired = false;\n this.status = 'disconnected';\n this.emitEvent({ type: 'transport:disconnected', timestamp: Date.now() });\n logger.debug('Nostr', 'Disconnected from all relays');\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.nostrClient?.isConnected() === true;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // Dynamic Relay Management\n // ===========================================================================\n\n /**\n * Get list of configured relay URLs\n */\n getRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get list of currently connected relay URLs\n */\n getConnectedRelays(): string[] {\n if (!this.nostrClient) return [];\n return Array.from(this.nostrClient.getConnectedRelays());\n }\n\n /**\n * Add a new relay dynamically\n * Will connect immediately if provider is already connected\n */\n async addRelay(relayUrl: string): Promise<boolean> {\n // Check if already configured\n if (this.config.relays.includes(relayUrl)) {\n logger.debug('Nostr', 'Relay already configured:', relayUrl);\n return false;\n }\n\n // Add to config\n this.config.relays.push(relayUrl);\n\n // Connect if provider is connected\n if (this.status === 'connected' && this.nostrClient) {\n try {\n await this.nostrClient.connect(relayUrl);\n logger.debug('Nostr', 'Added and connected to relay:', relayUrl);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: true },\n });\n return true;\n } catch (error) {\n logger.debug('Nostr', 'Failed to connect to new relay:', relayUrl, error);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false, error: String(error) },\n });\n return false;\n }\n }\n\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false },\n });\n return true;\n }\n\n /**\n * Remove a relay dynamically\n * Will disconnect from the relay if connected\n * NOTE: NostrClient doesn't support removing individual relays at runtime.\n * We remove from config so it won't be used on next connect().\n */\n async removeRelay(relayUrl: string): Promise<boolean> {\n const index = this.config.relays.indexOf(relayUrl);\n if (index === -1) {\n logger.debug('Nostr', 'Relay not found:', relayUrl);\n return false;\n }\n\n // Remove from config\n this.config.relays.splice(index, 1);\n logger.debug('Nostr', 'Removed relay from config:', relayUrl);\n\n this.emitEvent({\n type: 'transport:relay_removed',\n timestamp: Date.now(),\n data: { relay: relayUrl },\n });\n\n // Check if we still have connections\n if (this.nostrClient && !this.nostrClient.isConnected() && this.status === 'connected') {\n this.status = 'error';\n this.emitEvent({\n type: 'transport:error',\n timestamp: Date.now(),\n data: { error: 'No connected relays remaining' },\n });\n }\n\n return true;\n }\n\n /**\n * Check if a relay is configured\n */\n hasRelay(relayUrl: string): boolean {\n return this.config.relays.includes(relayUrl);\n }\n\n /**\n * Check if a relay is currently connected\n */\n isRelayConnected(relayUrl: string): boolean {\n if (!this.nostrClient) return false;\n return this.nostrClient.getConnectedRelays().has(relayUrl);\n }\n\n // ===========================================================================\n // TransportProvider Implementation\n // ===========================================================================\n\n async setIdentity(identity: FullIdentity): Promise<void> {\n // Staged, NOT applied: the key material belongs to the client that will\n // carry it. Applying it up front left a FAILED swap running the old client\n // and its old-address subscriptions under the NEW key — gift wraps for the\n // old address decrypted with the wrong key, the new address subscribed\n // nowhere, and no error path that could put either back.\n const secretKey = Buffer.from(identity.privateKey, 'hex');\n const nextKeyManager = NostrKeyManager.fromPrivateKey(secretKey);\n\n // Nostr-format pubkey (32 bytes / 64 hex chars)\n const nostrPubkey = nextKeyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Identity staged, Nostr pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n /**\n * Commit the staged identity — always together with the client it belongs\n * to, never before it. The per-address dedup window is reset here for the\n * same reason: stale entries from the previous address must not block the\n * new address's events, and wiping them for a swap that then FAILS would\n * re-admit already-processed events on the address still subscribed.\n */\n const applyStagedIdentity = (): void => {\n this.identity = identity;\n this.keyManager = nextKeyManager;\n this.processedEventIds.clear();\n this.lastEventTs = 0;\n this.lastDmEventTs = 0;\n this.fallbackDmSince = null;\n };\n\n // NostrClient cannot swap its key at runtime, so an identity change while\n // connected means building a replacement client and handing it the socket.\n if (this.nostrClient && this.status === 'connected') {\n logger.debug('Nostr', 'Identity changed while connected - recreating NostrClient');\n const oldClient = this.nostrClient;\n\n // Build the replacement into a LOCAL: the field must not move until the\n // new client is connected. Swapping first and then failing the connect\n // orphans `oldClient` — socket open but unreachable from the provider, so\n // `disconnect()` cannot reach it either — and setIdentity never touches\n // `status`, so the caller's next retry re-enters here and leaks another.\n const nextClient = new NostrClient(nextKeyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16,\n pingIntervalMs: 15000, // 15 second keepalive pings\n queryTimeoutMs: 20000, // see same option above for rationale\n });\n\n // Add connection event listener\n nextClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n },\n });\n\n try {\n await this.connectWithDeadline(\n nextClient,\n `Transport reconnection timed out after ${this.config.timeout}ms`\n );\n } catch (error) {\n // Dispose ONLY the replacement; the provider stays on the working\n // `oldClient`. NOT both: MultiAddressTransportMux SHARES this client\n // (ensureTransportMux suppresses subscriptions and reuses the socket),\n // so killing it over a transient relay timeout kills the mux's too.\n try { nextClient.disconnect(); } catch { /* best-effort cleanup */ }\n throw error;\n }\n\n // A `disconnect()` (or a competing swap) during the await above already\n // tore this provider down: installing a freshly connected client now\n // resurrects it — live socket plus subscriptions, owned by nobody, with\n // the caller told the identity took. Dispose the replacement and reject,\n // because neither the client nor the identity was applied.\n if (this.nostrClient !== oldClient || this.status !== 'connected') {\n try { nextClient.disconnect(); } catch { /* best-effort cleanup */ }\n throw new SphereError(\n 'Transport client changed or left the connected state during setIdentity'\n + ' - identity not applied',\n 'TRANSPORT_ERROR'\n );\n }\n\n // The swap is committed. From here on `oldClient` is unreachable from the\n // provider, so it must be disposed on EVERY path out — including a\n // throwing `subscribeToEvents()`.\n this.nostrClient = nextClient;\n applyStagedIdentity();\n try {\n await this.subscribeToEvents();\n } finally {\n try { oldClient.disconnect(); } catch { /* best-effort cleanup */ }\n }\n } else if (this.isConnected()) {\n // No client swap needed — commit first, since subscribeToEvents() reads\n // `identity`/`keyManager` to build its filters.\n applyStagedIdentity();\n await this.subscribeToEvents();\n } else {\n // Not connected: no client work to fail, so nothing to stage against.\n applyStagedIdentity();\n }\n }\n\n setFallbackSince(sinceSeconds: number): void {\n this.fallbackSince = sinceSeconds;\n }\n\n setFallbackDmSince(sinceSeconds: number): void {\n this.fallbackDmSince = sinceSeconds;\n }\n\n /**\n * Get the Nostr-format public key (32 bytes / 64 hex chars)\n * This is the x-coordinate only, without the 02/03 prefix.\n */\n getNostrPubkey(): string {\n if (!this.keyManager) {\n throw new SphereError('KeyManager not initialized - call setIdentity first', 'NOT_INITIALIZED');\n }\n return this.keyManager.getPublicKeyHex();\n }\n\n async sendMessage(recipientPubkey: string, content: string): Promise<string> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Wrap content with sender nametag for Sphere app compatibility\n const senderNametag = this.identity?.nametag;\n const wrappedContent = senderNametag\n ? JSON.stringify({ senderNametag, text: content })\n : content;\n\n // Create NIP-17 gift-wrapped message (kind 1059) for recipient\n const giftWrap = NIP17.createGiftWrap(this.keyManager!, nostrRecipient, wrappedContent);\n\n await this.publishWithVerification(giftWrap, 3, 'dm');\n\n // NIP-17 self-wrap: send a copy to ourselves so relay can replay sent messages.\n // Content includes recipientPubkey and originalId for dedup against the live-sent record.\n const selfWrapContent = JSON.stringify({\n selfWrap: true,\n originalId: giftWrap.id,\n recipientPubkey,\n senderNametag,\n text: content,\n });\n const selfPubkey = this.keyManager!.getPublicKeyHex();\n const selfGiftWrap = NIP17.createGiftWrap(this.keyManager!, selfPubkey, selfWrapContent);\n this.publishEvent(selfGiftWrap).catch(err => {\n logger.debug('Nostr', 'Self-wrap publish failed:', err);\n });\n\n this.emitEvent({\n type: 'message:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return giftWrap.id;\n }\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.add(handler);\n\n // Flush any messages that arrived before this handler was registered\n if (this.pendingMessages.length > 0) {\n const pending = this.pendingMessages;\n this.pendingMessages = [];\n logger.debug('Nostr', 'Flushing', pending.length, 'buffered messages to new handler');\n for (const message of pending) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error (buffered):', error);\n }\n }\n }\n\n return () => this.messageHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Read Receipts\n // ===========================================================================\n\n async sendReadReceipt(recipientTransportPubkey: string, messageEventId: string): Promise<void> {\n if (!this.keyManager) throw new SphereError('Not initialized', 'NOT_INITIALIZED');\n\n // NIP-17 uses x-only pubkeys (64 hex chars, no 02/03 prefix)\n const nostrRecipient = recipientTransportPubkey.length === 66\n ? recipientTransportPubkey.slice(2)\n : recipientTransportPubkey;\n\n const event = NIP17.createReadReceipt(this.keyManager, nostrRecipient, messageEventId);\n await this.publishEvent(event);\n logger.debug('Nostr', 'Sent read receipt for:', messageEventId, 'to:', nostrRecipient.slice(0, 16));\n }\n\n onReadReceipt(handler: ReadReceiptHandler): () => void {\n this.readReceiptHandlers.add(handler);\n return () => this.readReceiptHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Typing Indicators\n // ===========================================================================\n\n async sendTypingIndicator(recipientTransportPubkey: string): Promise<void> {\n if (!this.keyManager) throw new SphereError('Not initialized', 'NOT_INITIALIZED');\n\n const nostrRecipient = recipientTransportPubkey.length === 66\n ? recipientTransportPubkey.slice(2)\n : recipientTransportPubkey;\n\n const content = JSON.stringify({\n type: 'typing',\n senderNametag: this.identity?.nametag,\n });\n const event = NIP17.createGiftWrap(this.keyManager, nostrRecipient, content);\n await this.publishEvent(event);\n }\n\n onTypingIndicator(handler: TypingIndicatorHandler): () => void {\n this.typingIndicatorHandlers.add(handler);\n return () => this.typingIndicatorHandlers.delete(handler);\n }\n\n onChatReady(handler: () => void): () => void {\n // If EOSE already fired, invoke immediately\n if (this.chatEoseFired) {\n try { handler(); } catch { /* ignore */ }\n return () => {};\n }\n this.chatEoseHandlers.push(handler);\n return () => {\n this.chatEoseHandlers = this.chatEoseHandlers.filter(h => h !== handler);\n };\n }\n\n // ===========================================================================\n // Composing Indicators (NIP-59 kind 25050)\n // ===========================================================================\n\n onComposing(handler: ComposingHandler): () => void {\n this.composingHandlers.add(handler);\n return () => this.composingHandlers.delete(handler);\n }\n\n async sendComposingIndicator(recipientPubkey: string, content: string): Promise<void> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Build NIP-17 gift wrap with kind 25050 rumor (instead of kind 14 for DMs).\n // We replicate the three-layer NIP-59 envelope because NIP17.createGiftWrap\n // hardcodes kind 14 for the inner rumor.\n const giftWrap = this.createCustomKindGiftWrap(nostrRecipient, content, COMPOSING_INDICATOR_KIND);\n await this.publishEvent(giftWrap);\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Routes to the appropriate specific resolve method based on identifier format.\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n // @nametag\n if (identifier.startsWith('@')) {\n return this.resolveNametagInfo(identifier.slice(1));\n }\n\n // DIRECT:// address\n if (identifier.startsWith('DIRECT:')) {\n return this.resolveAddressInfo(identifier);\n }\n\n // 66-char hex starting with 02/03 → compressed chain pubkey (33 bytes)\n if (/^0[23][0-9a-f]{64}$/i.test(identifier)) {\n return this.resolveAddressInfo(identifier);\n }\n\n // 64-char hex string → transport pubkey\n if (/^[0-9a-f]{64}$/i.test(identifier)) {\n return this.resolveTransportPubkeyInfo(identifier);\n }\n\n // Fallback: treat as bare nametag\n return this.resolveNametagInfo(identifier);\n }\n\n async resolveNametag(nametag: string): Promise<string | null> {\n await this.ensureConnectedForResolve();\n // Delegate to nostr-js-sdk, which resolves via UNIP-01 (prefers the\n // relay-vetted single-owner binding; see unicity-tokens-relay/docs/UNIP-01.md)\n return this.nostrClient!.queryPubkeyByNametag(nametag);\n }\n\n async resolveNametagInfo(nametag: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n // Delegate to nostr-js-sdk, which resolves via UNIP-01 (prefers the\n // relay-vetted single-owner binding; see unicity-tokens-relay/docs/UNIP-01.md)\n const binding = await this.nostrClient!.queryBindingByNametag(nametag);\n if (!binding) {\n logger.debug('Nostr', `resolveNametagInfo: no binding events found for Unicity ID \"${nametag}\"`);\n return null;\n }\n\n return this.bindingInfoToPeerInfo(binding, nametag);\n }\n\n /**\n * Resolve a DIRECT:// address to full peer info.\n * Performs reverse lookup via nostr-js-sdk (UNIP-01 marker-preferring resolution).\n */\n async resolveAddressInfo(address: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const binding = await this.nostrClient!.queryBindingByAddress(address);\n if (!binding) return null;\n\n return this.bindingInfoToPeerInfo(binding);\n }\n\n /**\n * Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).\n *\n * `network` IS DELIBERATELY ABSENT HERE — a live limitation, not an oversight\n * (#734). `queryBindingByNametag`/`queryBindingByAddress` are the whole public\n * resolution API for `@nametag` and `DIRECT://`, and both hardcode\n * `parseBindingInfo` inside the SDK's PRIVATE `queryWithFirstSeenWins`: the\n * caller never sees the signed event that resolution selected, and the parse\n * has already dropped every content field outside its whitelist. Re-querying\n * raw would mean re-deriving UNIP-01 ownership ourselves (marker preference,\n * cross-author first-seen-wins, ambiguity→null, multi-relay settle — all\n * private), and a second resolver that can disagree with the SDK's is worse\n * than a missing field on a MONEY guard. Nothing can declare a network today\n * regardless: the publish builders whitelist the same fields.\n *\n * CONSEQUENCE, stated plainly: for `@nametag` and `DIRECT://` recipients the\n * §5.6 cross-network guard can only SIGNAL (`recipient:network-unverified`),\n * never refuse. Upstream #734 must add `network` to the binding builders AND\n * to `BindingInfo`; the tripwire in\n * tests/unit/payments-v2/recipient-network.transport.test.ts fails the day it\n * lands, so this wiring cannot be forgotten. Do not \"fix\" this by inventing a\n * value — a stamped network is what made the guard unfalsifiable (#733).\n */\n private async bindingInfoToPeerInfo(binding: BindingInfo, nametag?: string): Promise<PeerInfo> {\n return {\n nametag: nametag || binding.nametag,\n transportPubkey: binding.transportPubkey,\n chainPubkey: binding.publicKey || '',\n directAddress: binding.directAddress || '',\n timestamp: binding.timestamp,\n };\n }\n\n /**\n * Resolve transport pubkey (Nostr pubkey) to full peer info.\n * Queries binding events authored by the given pubkey.\n */\n async resolveTransportPubkeyInfo(transportPubkey: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [transportPubkey],\n limit: 5,\n });\n\n if (events.length === 0) return null;\n\n // Sort by timestamp descending and take the most recent\n events.sort((a, b) => b.created_at - a.created_at);\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n // This route parses the signed event itself, so a declared network is\n // readable here — unlike the SDK-parsed @nametag/DIRECT:// routes above.\n const network = declaredNetwork(bindingEvent);\n\n return {\n nametag: content.nametag || undefined,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key || '',\n directAddress: content.direct_address || '',\n ...(network !== undefined ? { network } : {}),\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n return {\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n directAddress: '',\n timestamp: bindingEvent.created_at * 1000,\n };\n }\n }\n\n /**\n * Batch-resolve multiple transport pubkeys to peer info.\n * Used for HD address discovery — single relay query with multi-author filter.\n */\n async discoverAddresses(transportPubkeys: string[]): Promise<PeerInfo[]> {\n await this.ensureConnectedForResolve();\n\n if (transportPubkeys.length === 0) return [];\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: transportPubkeys,\n limit: transportPubkeys.length * 2,\n });\n\n if (events.length === 0) return [];\n\n // Group by author, take most recent per author\n const byAuthor = new Map<string, NostrEvent>();\n for (const event of events) {\n const existing = byAuthor.get(event.pubkey);\n if (!existing || event.created_at > existing.created_at) {\n byAuthor.set(event.pubkey, event);\n }\n }\n\n const results: PeerInfo[] = [];\n for (const [pubkey, event] of byAuthor) {\n try {\n const content = JSON.parse(event.content);\n const network = declaredNetwork(event);\n results.push({\n nametag: content.nametag || undefined,\n transportPubkey: pubkey,\n chainPubkey: content.public_key || '',\n directAddress: content.direct_address || '',\n ...(network !== undefined ? { network } : {}),\n timestamp: event.created_at * 1000,\n });\n } catch {\n // Skip unparseable events\n }\n }\n\n return results;\n }\n\n /**\n * Recover nametag for the current identity by searching for encrypted nametag events\n * Used after wallet import to recover associated nametag\n * @returns Decrypted nametag or null if none found\n */\n async recoverNametag(): Promise<string | null> {\n await this.ensureConnectedForResolve();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n if (!this.identity || !this.keyManager) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n logger.debug('Nostr', 'Searching for nametag events for pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // Query for nametag binding events authored by this pubkey\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [nostrPubkey],\n limit: 10, // Get recent events in case of updates\n });\n\n if (events.length === 0) {\n logger.debug('Nostr', 'No nametag events found for this pubkey');\n return null;\n }\n\n // Sort by timestamp descending to get most recent\n events.sort((a, b) => b.created_at - a.created_at);\n\n // Try to decrypt nametag from events\n for (const event of events) {\n try {\n const content = JSON.parse(event.content);\n if (content.encrypted_nametag) {\n const decrypted = await decryptNametag(\n content.encrypted_nametag,\n this.identity.privateKey\n );\n if (decrypted) {\n logger.debug('Nostr', 'Recovered Unicity ID:', decrypted);\n return decrypted;\n }\n }\n } catch {\n // Try next event\n continue;\n }\n }\n\n logger.debug('Nostr', 'Could not decrypt Unicity ID from any event');\n return null;\n }\n\n /**\n * Publish identity binding event on Nostr.\n * Without nametag: publishes base binding (chainPubkey, directAddress)\n * using a per-identity d-tag for address discovery.\n * With nametag: delegates to nostr-js-sdk's publishNametagBinding which handles\n * conflict detection (UNIP-01 single-owner), encryption, and indexed tags.\n *\n * @returns true if successful, false if nametag is taken by another pubkey\n */\n async publishIdentityBinding(\n chainPubkey: string,\n directAddress: string,\n nametag?: string,\n ): Promise<boolean> {\n this.ensureReady();\n\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n\n if (nametag) {\n // Delegate to nostr-js-sdk — handles conflict detection, encryption, and event creation\n try {\n const success = await this.nostrClient!.publishNametagBinding(\n nametag,\n nostrPubkey,\n {\n publicKey: chainPubkey,\n directAddress,\n },\n );\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding with Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n } catch (error) {\n // publishNametagBinding throws if nametag is already claimed\n if (error instanceof Error && error.message.includes('already claimed')) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag);\n return false;\n }\n throw error;\n }\n }\n\n // No nametag — delegate to nostr-js-sdk for base identity binding\n const success = await this.nostrClient!.publishIdentityBinding({\n publicKey: chainPubkey,\n directAddress,\n });\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding (no Unicity ID) for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n }\n\n // Track broadcast subscriptions\n private broadcastSubscriptions: Map<string, string> = new Map(); // key -> subId\n\n subscribeToBroadcast(tags: string[], handler: BroadcastHandler): () => void {\n const key = tags.sort().join(':');\n\n if (!this.broadcastHandlers.has(key)) {\n this.broadcastHandlers.set(key, new Set());\n\n // Subscribe to relay\n if (this.isConnected() && this.nostrClient) {\n this.subscribeToTags(tags);\n }\n }\n\n this.broadcastHandlers.get(key)!.add(handler);\n\n return () => {\n this.broadcastHandlers.get(key)?.delete(handler);\n if (this.broadcastHandlers.get(key)?.size === 0) {\n this.broadcastHandlers.delete(key);\n // Unsubscribe from relay\n const subId = this.broadcastSubscriptions.get(key);\n if (subId && this.nostrClient) {\n this.nostrClient.unsubscribe(subId);\n this.broadcastSubscriptions.delete(key);\n }\n }\n };\n }\n\n async publishBroadcast(content: string, tags?: string[]): Promise<string> {\n this.ensureReady();\n\n const eventTags = tags?.map((t) => ['t', t]) ?? [];\n const event = await this.createEvent(EVENT_KINDS.BROADCAST, content, eventTags);\n\n await this.publishEvent(event);\n return event.id;\n }\n\n // ===========================================================================\n // Event Subscription\n // ===========================================================================\n\n onEvent(callback: TransportEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n // ===========================================================================\n // Private: Message Handling\n // ===========================================================================\n\n private async handleEvent(event: NostrEvent): Promise<void> {\n // Dedup: skip events already processed by another subscription\n if (event.id && this.processedEventIds.has(event.id)) {\n return;\n }\n if (event.id) {\n this.processedEventIds.add(event.id);\n }\n\n logger.debug('Nostr', 'Processing event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n try {\n switch (event.kind) {\n case EVENT_KINDS.DIRECT_MESSAGE:\n await this.handleDirectMessage(event);\n break;\n case EventKinds.GIFT_WRAP:\n logger.debug('Nostr', 'Handling gift wrap (NIP-17 DM)');\n await this.handleGiftWrap(event);\n break;\n case EVENT_KINDS.BROADCAST:\n this.handleBroadcast(event);\n break;\n }\n\n // Persist the latest event timestamp for resumption on reconnect.\n // Only update for wallet event kinds (not chat/broadcast).\n if (event.created_at && this.storage && this.keyManager) {\n if (event.kind === EVENT_KINDS.DIRECT_MESSAGE) {\n this.updateLastEventTimestamp(event.created_at);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle event:', error);\n }\n }\n\n /**\n * Save the max event timestamp to storage (fire-and-forget, no await needed by caller).\n * Uses in-memory `lastEventTs` to avoid read-before-write race conditions\n * when multiple events arrive in quick succession.\n */\n private updateLastEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastEventTs) return;\n\n this.lastEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last event timestamp:', err);\n });\n }\n\n /** Persist the max DM (gift-wrap) event timestamp for the since filter on next connect. */\n private updateLastDmEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastDmEventTs) return;\n\n this.lastDmEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last DM event timestamp:', err);\n });\n }\n\n private async handleDirectMessage(event: NostrEvent): Promise<void> {\n // NIP-04 (kind 4) is deprecated for DMs - only used for legacy token transfers\n // DMs should come through NIP-17 (kind 1059 gift wrap) via handleGiftWrap\n // This handler is kept for backwards compatibility but does NOT dispatch to messageHandlers\n logger.debug('Nostr', 'Ignoring NIP-04 kind 4 event (DMs use NIP-17):', event.id?.slice(0, 12));\n }\n\n private async handleGiftWrap(event: NostrEvent): Promise<void> {\n if (!this.identity || !this.keyManager) {\n logger.debug('Nostr', 'handleGiftWrap: no identity/keyManager');\n return;\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pm = NIP17.unwrap(event as any, this.keyManager);\n\n // Persist DM timestamp after successful unwrap so failed decryptions\n // do not advance the since filter and permanently skip events.\n // Use real wall-clock time, NOT event.created_at — NIP-17 gift wraps\n // randomize created_at by ±2 days for privacy, so it can be in the future.\n this.updateLastDmEventTimestamp(Math.floor(Date.now() / 1000));\n\n logger.debug('Nostr', 'Gift wrap unwrapped, sender:', pm.senderPubkey?.slice(0, 16), 'kind:', pm.kind);\n\n // Handle self-wrap (sent message copy for relay replay)\n if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.selfWrap && parsed.recipientPubkey) {\n logger.debug('Nostr', 'Self-wrap replay for recipient:', parsed.recipientPubkey?.slice(0, 16));\n const message: IncomingMessage = {\n id: parsed.originalId || pm.eventId,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n recipientTransportPubkey: parsed.recipientPubkey,\n content: parsed.text ?? '',\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n isSelfWrap: true,\n };\n for (const handler of this.messageHandlers) {\n try { handler(message); } catch (e) { logger.debug('Nostr', 'Self-wrap handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON self-wrap\n }\n logger.debug('Nostr', 'Skipping own non-self-wrap message');\n return;\n }\n\n // Handle read receipts (kind 15)\n if (isReadReceipt(pm)) {\n logger.debug('Nostr', 'Read receipt from:', pm.senderPubkey?.slice(0, 16), 'for:', pm.replyToEventId);\n if (pm.replyToEventId) {\n const receipt: IncomingReadReceipt = {\n senderTransportPubkey: pm.senderPubkey,\n messageEventId: pm.replyToEventId,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.readReceiptHandlers) {\n try { handler(receipt); } catch (e) { logger.debug('Nostr', 'Read receipt handler error:', e); }\n }\n }\n return;\n }\n\n // Handle composing indicators (kind 25050)\n if (pm.kind === COMPOSING_INDICATOR_KIND) {\n let senderNametag: string | undefined;\n let expiresIn = 30000;\n try {\n const parsed = JSON.parse(pm.content);\n senderNametag = parsed.senderNametag || undefined;\n expiresIn = parsed.expiresIn ?? 30000;\n } catch {\n // Payload parse failed — use defaults\n }\n const indicator = {\n senderPubkey: pm.senderPubkey,\n senderNametag,\n expiresIn,\n };\n logger.debug('Nostr', 'Composing indicator from:', indicator.senderNametag || pm.senderPubkey?.slice(0, 16));\n for (const handler of this.composingHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Composing handler error:', e); }\n }\n return;\n }\n\n // Handle typing indicators (JSON content with type: 'typing')\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.type === 'typing') {\n logger.debug('Nostr', 'Typing indicator from:', pm.senderPubkey?.slice(0, 16));\n const indicator: IncomingTypingIndicator = {\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.typingIndicatorHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Typing handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON — continue to chat message handling\n }\n\n if (!isChatMessage(pm)) {\n logger.debug('Nostr', 'Skipping unknown message kind:', pm.kind);\n return;\n }\n\n // Sphere app wraps DM content as JSON: {senderNametag, text}\n let content = pm.content;\n let senderNametag: string | undefined;\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed === 'object' && parsed.text !== undefined) {\n content = parsed.text;\n senderNametag = parsed.senderNametag || undefined;\n }\n } catch {\n // Plain text — use as-is\n }\n\n logger.debug('Nostr', 'DM received from:', senderNametag || pm.senderPubkey?.slice(0, 16), 'content:', content?.slice(0, 50));\n\n const message: IncomingMessage = {\n // Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.\n // This ensures read receipts reference an ID the sender recognizes.\n id: event.id,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag,\n content,\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n };\n\n this.emitEvent({ type: 'message:received', timestamp: Date.now() });\n\n if (this.messageHandlers.size === 0) {\n logger.debug('Nostr', 'No message handlers registered, buffering message for later delivery');\n this.pendingMessages.push(message);\n } else {\n logger.debug('Nostr', 'Dispatching to', this.messageHandlers.size, 'handlers');\n for (const handler of this.messageHandlers) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error:', error);\n }\n }\n }\n } catch (err) {\n // Expected for gift wraps meant for other recipients\n logger.debug('Nostr', 'Gift wrap decrypt failed (expected if not for us):', (err as Error)?.message?.slice(0, 50));\n }\n }\n\n private handleBroadcast(event: NostrEvent): void {\n const tags = event.tags\n .filter((t: string[]) => t[0] === 't')\n .map((t: string[]) => t[1]);\n\n const broadcast: IncomingBroadcast = {\n id: event.id,\n authorTransportPubkey: event.pubkey,\n content: event.content,\n tags,\n timestamp: event.created_at * 1000,\n };\n\n // Find matching handlers\n for (const [key, handlers] of this.broadcastHandlers) {\n const subscribedTags = key.split(':');\n if (tags.some((t) => subscribedTags.includes(t))) {\n for (const handler of handlers) {\n try {\n handler(broadcast);\n } catch (error) {\n logger.debug('Nostr', 'Broadcast handler error:', error);\n }\n }\n }\n }\n }\n\n // ===========================================================================\n // Private: Event Creation & Publishing\n // ===========================================================================\n\n private async createEvent(\n kind: number,\n content: string,\n tags: string[][]\n ): Promise<NostrEvent> {\n if (!this.identity) throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Create and sign event using SDK\n const signedEvent = NostrEventClass.create(this.keyManager, {\n kind,\n content,\n tags,\n });\n\n // Convert to our interface\n const event: NostrEvent = {\n id: signedEvent.id,\n kind: signedEvent.kind,\n content: signedEvent.content,\n tags: signedEvent.tags,\n pubkey: signedEvent.pubkey,\n created_at: signedEvent.created_at,\n sig: signedEvent.sig,\n };\n\n return event;\n }\n\n private async createEncryptedEvent(\n kind: number,\n content: string,\n tags: string[][]\n ): Promise<NostrEvent> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Extract recipient pubkey from tags (first 'p' tag)\n const recipientTag = tags.find((t) => t[0] === 'p');\n if (!recipientTag || !recipientTag[1]) {\n throw new SphereError('No recipient pubkey in tags for encryption', 'VALIDATION_ERROR');\n }\n const recipientPubkey = recipientTag[1];\n\n // Encrypt content with NIP-04 (using hex variant for string keys)\n const encrypted = await NIP04.encryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n recipientPubkey\n );\n\n return this.createEvent(kind, encrypted, tags);\n }\n\n private async publishEvent(event: NostrEvent): Promise<void> {\n if (!this.nostrClient) {\n throw new SphereError('NostrClient not initialized', 'NOT_INITIALIZED');\n }\n\n const MAX_ATTEMPTS = 3;\n const RETRY_BASE_DELAY_MS = 500;\n const RETRY_JITTER_MS = 200;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n // Snapshot this.nostrClient at the top of each iteration. Using a local\n // reference (instead of `this.nostrClient.publishEvent(...)`) prevents a\n // concurrent disconnect() during the subsequent await from turning the\n // call into a TypeError on null.\n const client = this.nostrClient;\n if (!client) {\n throw new SphereError('Transport disconnected during retry', 'TRANSPORT_ERROR');\n }\n\n try {\n // Re-create SDK event each attempt to avoid reusing the same event ID\n // in nostr-js-sdk's pendingOks map (which would leak timers from prior attempts)\n const sdkEvent = NostrEventClass.fromJSON(event);\n await client.publishEvent(sdkEvent);\n return;\n } catch (err: unknown) {\n lastError = err;\n const rawMessage = err instanceof Error ? err.message : String(err);\n\n // Only retry relay-level rejections (nostr-js-sdk wraps these as \"Event rejected: <reason>\")\n // or relay broadcast failures (\"sent 0 of N\"). All other errors (disconnected, closed,\n // no connected relays) are non-transient and should fail immediately.\n // Locale-invariant match with `en-US` to avoid Turkish-locale surprises\n // where `'I'.toLowerCase()` yields `'ı'` (dotless i).\n const lowered = rawMessage.toLocaleLowerCase('en-US');\n const isRelayRejection =\n lowered.startsWith('event rejected:') ||\n lowered.startsWith('sent 0 of');\n\n if (!isRelayRejection || attempt === MAX_ATTEMPTS) {\n break;\n }\n\n // Add jitter to desynchronize retries across concurrent clients\n const delay = RETRY_BASE_DELAY_MS + Math.floor(Math.random() * RETRY_JITTER_MS);\n logger.debug(\n 'Nostr',\n `publishEvent attempt ${attempt}/${MAX_ATTEMPTS} failed (${rawMessage}); retrying in ${delay}ms`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n const reason = lastError instanceof Error ? lastError.message : String(lastError);\n logger.error('Nostr', `publishEvent failed after ${MAX_ATTEMPTS} attempts: ${reason}`);\n // Chain the original error as `cause` to preserve context for debugging\n throw new SphereError(\n `Failed to publish event: ${reason}`,\n 'TRANSPORT_ERROR',\n lastError\n );\n }\n\n /**\n * Publish an event with verification: after publishing, query the relay to\n * confirm the event was stored. Retries up to `maxAttempts` times on failure.\n *\n * This is critical for token transfers and DMs where silent loss means\n * funds or messages disappear. The nostr-js-sdk's publishEvent resolves on\n * a 5s timeout even without relay confirmation, so verification is needed.\n */\n private async publishWithVerification(\n event: NostrEvent,\n maxAttempts: number = 3,\n label: string = 'event',\n ): Promise<void> {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await this.publishEvent(event);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Permanent rejection — don't retry (e.g., \"Event rejected: invalid signature\")\n if (msg.includes('Event rejected') && !msg.includes('rate') && !msg.includes('limit')) {\n throw err;\n }\n if (attempt === maxAttempts) throw err;\n logger.debug('Nostr', `${label} publish attempt ${attempt} failed (${msg}), retrying in ${attempt}s...`);\n await new Promise(r => setTimeout(r, 1000 * attempt));\n continue;\n }\n\n // Verify: query the relay for this specific event by ID.\n // Jittered delay to let relay index + reduce temporal fingerprinting.\n await new Promise(r => setTimeout(r, 300 + Math.random() * 1200));\n try {\n const found = await this.queryEvents({\n ids: [event.id],\n limit: 1,\n });\n if (found.length > 0) {\n if (attempt > 1) {\n logger.debug('Nostr', `${label} verified on relay after ${attempt} attempt(s)`);\n }\n return; // confirmed on relay\n }\n } catch {\n // Query failed — can't verify. If this is the last attempt,\n // accept the publish as best-effort.\n if (attempt === maxAttempts) {\n logger.debug('Nostr', `${label} verification query failed — accepting publish as best-effort`);\n return;\n }\n }\n\n // Event not found on relay — retry with increasing backoff\n if (attempt < maxAttempts) {\n const delay = Math.min(2000 * attempt, 10000);\n logger.debug('Nostr', `${label} not found on relay, retrying in ${delay}ms (attempt ${attempt}/${maxAttempts})...`);\n await new Promise(r => setTimeout(r, delay));\n } else {\n throw new SphereError(\n `${label} not verified on relay after ${maxAttempts} attempts — delivery failed`,\n 'TRANSPORT_ERROR',\n );\n }\n }\n }\n\n async fetchPendingEvents(): Promise<void> {\n if (!this.nostrClient?.isConnected() || !this.keyManager) {\n throw new SphereError('Transport not connected', 'TRANSPORT_ERROR');\n }\n\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EventKinds.GIFT_WRAP, // NIP-17 gift-wrapped DMs\n ];\n walletFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have randomized created_at (±172800 s). Extend the\n // catch-up window by the full randomization range so events sent recently\n // but timestamped far in the past are not missed.\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400 - 172800;\n\n // Capture client reference after the guard to avoid a null-deref race:\n // a concurrent disconnect() can set this.nostrClient = null between the\n // isConnected() check above and the subscribe() call below.\n const client = this.nostrClient;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n // Declared outside the Promise so it's available for cleanup after resolve.\n let subId: string | undefined;\n\n await new Promise<void>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n\n // settle() only resolves the Promise — unsubscribe happens AFTER the Promise\n // resolves (below), where subId is guaranteed to be the real subscription ID.\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n };\n\n try {\n subId = client.subscribe(walletFilter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n if (!settled) {\n timeout = setTimeout(() => settle(), 5000);\n }\n });\n\n // Unsubscribe AFTER the Promise resolves — subId is now the real subscription ID.\n // This also ensures onEvent cannot fire while we iterate events below.\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n\n // Process collected events sequentially (dedup skips already-processed ones)\n for (const event of events) {\n await this.handleEvent(event);\n }\n }\n\n private async queryEvents(filterObj: NostrFilter): Promise<NostrEvent[]> {\n if (!this.nostrClient || !this.nostrClient.isConnected()) {\n throw new SphereError('No connected relays', 'TRANSPORT_ERROR');\n }\n\n // Capture client reference after the guard — same disconnect() race as fetchPendingEvents.\n const client = this.nostrClient;\n\n const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n // Declared outside the Promise so unsubscription can happen after resolve(),\n // where subId is guaranteed to be the real subscription ID (not in TDZ).\n let subId: string | undefined;\n\n return new Promise((resolve) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n if (subId) {\n try { client.unsubscribe(subId); } catch { /* disconnected */ }\n }\n logger.warn('Nostr', `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 15000);\n\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n resolve(events);\n };\n\n try {\n subId = client.subscribe(filter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch {\n clearTimeout(timeout);\n resolve(events);\n return;\n }\n\n });\n }\n\n // ===========================================================================\n // Private: Subscriptions\n // ===========================================================================\n\n // Track subscription IDs for cleanup\n private walletSubscriptionId: string | null = null;\n private chatSubscriptionId: string | null = null;\n\n // Chat EOSE handlers — fired once when relay finishes delivering stored DMs\n private chatEoseHandlers: Array<() => void> = [];\n private chatEoseFired = false;\n\n private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\n if (this._subscriptionsSuppressed) {\n logger.debug('Nostr', 'subscribeToEvents: suppressed — mux handles event routing');\n return;\n }\n if (!this.identity || !this.keyManager || !this.nostrClient) {\n logger.debug('Nostr', 'subscribeToEvents: skipped - no identity, keyManager, or nostrClient');\n return;\n }\n\n // Unsubscribe from previous subscriptions if any\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Use 32-byte Nostr pubkey (x-coordinate only), not 33-byte compressed key\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Subscribing with Nostr pubkey:', nostrPubkey);\n\n // Determine 'since' filter from persisted last event timestamp.\n // - Existing wallet: resume from last processed event (inclusive >=, dedup handles replays)\n // - Fresh wallet / no storage: use current time (no historical replay)\n let since: number;\n if (this.storage) {\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(storageKey);\n if (stored) {\n since = parseInt(stored, 10);\n this.lastEventTs = since; // Seed in-memory tracker from storage\n this.fallbackSince = null; // Stored value takes priority\n logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else if (this.fallbackSince !== null) {\n // No stored timestamp but caller provided a fallback (e.g. address creation time).\n // This ensures events sent while the address was inactive are not missed.\n since = this.fallbackSince;\n this.lastEventTs = since;\n this.fallbackSince = null; // Consume once\n logger.debug('Nostr', 'Using fallback since timestamp:', since);\n } else {\n // No stored timestamp, no fallback = fresh wallet, start from now\n since = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored timestamp, starting from now:', since);\n }\n } catch (err) {\n logger.debug('Nostr', 'Failed to read last event timestamp, falling back to now:', err);\n since = Math.floor(Date.now() / 1000);\n this.fallbackSince = null;\n }\n } else {\n // No storage adapter — fallback to last 24h (legacy behavior)\n since = Math.floor(Date.now() / 1000) - 86400;\n logger.debug('Nostr', 'No storage adapter, using 24h fallback');\n }\n\n // Subscribe to wallet events (legacy kind-4 DMs) with since filter\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n ];\n walletFilter['#p'] = [nostrPubkey];\n walletFilter.since = since;\n\n this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received wallet event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Wallet subscription ready (EOSE)');\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Wallet subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Wallet subscription created, subId:', this.walletSubscriptionId);\n\n // Determine 'since' for DM (gift-wrap) subscription independently from wallet events.\n let dmSince: number;\n if (this.storage) {\n const dmStorageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(dmStorageKey);\n const parsed = stored ? parseInt(stored, 10) : NaN;\n if (Number.isFinite(parsed)) {\n dmSince = parsed;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Stored value takes priority\n logger.debug('Nostr', 'DM resuming from stored timestamp:', dmSince);\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Consume once\n logger.debug('Nostr', 'DM using fallback since timestamp:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored DM timestamp, starting from now:', dmSince);\n }\n } catch (err) {\n if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'Storage read failed, using DM fallback since:', dmSince, err);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'Failed to read last DM event timestamp, falling back to now:', err);\n }\n }\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'No storage adapter for DM, using fallback since:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No storage adapter for DM, starting from now:', dmSince);\n }\n\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have created_at randomized ±2 days for privacy.\n // Without this offset, ~50% of messages are silently dropped by the relay\n // because their randomized timestamp lands before the `since` filter.\n // Math.max(0, ...) prevents negative timestamps when dmSince is small.\n chatFilter.since = Math.max(0, dmSince - TIMESTAMP_RANDOMIZATION);\n\n this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received chat event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Chat subscription ready (EOSE)');\n if (!this.chatEoseFired) {\n this.chatEoseFired = true;\n for (const handler of this.chatEoseHandlers) {\n try { handler(); } catch { /* ignore */ }\n }\n this.chatEoseHandlers = [];\n }\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Chat subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Chat subscription created, subId:', this.chatSubscriptionId);\n }\n\n private subscribeToTags(tags: string[]): void {\n if (!this.nostrClient) return;\n\n const key = tags.sort().join(':');\n const filter = new Filter({\n kinds: [EVENT_KINDS.BROADCAST],\n '#t': tags,\n since: Math.floor(Date.now() / 1000) - 3600, // Last hour\n });\n\n const subId = this.nostrClient.subscribe(filter, {\n onEvent: (event) => {\n this.handleBroadcast({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n });\n\n this.broadcastSubscriptions.set(key, subId);\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private async decryptContent(content: string, senderPubkey: string): Promise<string> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Decrypt content using NIP-04 (using hex variant for string keys)\n const decrypted = await NIP04.decryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n senderPubkey\n );\n\n // Strip known prefixes for compatibility with nostr-js-sdk\n return this.stripContentPrefix(decrypted);\n }\n\n /**\n * Strip known content prefixes (nostr-js-sdk compatibility)\n * Handles: payment_request:, token_transfer:, etc.\n */\n private stripContentPrefix(content: string): string {\n const prefixes = [\n 'payment_request:',\n 'token_transfer:',\n 'payment_response:',\n ];\n\n for (const prefix of prefixes) {\n if (content.startsWith(prefix)) {\n return content.slice(prefix.length);\n }\n }\n\n return content;\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (!this.isConnected()) {\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n }\n\n /**\n * Async version of ensureConnected — reconnects if the original transport\n * lost its WebSocket while subscriptions are suppressed (mux handles events).\n * Used by resolve methods which are always async.\n */\n private async ensureConnectedForResolve(): Promise<void> {\n if (this.isConnected()) return;\n\n // When mux is active, the original transport can lose its idle WebSocket.\n // Reconnect transparently so resolve operations still work.\n if (this._subscriptionsSuppressed && this.nostrClient) {\n logger.debug('Nostr', 'Suppressed transport disconnected — reconnecting for resolve');\n try {\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('reconnect timeout')), 5000)\n ),\n ]);\n if (this.nostrClient.isConnected()) {\n this.status = 'connected';\n return;\n }\n } catch {\n // Fall through to throw\n }\n }\n\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n\n private ensureReady(): void {\n this.ensureConnected();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n }\n\n private emitEvent(event: TransportEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n logger.debug('Nostr', 'Event callback error:', error);\n }\n }\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom inner rumor kind.\n * Replicates the three-layer NIP-59 envelope (rumor → seal → gift wrap)\n * because NIP17.createGiftWrap hardcodes kind 14 for the inner rumor.\n */\n private createCustomKindGiftWrap(recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n return NostrTransportProvider.createCustomKindGiftWrap(this.keyManager!, recipientPubkeyHex, content, rumorKind);\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom rumor kind.\n * Shared between NostrTransportProvider and MultiAddressTransportMux.\n */\n static createCustomKindGiftWrap(keyManager: NostrKeyManager, recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n const senderPubkey = keyManager.getPublicKeyHex();\n const now = Math.floor(Date.now() / 1000);\n\n // 1. Create Rumor (unsigned inner event with custom kind)\n const rumorTags: string[][] = [['p', recipientPubkeyHex]];\n const rumorSerialized = JSON.stringify([0, senderPubkey, now, rumorKind, rumorTags, content]);\n const rumorId = bytesToHex(sha256Noble(new TextEncoder().encode(rumorSerialized)));\n const rumor = { id: rumorId, pubkey: senderPubkey, created_at: now, kind: rumorKind, tags: rumorTags, content };\n\n // 2. Create Seal (kind 13, signed by sender, encrypts rumor)\n const recipientPubkeyBytes = hexToBytes(recipientPubkeyHex);\n const encryptedRumor = NIP44.encrypt(JSON.stringify(rumor), keyManager.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(keyManager, {\n kind: EventKinds.SEAL,\n tags: [],\n content: encryptedRumor,\n created_at: sealTimestamp,\n });\n\n // 3. Create Gift Wrap (kind 1059, signed by ephemeral key, encrypts seal)\n const ephemeralKeys = NostrKeyManager.generate();\n const encryptedSeal = NIP44.encrypt(JSON.stringify(seal.toJSON()), ephemeralKeys.getPrivateKey(), recipientPubkeyBytes);\n const wrapTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const giftWrap = NostrEventClass.create(ephemeralKeys, {\n kind: EventKinds.GIFT_WRAP,\n tags: [['p', recipientPubkeyHex]],\n content: encryptedSeal,\n created_at: wrapTimestamp,\n });\n ephemeralKeys.clear();\n\n return giftWrap;\n }\n\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface NostrEvent {\n id: string;\n kind: number;\n content: string;\n tags: string[][];\n pubkey: string;\n created_at: number;\n sig: string;\n}\n\ninterface NostrFilter {\n ids?: string[];\n authors?: string[];\n kinds?: number[];\n '#p'?: string[];\n '#t'?: string[];\n '#d'?: string[];\n since?: number;\n until?: number;\n limit?: number;\n}\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 §5.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 §6.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n","/**\n * WebSocket Abstraction\n * Platform-independent WebSocket interface for cross-platform support\n */\n\n// =============================================================================\n// WebSocket Interface\n// =============================================================================\n\n/**\n * Minimal WebSocket interface compatible with browser and Node.js\n */\nexport interface IWebSocket {\n readonly readyState: number;\n\n send(data: string): void;\n close(code?: number, reason?: string): void;\n\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: IMessageEvent) => void) | null;\n}\n\nexport interface IMessageEvent {\n data: string;\n}\n\n/**\n * WebSocket ready states (same as native WebSocket)\n */\nexport const WebSocketReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Factory function to create WebSocket instances\n * Different implementations for browser (native) vs Node.js (ws package)\n */\nexport type WebSocketFactory = (url: string) => IWebSocket;\n\n// =============================================================================\n// UUID Generator\n// =============================================================================\n\n/**\n * Generate a unique ID (platform-independent)\n * Browser: crypto.randomUUID()\n * Node: crypto.randomUUID() or uuid package\n */\nexport type UUIDGenerator = () => string;\n\n/**\n * Default UUID generator using crypto.randomUUID\n * Works in modern browsers and Node 19+\n */\nexport function defaultUUIDGenerator(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","/**\n * Browser Transport Exports\n * Re-exports shared transport with browser-specific WebSocket factory\n */\n\nimport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export shared types and classes\nexport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\n\nexport {\n type IWebSocket,\n type IMessageEvent,\n type WebSocketFactory,\n type UUIDGenerator,\n WebSocketReadyState,\n defaultUUIDGenerator,\n} from '../../../transport/websocket';\n\n// =============================================================================\n// Browser WebSocket Factory\n// =============================================================================\n\n/**\n * Browser WebSocket factory using native WebSocket\n * Cast to IWebSocket since native WebSocket is a superset\n */\nexport function createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create NostrTransportProvider with browser WebSocket\n * Convenience factory that injects browser-native WebSocket\n */\nexport function createNostrTransportProvider(\n config?: Omit<NostrTransportProviderConfig, 'createWebSocket'>\n): NostrTransportProvider {\n return new NostrTransportProvider({\n ...config,\n createWebSocket: createBrowserWebSocket,\n });\n}\n","/**\n * Unicity Aggregator Provider\n *\n * Post v1-cutover this is a thin NETWORK-CONFIG provider for the v2 token\n * engine: it loads the root trust base (JSON) via the injected platform loader\n * and exposes the gateway URL + API key. The engine (token-engine/) builds its\n * own SDK clients from these — no state-transition SDK objects live here.\n *\n * TrustBaseLoader is injected for platform-specific loading:\n * - Browser: fetch from URL\n * - Node.js: read from file\n */\n\nimport { logger } from '../core/logger';\nimport type { ProviderStatus } from '../types';\nimport type {\n OracleProvider,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT } from '../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator (gateway) URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base loading (dev only) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n /** Trust base loader (platform-specific) */\n trustBaseLoader?: TrustBaseLoader;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class UnicityAggregatorProvider implements OracleProvider {\n readonly id = 'unicity-aggregator';\n readonly name = 'Unicity Aggregator';\n readonly type = 'network' as const;\n readonly description = 'Unicity gateway network config (trust base + URL + API key) for the v2 token engine';\n\n private config: Required<Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'>> & {\n trustBaseLoader?: TrustBaseLoader;\n };\n private status: ProviderStatus = 'disconnected';\n private eventCallbacks: Set<OracleEventCallback> = new Set();\n\n /** Raw trust-base JSON as loaded (the v2 token engine parses it itself). */\n private trustBaseJson: unknown | null = null;\n\n\n constructor(config: UnicityAggregatorProviderConfig) {\n this.config = {\n url: config.url,\n apiKey: config.apiKey ?? '',\n timeout: config.timeout ?? DEFAULT_AGGREGATOR_TIMEOUT,\n skipVerification: config.skipVerification ?? false,\n debug: config.debug ?? false,\n trustBaseLoader: config.trustBaseLoader,\n };\n }\n\n // ===========================================================================\n // v2 token-engine config surface\n // ===========================================================================\n\n /** Raw trust-base JSON (for constructing the v2 token engine); null when unavailable. */\n getTrustBaseJson(): unknown | null {\n return this.trustBaseJson;\n }\n\n /** The gateway URL this provider is configured against. */\n getAggregatorUrl(): string {\n return this.config.url;\n }\n\n /** The gateway API key (for the v2 token engine); undefined when none is configured. */\n getApiKey(): string | undefined {\n return this.config.apiKey || undefined;\n }\n\n /**\n * Update the API key on this live provider. The v2 token engine snapshots the\n * key at build time, so this alone does NOT change money operations already\n * wired into a running engine — `Sphere.setOracleApiKey()` rebuilds the engine\n * for that. Paths that read `getApiKey()` fresh (e.g. the Unicity-ID minter)\n * pick up the new value immediately.\n */\n setApiKey(apiKey: string): void {\n this.config.apiKey = apiKey ?? '';\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n // Mark as connected — actual connectivity is verified on first operation.\n this.status = 'connected';\n this.emitEvent({ type: 'oracle:connected', timestamp: Date.now() });\n this.log('Connected to oracle:', this.config.url);\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.emitEvent({ type: 'oracle:disconnected', timestamp: Date.now() });\n this.log('Disconnected from oracle');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // OracleProvider Implementation\n // ===========================================================================\n\n async initialize(trustBaseJson?: unknown): Promise<void> {\n if (trustBaseJson) {\n this.trustBaseJson = trustBaseJson;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n try {\n const loaded = await this.config.trustBaseLoader.load();\n if (loaded) {\n this.trustBaseJson = loaded;\n }\n } catch (error) {\n this.log('Failed to load trust base:', error);\n }\n }\n\n await this.connect();\n this.log('Initialized with trust base JSON:', !!this.trustBaseJson);\n }\n\n // ===========================================================================\n // Event Subscription\n // ===========================================================================\n\n onEvent(callback: OracleEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private emitEvent(event: OracleEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n this.log('Event callback error:', error);\n }\n }\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('Aggregator', message, ...args);\n }\n}\n\n// =============================================================================\n// Backward Compatibility Aliases (Oracle -> Aggregator)\n// =============================================================================\n\n/** @deprecated Use UnicityAggregatorProvider instead */\nexport const UnicityOracleProvider = UnicityAggregatorProvider;\n/** @deprecated Use UnicityAggregatorProviderConfig instead */\nexport type UnicityOracleProviderConfig = UnicityAggregatorProviderConfig;\n","/**\n * Embedded trust bases — one per LIVE network. These literals ARE the root of trust:\n * RootTrustBase.fromJSON validates structure only, never a base's own signatures.\n * Pinned to the published files by tests/unit/constants/trustbase-integrity.test.ts.\n */\n\n// Testnet2 (networkId 4) — embedded copy of bft-trustbase.testnet2.json\nexport const TRUSTBASE_TESTNET2 = {\n version: 1,\n networkId: 4,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy',\n sigKey: '0x02b2b1daf4dee562ef5ed5b89e1e0e6cf0bf952ce3af12be6531e40f8bd1a7e682',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M',\n sigKey: '0x02822d7ce59d2ff4f2da4cef361d5a80a3a74c351fe61a8a2c908a54841930cee0',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC',\n sigKey: '0x0352ef027d60921e252839bea60e2f1810473fb0ad8cb89044b101aecdea5f356a',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF',\n sigKey: '0x02ef71c49c77ca9510ad84596522b887e4df0b3262fce80192ddcfb6276efdf901',\n stake: 1,\n },\n ],\n quorumThreshold: 3,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy':\n '0xe9289562b9faf283ae264ce42ef641133237bca6b7f232e1e293305f0cf994d3262cee1ca9c8de93fc5b7da1466486123b6219e5308843b8ead173472dad35e501',\n '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M':\n '0x45e26943305c264d5dc5c3069cd5dbd2d43feb35d56d580656b80b33f4126e2a735b0c8eace0ef638128d9dbf5eb854dd9ad3ba42f9eafded60251bf7edb454501',\n '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC':\n '0xe2263c1cb24b6d933ead249901c7fd83fa884d95b935a63f1489b07ea342281071cbf2117ad27be29cde9207d8d87e8b331630718d92d644faf2fccbc9dba4c000',\n '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF':\n '0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301',\n },\n};\n\n// Mainnet (networkId 1) — embedded copy of bft-trustbase.mainnet.json\n// sha256 of the published file: 346c217b3f0f5debb906781a49c29791f8dcbba63f24615e5a78fcd9b79b43f8\nexport const TRUSTBASE_MAINNET = {\n version: 1,\n networkId: 1,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma',\n sigKey: '0x03059e30b5c980ce92e2c6d4e14775838be91a6f0e993f3a4899ab459c4b5225f2',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr',\n sigKey: '0x03a514db8b17b1c79b4a6affeb5d88a129c4d5ff0f92c8f86c311971b0beb94335',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG',\n sigKey: '0x02e5669b2daa5d1afccfe66603f7d3fe171ecbd7ed891487ab3683cdebc3c3cd8b',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4',\n sigKey: '0x03ffee964da8ca276d4fc1cf391929e6759db106ef1d0aea998154b51e5a7fb1b7',\n stake: 1,\n },\n ],\n quorumThreshold: 3,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma':\n '0xc2f1d67dbb47fe4dcf502301d8cd5021b8903c0d3385781653d09c703d566a3a3a4e59c75792b36f929e32564ddc14c40ef728c0bde5859774d08d802de2318c00',\n '16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr':\n '0x3e3ad3a7540daa0eded457e31d2ebcacfb354036798f49f63433d26c44857e76157e4abd36cbb1d39de8cdf6e1d33f88b250077d4347c3e5bf0f35a64f2d815300',\n '16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG':\n '0x6101bd9df8ab87439d90b52cd74727bc8bf31dd74173c8f731ec3ea501d3d86556ba7c0e84643337959f3be0e448637c301e8e0b28196e780d4f2ff930d9062100',\n '16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4':\n '0xda13c777b832425e5cbd8d6e49eb08ddbde35c1e4a9bee8bd495c3883f039e434b64560865c3bc52b68d65780932b1f2bf8971f245d2244face822af5984945c00',\n },\n};\n","/**\n * Shared TrustBase Loader Logic\n * Common embedded trustbase data and base loader\n */\n\nimport { TRUSTBASE_TESTNET2, TRUSTBASE_MAINNET } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\nimport { SphereError } from '../../core/errors';\n\nexport interface TrustBaseLoader {\n load(): Promise<unknown | null>;\n}\n\n/**\n * Get embedded trustbase data by network\n */\nexport function getEmbeddedTrustBase(network: NetworkType): unknown | null {\n switch (network) {\n case 'mainnet':\n return TRUSTBASE_MAINNET;\n // v1 cutover: 'testnet' is now an alias of testnet2 (NETWORKS.testnet points\n // at the testnet2 gateway), so it MUST resolve the testnet2 trust base —\n // a mismatched trust base would make the engine reject every proof.\n case 'testnet':\n case 'testnet2':\n return TRUSTBASE_TESTNET2;\n default:\n // Fail loud: an unknown network must not silently resolve to the testnet trust base.\n throw new SphereError(`getEmbeddedTrustBase: unknown network \"${network}\"`, 'INVALID_CONFIG');\n }\n}\n\n/**\n * Base TrustBase loader with embedded fallback\n */\nexport abstract class BaseTrustBaseLoader implements TrustBaseLoader {\n protected network: NetworkType;\n\n constructor(network: NetworkType = 'testnet') {\n this.network = network;\n }\n\n /**\n * Try to load from external source (file, URL, etc.)\n * Override in subclass\n */\n protected abstract loadFromExternal(): Promise<unknown | null>;\n\n async load(): Promise<unknown | null> {\n // Try external source first\n const external = await this.loadFromExternal();\n if (external) {\n return external;\n }\n\n // Fallback to embedded data\n return getEmbeddedTrustBase(this.network);\n }\n}\n","/**\n * Browser Oracle Exports\n * Re-exports shared oracle with browser-specific TrustBaseLoader\n */\n\nimport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\nimport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\nimport { BaseTrustBaseLoader } from '../../shared/trustbase-loader';\nimport type { NetworkType } from '../../../constants';\nimport { SphereError } from '../../../core/errors';\n\n// Re-export shared types and classes\nexport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n UnicityOracleProvider,\n type UnicityOracleProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\n\nexport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\n\n// =============================================================================\n// Browser TrustBase Loader\n// =============================================================================\n\n/**\n * Browser TrustBase loader - fetches from URL or uses embedded data\n */\nexport class BrowserTrustBaseLoader extends BaseTrustBaseLoader {\n private url?: string;\n\n /**\n * @param networkOrUrl - a NetworkType, or a URL to fetch the trust base from\n * @param fallbackNetwork - network used for the embedded fallback when a URL is\n * supplied (and the fetch fails). Required when networkOrUrl is a URL — we must\n * never silently fall back to testnet on a different network.\n */\n constructor(networkOrUrl?: NetworkType | string, fallbackNetwork?: NetworkType) {\n if (!networkOrUrl) {\n throw new SphereError('BrowserTrustBaseLoader: network or trustBaseUrl is required.', 'INVALID_CONFIG');\n }\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n if (!fallbackNetwork) {\n throw new SphereError(\n 'BrowserTrustBaseLoader: fallbackNetwork is required when a trustBaseUrl is supplied.',\n 'INVALID_CONFIG',\n );\n }\n super(fallbackNetwork);\n this.url = networkOrUrl;\n } else {\n super(networkOrUrl as NetworkType);\n }\n }\n\n protected async loadFromExternal(): Promise<unknown | null> {\n if (!this.url) return null;\n\n try {\n const response = await fetch(this.url);\n if (response.ok) {\n return await response.json();\n }\n } catch {\n // Fall through to embedded\n }\n return null;\n }\n}\n\n/**\n * Create browser TrustBase loader\n */\nexport function createBrowserTrustBaseLoader(\n networkOrUrl?: NetworkType | string,\n fallbackNetwork?: NetworkType,\n): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl, fallbackNetwork);\n}\n\n// =============================================================================\n// Browser Factory\n// =============================================================================\n\n/**\n * Create UnicityAggregatorProvider with browser TrustBase loader\n */\nexport function createUnicityAggregatorProvider(\n config: Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'> & {\n trustBaseUrl?: string;\n network?: NetworkType;\n }\n): UnicityAggregatorProvider {\n const { trustBaseUrl, network, ...restConfig } = config;\n // Fail loud: without a network we cannot pick the right embedded trust base.\n if (!trustBaseUrl && !network) {\n throw new SphereError(\n 'createUnicityAggregatorProvider: network or trustBaseUrl required.',\n 'INVALID_CONFIG',\n );\n }\n return new UnicityAggregatorProvider({\n ...restConfig,\n // When a URL is supplied, `network` (if any) is the embedded-fallback network.\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network, network),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\n","// The wallet-api composition preset: a plain CONFIG (WalletApiTransportConfig)\n// that Sphere.init consumes directly and fails closed without.\n\nimport type { StorageProvider } from '../../../storage';\nimport type { TransportProvider } from '../../../transport';\nimport type { OracleProvider } from '../../../oracle';\nimport type { WalletApiTransportConfig } from '../../../core/payments-v2-wiring';\n\nexport type { WalletApiTransportConfig } from '../../../core/payments-v2-wiring';\n\n/** The minimum any platform base bundle provides (browser/node factories do). */\nexport interface SphereBaseProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n}\n\nexport interface SphereProviderPorts {\n /** Engine/aggregator config port override */\n engine?: OracleProvider;\n}\n\n/** Compose a bundle with the engine port selected independently. */\nexport function createSphereProviders<B extends SphereBaseProviders>(\n base: B,\n ports: SphereProviderPorts = {}\n): B {\n return {\n ...base,\n ...(ports.engine ? { oracle: ports.engine } : {}),\n };\n}\n\nexport interface WalletApiCompositionConfig {\n baseUrl: string;\n network: string;\n deviceId?: string;\n fetchFn?: unknown;\n webSocketFactory?: unknown;\n paymentsV2Transport?: WalletApiTransportConfig['paymentsV2Transport'];\n}\n\n/** Attach the transport config the payments vertical is composed from; pass the result to `Sphere.init`. */\nexport function createWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & { walletApi: WalletApiTransportConfig } {\n const walletApi: WalletApiTransportConfig = {\n network: config.network,\n baseUrl: config.baseUrl,\n ...(config.deviceId !== undefined ? { deviceId: config.deviceId } : {}),\n ...(config.fetchFn !== undefined ? { fetchFn: config.fetchFn } : {}),\n ...(config.webSocketFactory !== undefined ? { webSocketFactory: config.webSocketFactory } : {}),\n ...(config.paymentsV2Transport !== undefined ? { paymentsV2Transport: config.paymentsV2Transport } : {}),\n };\n return { ...base, walletApi };\n}\n","/**\n * Browser Download Utilities\n * Functions for downloading wallet backups as files\n */\n\nimport type { Sphere } from '../../core/Sphere';\nimport type { WalletJSON, WalletJSONExportOptions } from '../../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DownloadTextOptions {\n /** Password for encryption */\n password?: string;\n /** Number of addresses to include */\n addressCount?: number;\n /** Custom filename (without extension) */\n filename?: string;\n}\n\nexport interface DownloadJSONOptions extends WalletJSONExportOptions {\n /** Custom filename (without extension) */\n filename?: string;\n /** Pretty print JSON (default: true) */\n pretty?: boolean;\n}\n\n// =============================================================================\n// Core Download Function\n// =============================================================================\n\n/**\n * Download content as a file in the browser\n */\nexport function downloadFile(\n content: string | Blob,\n filename: string,\n mimeType: string = 'text/plain'\n): void {\n const blob = content instanceof Blob\n ? content\n : new Blob([content], { type: mimeType });\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n\n link.href = url;\n link.download = filename;\n link.click();\n\n // Clean up the URL after a short delay\n setTimeout(() => URL.revokeObjectURL(url), 100);\n}\n\n/**\n * Download text content as a .txt file\n */\nexport function downloadTextFile(content: string, filename: string): void {\n downloadFile(content, filename, 'text/plain');\n}\n\n/**\n * Download JSON content as a .json file\n */\nexport function downloadJSONFile(content: object | string, filename: string): void {\n const jsonString = typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2);\n downloadFile(jsonString, filename, 'application/json');\n}\n\n// =============================================================================\n// Wallet Download Functions\n// =============================================================================\n\n/**\n * Download wallet backup as text file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletText(sphere);\n *\n * // Download encrypted backup\n * downloadWalletText(sphere, { password: 'secret' });\n *\n * // Custom filename\n * downloadWalletText(sphere, { filename: 'my-backup' });\n * ```\n */\nexport function downloadWalletText(sphere: Sphere, options: DownloadTextOptions = {}): void {\n const content = sphere.exportToTxt({\n password: options.password,\n addressCount: options.addressCount,\n });\n\n const filename = options.filename\n ? `${options.filename}.txt`\n : `sphere-wallet-${Date.now()}.txt`;\n\n downloadTextFile(content, filename);\n}\n\n/**\n * Download wallet backup as JSON file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletJSON(sphere);\n *\n * // Download encrypted backup\n * downloadWalletJSON(sphere, { password: 'secret' });\n *\n * // Include multiple addresses\n * downloadWalletJSON(sphere, { addressCount: 5 });\n * ```\n */\nexport function downloadWalletJSON(sphere: Sphere, options: DownloadJSONOptions = {}): void {\n const json = sphere.exportToJSON({\n password: options.password,\n addressCount: options.addressCount,\n includeMnemonic: options.includeMnemonic,\n });\n\n const filename = options.filename\n ? `${options.filename}.json`\n : `sphere-wallet-${Date.now()}.json`;\n\n const jsonString = options.pretty !== false\n ? JSON.stringify(json, null, 2)\n : JSON.stringify(json);\n\n downloadFile(jsonString, filename, 'application/json');\n}\n\n/**\n * Download pre-built WalletJSON as file\n */\nexport function downloadWalletJSONData(json: WalletJSON, filename?: string): void {\n const name = filename || `sphere-wallet-${Date.now()}.json`;\n downloadJSONFile(json, name.endsWith('.json') ? name : `${name}.json`);\n}\n\n// =============================================================================\n// File Reading Utilities\n// =============================================================================\n\n/**\n * Read a file as text\n */\nexport function readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\n/**\n * Read a file as ArrayBuffer (for binary files like .dat)\n */\nexport function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(file);\n });\n}\n\n/**\n * Read a file as Uint8Array (for binary files like .dat)\n */\nexport async function readFileAsUint8Array(file: File): Promise<Uint8Array> {\n const buffer = await readFileAsArrayBuffer(file);\n return new Uint8Array(buffer);\n}\n","/**\n * Centralized network-config resolution + a startup consistency assertion.\n *\n * Principle: resolve a network's full configuration in ONE place, and refuse to\n * run a network whose configuration is provably broken (e.g. a null or mismatched\n * trust base would silently accept unverified tokens — a funds risk).\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport { getEmbeddedTrustBase } from './trustbase-loader';\nimport { SphereError } from '../../core/errors';\n\n/**\n * Known trust-base networkId per network. The embedded trust base for a network\n * MUST carry this id; a mismatch means the wrong trust base is wired to a network\n * (e.g. the testnet trust base under testnet2), which would verify tokens against\n * the wrong chain. Typed as a COMPLETE Record, so adding a network without pinning its id is\n * a compile error rather than a silently unchecked network — that gap is what made mainnet\n * dangerous to enable: with no row, embedding a trust base removed the only gate.\n */\nconst EXPECTED_NETWORK_ID: Record<NetworkType, number> = {\n mainnet: 1,\n // v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),\n // so both expect the testnet2 trust base (networkId 4).\n testnet: 4,\n testnet2: 4,\n};\n\nexport interface ResolvedNetworkConfig {\n readonly network: NetworkType;\n readonly config: NetworkConfig;\n readonly trustBase: unknown | null;\n readonly networkId: number | undefined;\n}\n\n/** Resolve a network's full config + embedded trust base in one place. */\nexport function resolveNetworkConfig(network: NetworkType): ResolvedNetworkConfig {\n const config = NETWORKS[network];\n const trustBase = getEmbeddedTrustBase(network);\n const networkId = (trustBase as { networkId?: number } | null)?.networkId;\n return { network, config, trustBase, networkId };\n}\n\n/**\n * Fail loud if a network is unsafe to run. Conservative on purpose: it rejects\n * only provably-broken configs: a null trust base (tokens would go unverified) or one\n * whose networkId contradicts EXPECTED_NETWORK_ID (the wrong chain's trust base wired\n * to this network). Both checks now apply to EVERY network — there is no unpinned\n * network left to skip. The stricter hygiene checks (e.g. no cross-network registry\n * reuse) live in the per-network CI test, not at runtime, so they can't break a valid\n * deployment.\n */\nexport function assertNetworkConsistency(network: NetworkType): void {\n const { trustBase, networkId } = resolveNetworkConfig(network);\n if (trustBase == null) {\n throw new SphereError(\n `Network \"${network}\" has no embedded trust base — refusing to run (tokens would be unverified).`,\n 'INVALID_CONFIG',\n );\n }\n const expected = EXPECTED_NETWORK_ID[network];\n if (networkId !== expected) {\n throw new SphereError(\n `Network \"${network}\" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,\n 'INVALID_CONFIG',\n );\n }\n}\n","/**\n * Abort / timeout signal helpers.\n *\n * Kept in a dedicated, dependency-free module (no node:crypto fallback like\n * core/utils.ts) so importing it never pulls a Node builtin into the\n * browser-platform bundles (impl/browser/*, impl/shared/wallet-api).\n */\n\n/**\n * Create an {@link AbortSignal} that aborts after `ms` milliseconds.\n *\n * Prefers the native `AbortSignal.timeout` (Chrome 103+, Firefox 100+,\n * Safari 16+ / iOS 16+). Falls back to an `AbortController` + `setTimeout` on\n * runtimes that lack it — older mobile Safari (iOS 15), older Android WebViews,\n * and in-app/embedded browsers — which would otherwise throw\n * `AbortSignal.timeout is not a function` the moment a timed fetch or engine\n * op runs (sphere-sdk#617: Swap / Top Up crash). The native fast-path is kept\n * for modern runtimes; the fallback aborts with a `TimeoutError` to match\n * native semantics.\n */\nexport function timeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n return AbortSignal.timeout(ms);\n }\n const controller = new AbortController();\n setTimeout(() => {\n controller.abort(\n typeof DOMException !== 'undefined'\n ? new DOMException('The operation timed out.', 'TimeoutError')\n : new Error('The operation timed out.'),\n );\n }, ms);\n return controller.signal;\n}\n","/**\n * CoinGecko Price Provider\n *\n * Fetches token prices from CoinGecko API with internal caching.\n * Supports both free and pro API tiers.\n * Optionally persists cache to StorageProvider for survival across page reloads.\n */\n\nimport { logger } from '../core/logger';\nimport { SphereError } from '../core/errors';\nimport { timeoutSignal } from '../core/timeout';\nimport { STORAGE_KEYS_GLOBAL } from '../constants';\nimport type { StorageProvider } from '../storage';\nimport type { PriceProvider, PricePlatform, TokenPrice, PriceProviderConfig } from './price-provider';\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface CacheEntry {\n price: TokenPrice;\n expiresAt: number;\n}\n\n/** Serializable format for persistent storage */\ninterface PersistedPriceCache {\n [tokenName: string]: TokenPrice;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * CoinGecko price provider\n *\n * @example\n * ```ts\n * // Free tier (no API key)\n * const provider = new CoinGeckoPriceProvider();\n *\n * // Pro tier\n * const provider = new CoinGeckoPriceProvider({ apiKey: 'CG-xxx' });\n *\n * // With persistent cache (survives page reloads)\n * const provider = new CoinGeckoPriceProvider({ storage: myStorageProvider });\n *\n * const prices = await provider.getPrices(['bitcoin', 'ethereum']);\n * console.log(prices.get('bitcoin')?.priceUsd);\n * ```\n */\nexport class CoinGeckoPriceProvider implements PriceProvider {\n readonly platform: PricePlatform = 'coingecko';\n\n private readonly cache: Map<string, CacheEntry> = new Map();\n private readonly apiKey?: string;\n private readonly cacheTtlMs: number;\n private readonly timeout: number;\n private readonly debug: boolean;\n private readonly baseUrl: string;\n private readonly storage: StorageProvider | null;\n\n /** In-flight fetch promise for deduplication of concurrent getPrices() calls */\n private fetchPromise: Promise<Map<string, TokenPrice>> | null = null;\n /** Token names being fetched in the current in-flight request */\n private fetchNames: Set<string> | null = null;\n /** Whether persistent cache has been loaded into memory */\n private persistentCacheLoaded = false;\n /** Promise for loading persistent cache (deduplication) */\n private loadCachePromise: Promise<void> | null = null;\n\n constructor(config?: Omit<PriceProviderConfig, 'platform'>) {\n this.apiKey = config?.apiKey;\n this.cacheTtlMs = config?.cacheTtlMs ?? 60_000;\n this.timeout = config?.timeout ?? 10_000;\n this.debug = config?.debug ?? false;\n this.storage = config?.storage ?? null;\n\n this.baseUrl = config?.baseUrl\n ?? (this.apiKey\n ? 'https://pro-api.coingecko.com/api/v3'\n : 'https://api.coingecko.com/api/v3');\n }\n\n async getPrices(tokenNames: string[]): Promise<Map<string, TokenPrice>> {\n if (tokenNames.length === 0) {\n return new Map();\n }\n\n // Load persistent cache on first call (once)\n if (!this.persistentCacheLoaded && this.storage) {\n await this.loadFromStorage();\n }\n\n const now = Date.now();\n const result = new Map<string, TokenPrice>();\n const uncachedNames: string[] = [];\n\n // Check cache first\n for (const name of tokenNames) {\n const cached = this.cache.get(name);\n if (cached && cached.expiresAt > now) {\n result.set(name, cached.price);\n } else {\n uncachedNames.push(name);\n }\n }\n\n // All cached — return immediately\n if (uncachedNames.length === 0) {\n return result;\n }\n\n // Deduplicate concurrent calls: if an in-flight fetch covers all needed tokens, reuse it\n if (this.fetchPromise && this.fetchNames) {\n const allCovered = uncachedNames.every((n) => this.fetchNames!.has(n));\n if (allCovered) {\n if (this.debug) {\n logger.debug('CoinGecko', 'Deduplicating request, reusing in-flight fetch');\n }\n const fetched = await this.fetchPromise;\n for (const name of uncachedNames) {\n const price = fetched.get(name);\n if (price) {\n result.set(name, price);\n }\n }\n return result;\n }\n }\n\n // Fetch uncached prices\n const fetchPromise = this.doFetch(uncachedNames);\n this.fetchPromise = fetchPromise;\n this.fetchNames = new Set(uncachedNames);\n\n try {\n const fetched = await fetchPromise;\n for (const [name, price] of fetched) {\n result.set(name, price);\n }\n } finally {\n // Clear in-flight state only if this is still the current request\n if (this.fetchPromise === fetchPromise) {\n this.fetchPromise = null;\n this.fetchNames = null;\n }\n }\n\n return result;\n }\n\n private async doFetch(uncachedNames: string[]): Promise<Map<string, TokenPrice>> {\n const result = new Map<string, TokenPrice>();\n const now = Date.now();\n\n try {\n const ids = uncachedNames.join(',');\n const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;\n\n const headers: Record<string, string> = { Accept: 'application/json' };\n if (this.apiKey) {\n headers['x-cg-pro-api-key'] = this.apiKey;\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Fetching prices for: ${uncachedNames.join(', ')}`);\n }\n\n const response = await fetch(url, {\n headers,\n signal: timeoutSignal(this.timeout),\n });\n\n if (!response.ok) {\n // On rate-limit, extend existing cache entries to avoid hammering the API\n if (response.status === 429) {\n this.extendCacheOnRateLimit(uncachedNames);\n }\n throw new SphereError(`CoinGecko API error: ${response.status} ${response.statusText}`, 'NETWORK_ERROR');\n }\n\n const data = await response.json() as Record<string, Record<string, number>>;\n\n // Parse and cache response\n for (const [name, values] of Object.entries(data)) {\n if (values && typeof values === 'object') {\n const price: TokenPrice = {\n tokenName: name,\n priceUsd: values.usd ?? 0,\n priceEur: values.eur,\n change24h: values.usd_24h_change,\n timestamp: now,\n };\n this.cache.set(name, { price, expiresAt: now + this.cacheTtlMs });\n result.set(name, price);\n }\n }\n\n // Tokens not found on CoinGecko: cache with zero prices to avoid re-requesting\n // and to persist in storage (so page reloads don't trigger unnecessary API calls)\n for (const name of uncachedNames) {\n if (!result.has(name)) {\n const zeroPrice: TokenPrice = {\n tokenName: name,\n priceUsd: 0,\n priceEur: 0,\n change24h: 0,\n timestamp: now,\n };\n this.cache.set(name, { price: zeroPrice, expiresAt: now + this.cacheTtlMs });\n result.set(name, zeroPrice);\n }\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Fetched ${result.size} prices`);\n }\n\n // Persist to storage (fire-and-forget)\n this.saveToStorage();\n } catch (error) {\n if (this.debug) {\n logger.warn('CoinGecko', 'Fetch failed, using stale cache:', error);\n }\n\n // On error, return stale cached data if available\n for (const name of uncachedNames) {\n const stale = this.cache.get(name);\n if (stale) {\n result.set(name, stale.price);\n }\n }\n }\n\n return result;\n }\n\n // ===========================================================================\n // Persistent Storage\n // ===========================================================================\n\n /**\n * Load cached prices from StorageProvider into in-memory cache.\n * Only loads entries that are still within cacheTtlMs.\n */\n private async loadFromStorage(): Promise<void> {\n // Deduplicate concurrent loads\n if (this.loadCachePromise) {\n return this.loadCachePromise;\n }\n this.loadCachePromise = this.doLoadFromStorage();\n try {\n await this.loadCachePromise;\n } finally {\n this.loadCachePromise = null;\n }\n }\n\n private async doLoadFromStorage(): Promise<void> {\n this.persistentCacheLoaded = true;\n if (!this.storage) return;\n\n try {\n const [cached, cachedTs] = await Promise.all([\n this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE),\n this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS),\n ]);\n\n if (!cached || !cachedTs) return;\n\n const ts = parseInt(cachedTs, 10);\n if (isNaN(ts)) return;\n\n // Only use if within TTL\n const age = Date.now() - ts;\n if (age > this.cacheTtlMs) return;\n\n const data: PersistedPriceCache = JSON.parse(cached);\n const expiresAt = ts + this.cacheTtlMs;\n\n for (const [name, price] of Object.entries(data)) {\n // Only populate if not already in memory (in-memory is always fresher)\n if (!this.cache.has(name)) {\n this.cache.set(name, { price, expiresAt });\n }\n }\n\n if (this.debug) {\n logger.debug('CoinGecko', `Loaded ${Object.keys(data).length} prices from persistent cache`);\n }\n } catch {\n // Cache load failure is non-critical\n }\n }\n\n /**\n * Save current prices to StorageProvider (fire-and-forget).\n */\n private saveToStorage(): void {\n if (!this.storage) return;\n\n const data: PersistedPriceCache = {};\n for (const [name, entry] of this.cache) {\n data[name] = entry.price;\n }\n\n // Fire-and-forget\n Promise.all([\n this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),\n this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now())),\n ]).catch((err) => logger.debug('Price', 'Cache save failed (non-critical)', err));\n }\n\n // ===========================================================================\n // Rate-limit handling\n // ===========================================================================\n\n /**\n * On 429 rate-limit, extend stale cache entries so subsequent calls\n * don't immediately retry and hammer the API.\n */\n private extendCacheOnRateLimit(names: string[]): void {\n const backoffMs = 60_000; // 1 minute backoff on rate-limit\n const extendedExpiry = Date.now() + backoffMs;\n\n for (const name of names) {\n const existing = this.cache.get(name);\n if (existing) {\n existing.expiresAt = Math.max(existing.expiresAt, extendedExpiry);\n }\n }\n\n if (this.debug) {\n logger.warn('CoinGecko', `Rate-limited (429), extended cache TTL by ${backoffMs / 1000}s`);\n }\n }\n\n async getPrice(tokenName: string): Promise<TokenPrice | null> {\n const prices = await this.getPrices([tokenName]);\n return prices.get(tokenName) ?? null;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n","/**\n * Price Provider\n *\n * Token market price abstraction with CoinGecko implementation.\n */\n\nexport type {\n PriceProvider,\n PricePlatform,\n TokenPrice,\n PriceProviderConfig,\n} from './price-provider';\n\nexport { CoinGeckoPriceProvider } from './CoinGeckoPriceProvider';\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nimport type { PriceProviderConfig, PriceProvider } from './price-provider';\nimport { CoinGeckoPriceProvider } from './CoinGeckoPriceProvider';\nimport { SphereError } from '../core/errors';\n\n/**\n * Create a price provider based on platform configuration\n *\n * @example\n * ```ts\n * const provider = createPriceProvider({ platform: 'coingecko', apiKey: 'CG-xxx' });\n * ```\n */\nexport function createPriceProvider(config: PriceProviderConfig): PriceProvider {\n switch (config.platform) {\n case 'coingecko':\n return new CoinGeckoPriceProvider(config);\n default:\n throw new SphereError(`Unsupported price platform: ${String(config.platform)}`, 'INVALID_CONFIG');\n }\n}\n","/**\n * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n ResolvedTransportConfig,\n ResolvedOracleConfig,\n} from './config';\nimport type { PriceProviderConfig } from '../../price';\nimport type { StorageProvider } from '../../storage';\nimport type { GroupChatModuleConfig } from '../../modules/groupchat';\nimport type { MarketModuleConfig } from '../../modules/market';\n\n// =============================================================================\n// Network Resolution\n// =============================================================================\n\n/**\n * Get network configuration by type\n */\nexport function getNetworkConfig(network: NetworkType = 'mainnet'): NetworkConfig {\n return NETWORKS[network];\n}\n\n// =============================================================================\n// Transport Resolution\n// =============================================================================\n\n/**\n * Resolve transport configuration with extend/override pattern\n *\n * Priority:\n * 1. `relays` - replaces defaults entirely\n * 2. `additionalRelays` - extends network defaults\n * 3. Network defaults\n *\n * @example\n * ```ts\n * // Use network defaults\n * resolveTransportConfig('testnet', undefined);\n *\n * // Replace relays entirely\n * resolveTransportConfig('testnet', { relays: ['wss://custom.relay'] });\n *\n * // Extend defaults\n * resolveTransportConfig('testnet', { additionalRelays: ['wss://extra.relay'] });\n * ```\n */\nexport function resolveTransportConfig(\n network: NetworkType,\n config?: BaseTransportConfig & { reconnectDelay?: number; maxReconnectAttempts?: number }\n): ResolvedTransportConfig {\n const networkConfig = getNetworkConfig(network);\n\n // Resolve relays with extend/override pattern\n let relays: string[];\n if (config?.relays) {\n // Explicit relays - replace entirely\n relays = config.relays;\n } else {\n // Start with network defaults\n relays = [...networkConfig.nostrRelays] as string[];\n // Add additional relays if specified\n if (config?.additionalRelays) {\n relays = [...relays, ...config.additionalRelays];\n }\n }\n\n return {\n relays,\n timeout: config?.timeout,\n autoReconnect: config?.autoReconnect,\n debug: config?.debug,\n // Browser-specific\n reconnectDelay: config?.reconnectDelay,\n maxReconnectAttempts: config?.maxReconnectAttempts,\n };\n}\n\n// =============================================================================\n// Oracle Resolution\n// =============================================================================\n\n/**\n * Resolve oracle configuration with override pattern\n *\n * Uses network default URL if not explicitly provided\n *\n * @example\n * ```ts\n * // Use network default\n * resolveOracleConfig('testnet', undefined);\n *\n * // Override URL\n * resolveOracleConfig('testnet', { url: 'https://custom.aggregator' });\n * ```\n */\nexport function resolveOracleConfig(\n network: NetworkType,\n config?: BaseOracleConfig & { trustBasePath?: string }\n): ResolvedOracleConfig {\n const networkConfig = getNetworkConfig(network);\n\n return {\n url: config?.url ?? networkConfig.aggregatorUrl,\n apiKey: config?.apiKey,\n timeout: config?.timeout,\n skipVerification: config?.skipVerification,\n debug: config?.debug,\n // Node.js-specific\n trustBasePath: config?.trustBasePath,\n };\n}\n\n// =============================================================================\n// Price Resolution\n// =============================================================================\n\n/**\n * Resolve price provider configuration\n *\n * Returns undefined if no price config is provided (price is optional).\n *\n * @example\n * ```ts\n * // No price config\n * resolvePriceConfig(undefined); // undefined\n *\n * // Minimal config (defaults to coingecko)\n * resolvePriceConfig({}); // { platform: 'coingecko' }\n *\n * // With API key\n * resolvePriceConfig({ apiKey: 'CG-xxx' }); // { platform: 'coingecko', apiKey: 'CG-xxx' }\n * ```\n */\nexport function resolvePriceConfig(\n config?: BasePriceConfig,\n storage?: StorageProvider,\n): PriceProviderConfig | undefined {\n if (config === undefined) {\n return undefined;\n }\n\n return {\n platform: config.platform ?? 'coingecko',\n apiKey: config.apiKey,\n baseUrl: config.baseUrl,\n cacheTtlMs: config.cacheTtlMs,\n timeout: config.timeout,\n debug: config.debug,\n storage,\n };\n}\n\n// =============================================================================\n// Array Extension Helper\n// =============================================================================\n\n/**\n * Resolve array with extend/override pattern\n *\n * @param defaults - Default values from network config\n * @param replace - Values that replace defaults entirely\n * @param additional - Values to add to defaults\n * @returns Resolved array\n *\n * @example\n * ```ts\n * // Use defaults\n * resolveArrayConfig(['a', 'b'], undefined, undefined); // ['a', 'b']\n *\n * // Replace\n * resolveArrayConfig(['a', 'b'], ['x'], undefined); // ['x']\n *\n * // Extend\n * resolveArrayConfig(['a', 'b'], undefined, ['c']); // ['a', 'b', 'c']\n * ```\n */\nexport function resolveArrayConfig<T>(\n defaults: readonly T[],\n replace?: T[],\n additional?: T[]\n): T[] {\n if (replace) {\n return replace;\n }\n\n const result = [...defaults];\n if (additional) {\n return [...result, ...additional];\n }\n\n return result;\n}\n\n// =============================================================================\n// Group Chat Resolution\n// =============================================================================\n\n/**\n * Resolve group chat configuration for provider factories.\n * @param network - Network type for default relay URLs\n * @param config - User-provided group chat config (true, object, or undefined)\n * @returns Resolved GroupChatModuleConfig or undefined if disabled\n */\nexport function resolveGroupChatConfig(\n network: NetworkType,\n config?: { enabled?: boolean; relays?: string[] } | boolean,\n): GroupChatModuleConfig | undefined {\n if (!config) return undefined;\n\n if (config === true) {\n const netConfig = getNetworkConfig(network);\n return { relays: [...netConfig.groupRelays] };\n }\n\n if (typeof config === 'object' && config.enabled === false) {\n return undefined;\n }\n\n const netConfig = getNetworkConfig(network);\n return {\n relays: config.relays ?? [...netConfig.groupRelays],\n };\n}\n\n// =============================================================================\n// Market Resolution\n// =============================================================================\n\n/**\n * Resolve market module configuration.\n * @param config - User-provided market config (true, object, or undefined)\n * @returns Resolved MarketModuleConfig or undefined if disabled\n */\nexport function resolveMarketConfig(\n config?: BaseMarketConfig | boolean,\n): MarketModuleConfig | undefined {\n if (!config) return undefined;\n if (config === true) return {};\n return { apiUrl: config.apiUrl, timeout: config.timeout };\n}\n"],"mappings":"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","names":["Buffer","success","message","senderNametag","netConfig","Buffer"]}
|