@unicitylabs/sphere-sdk 0.5.8 → 0.6.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/dist/core/index.cjs +11 -9
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +0 -10
- package/dist/core/index.d.ts +0 -10
- package/dist/core/index.js +11 -9
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +191 -497
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +191 -497
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +203 -509
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +10 -20
- package/dist/impl/nodejs/index.d.ts +10 -20
- package/dist/impl/nodejs/index.js +203 -509
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +22 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -11
- package/dist/index.d.ts +1 -11
- package/dist/index.js +21 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -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","../../../impl/browser/storage/IndexedDBTokenStorageProvider.ts","../../../transport/NostrTransportProvider.ts","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/hmac.ts","../../../node_modules/@noble/hashes/src/hkdf.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha2.ts","../../../core/crypto.ts","../../../core/bech32.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/browser/download.ts","../../../impl/shared/ipfs/ipfs-error-types.ts","../../../impl/shared/ipfs/ipfs-state-persistence.ts","../../../impl/shared/ipfs/ipns-key-derivation.ts","../../../impl/shared/ipfs/ipns-record-manager.ts","../../../impl/shared/ipfs/ipfs-cache.ts","../../../impl/shared/ipfs/ipfs-http-client.ts","../../../impl/shared/ipfs/txf-merge.ts","../../../impl/shared/ipfs/ipns-subscription-client.ts","../../../impl/shared/ipfs/write-behind-buffer.ts","../../../impl/shared/ipfs/ipfs-storage-provider.ts","../../../impl/browser/ipfs/browser-ipfs-state-persistence.ts","../../../impl/browser/ipfs/index.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';\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n L1Config,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig, createIndexedDBTokenStorageProvider } from './storage';\nimport { createNostrTransportProvider } from './transport';\nimport { createUnicityAggregatorProvider } from './oracle';\nimport { createBrowserIpfsStorageProvider } from './ipfs';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } 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 L1Config,\n type BrowserTransportExtensions,\n resolveTransportConfig,\n resolveOracleConfig,\n resolveL1Config,\n resolvePriceConfig,\n resolveArrayConfig,\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 * IPFS sync backend configuration\n */\nexport interface IpfsSyncConfig {\n /** Enable IPFS sync (default: false) */\n enabled?: boolean;\n /** Replace default gateways entirely */\n gateways?: string[];\n /** Add gateways to network defaults */\n additionalGateways?: string[];\n /** Replace default bootstrap peers */\n bootstrapPeers?: string[];\n /** Add bootstrap peers to defaults */\n additionalBootstrapPeers?: string[];\n /** Use browser DHT (Helia) vs HTTP-only mode */\n useDht?: boolean;\n}\n\n/**\n * File sync backend configuration (future)\n */\nexport interface FileSyncConfig {\n /** Enable file sync (default: false) */\n enabled?: boolean;\n /** Directory path for token files */\n directory?: string;\n /** File format: 'json' | 'txf' */\n format?: 'json' | 'txf';\n}\n\n/**\n * Cloud sync backend configuration (future)\n */\nexport interface CloudSyncConfig {\n /** Enable cloud sync (default: false) */\n enabled?: boolean;\n /** Cloud provider */\n provider?: 'aws' | 'gcp' | 'azure' | 'custom';\n /** Bucket/container name */\n bucket?: string;\n /** API endpoint (for custom provider) */\n endpoint?: string;\n /** API key or credentials */\n apiKey?: string;\n}\n\n/**\n * MongoDB sync backend configuration\n */\nexport interface MongoDbSyncConfig {\n /** Enable MongoDB sync (default: false) */\n enabled?: boolean;\n /** MongoDB connection URI */\n uri?: string;\n /** Database name */\n database?: string;\n /** Collection name (default: 'tokens') */\n collection?: string;\n /** Enable authentication */\n authEnabled?: boolean;\n /** Username (if authEnabled) */\n username?: string;\n /** Password (if authEnabled) */\n password?: string;\n}\n\n/**\n * Token sync configuration - supports multiple backends\n */\nexport interface TokenSyncConfig {\n /** IPFS sync backend */\n ipfs?: IpfsSyncConfig;\n /** File sync backend (future) */\n file?: FileSyncConfig;\n /** Cloud sync backend (future) */\n cloud?: CloudSyncConfig;\n /** MongoDB sync backend */\n mongodb?: MongoDbSyncConfig;\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 /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /**\n * Token sync backends configuration\n * Supports multiple backends: IPFS, file, cloud (future)\n * Each backend can be enabled/disabled independently\n */\n tokenSync?: TokenSyncConfig;\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 /** Token storage provider for local persistence (IndexedDB) */\n tokenStorage: TokenStorageProvider<TxfStorageDataBase>;\n /** L1 configuration (for passing to Sphere.init) */\n l1?: L1Config;\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n /** IPFS token storage provider (when tokenSync.ipfs.enabled is true) */\n ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\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 * Token sync configuration (resolved from tokenSync options)\n * For advanced use cases when additional sync backends are needed\n * @deprecated Use tokenStorage provider instead. For custom sync backends,\n * use Sphere.addTokenStorageProvider() after initialization.\n */\n tokenSyncConfig?: {\n ipfs?: {\n enabled: boolean;\n gateways: string[];\n bootstrapPeers?: string[];\n useDht?: boolean;\n };\n file?: {\n enabled: boolean;\n directory?: string;\n format?: 'json' | 'txf';\n };\n cloud?: {\n enabled: boolean;\n provider?: string;\n bucket?: string;\n endpoint?: string;\n apiKey?: string;\n };\n mongodb?: {\n enabled: boolean;\n uri?: string;\n database?: string;\n collection?: string;\n };\n };\n}\n\n// =============================================================================\n// Token Sync Resolution\n// =============================================================================\n\n/**\n * Resolve IPFS sync configuration with extend/override pattern\n */\nfunction resolveIpfsSyncConfig(\n network: NetworkType,\n config?: IpfsSyncConfig\n): NonNullable<BrowserProviders['tokenSyncConfig']>['ipfs'] | undefined {\n if (!config) return undefined;\n\n const networkConfig = getNetworkConfig(network);\n const gateways = resolveArrayConfig(\n networkConfig.ipfsGateways,\n config.gateways,\n config.additionalGateways\n );\n\n return {\n enabled: config.enabled ?? false,\n gateways,\n bootstrapPeers: config.bootstrapPeers ?? config.additionalBootstrapPeers,\n useDht: config.useDht,\n };\n}\n\n/**\n * Resolve all token sync backends\n */\nfunction resolveTokenSyncConfig(\n network: NetworkType,\n config?: TokenSyncConfig\n): BrowserProviders['tokenSyncConfig'] {\n if (!config) return undefined;\n\n const result: BrowserProviders['tokenSyncConfig'] = {};\n\n // IPFS backend\n const ipfs = resolveIpfsSyncConfig(network, config.ipfs);\n if (ipfs) result.ipfs = ipfs;\n\n // File backend\n if (config.file) {\n result.file = {\n enabled: config.file.enabled ?? false,\n directory: config.file.directory,\n format: config.file.format,\n };\n }\n\n // Cloud backend\n if (config.cloud) {\n result.cloud = {\n enabled: config.cloud.enabled ?? false,\n provider: config.cloud.provider,\n bucket: config.cloud.bucket,\n endpoint: config.cloud.endpoint,\n apiKey: config.cloud.apiKey,\n };\n }\n\n // MongoDB backend\n if (config.mongodb) {\n result.mongodb = {\n enabled: config.mongodb.enabled ?? false,\n uri: config.mongodb.uri,\n database: config.mongodb.database,\n collection: config.mongodb.collection,\n };\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\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 (tokenStorage is automatically included)\n * const { sphere } = await Sphere.init({\n * ...providers,\n * autoGenerate: true,\n * });\n *\n * // Add additional sync backends dynamically after init\n * // await sphere.addTokenStorageProvider(myMongoDbProvider);\n * ```\n */\nexport function createBrowserProviders(config?: BrowserProvidersConfig): BrowserProviders {\n const network = config?.network ?? 'mainnet';\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 const l1Config = resolveL1Config(network, config?.l1);\n const tokenSyncConfig = resolveTokenSyncConfig(network, config?.tokenSync);\n\n const storage = createIndexedDBStorageProvider(config?.storage);\n const priceConfig = resolvePriceConfig(config?.price, storage);\n\n // Create IPFS storage provider if enabled\n const ipfsConfig = tokenSyncConfig?.ipfs;\n const ipfsTokenStorage = ipfsConfig?.enabled\n ? createBrowserIpfsStorageProvider({\n gateways: ipfsConfig.gateways,\n debug: config?.tokenSync?.ipfs?.useDht, // reuse debug-like flag\n })\n : undefined;\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 tokenStorage: createIndexedDBTokenStorageProvider(),\n l1: l1Config,\n price: priceConfig ? createPriceProvider(priceConfig) : undefined,\n ipfsTokenStorage,\n tokenSyncConfig,\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 | 'STORAGE_ERROR'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\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 * 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 /** 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 (tokens, tombstones, archived, forked) is stored via\n * TokenStorageProvider, not here. This avoids duplication.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Pending transfers for this address */\n PENDING_TRANSFERS: 'pending_transfers',\n /** Transfer outbox for this address */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Transaction history for this address */\n TRANSACTION_HISTORY: 'transaction_history',\n /** Pending V5 finalization tokens (unconfirmed instant split tokens) */\n PENDING_V5_TOKENS: 'pending_v5_tokens',\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 /** Processed V5 split group IDs for Nostr re-delivery dedup */\n PROCESSED_SPLIT_GROUP_IDS: 'processed_split_group_ids',\n /** Processed V6 combined transfer IDs for Nostr re-delivery dedup */\n PROCESSED_COMBINED_TRANSFER_IDS: 'processed_combined_transfer_ids',\n} as const;\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\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 /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\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/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n/** Default API key for aggregator authentication */\nexport const DEFAULT_AGGREGATOR_API_KEY = 'sk_06365a9c44654841a366068bcfc68986' as const;\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://unicity-ipfs1.dyndns.org',\n] as const;\n\n/** Unicity IPFS bootstrap peers */\nexport const DEFAULT_IPFS_BOOTSTRAP_PEERS = [\n '/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh',\n '/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6',\n '/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1',\n '/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv',\n] as const;\n\n/** Unicity dedicated IPFS nodes (HTTP API access) */\nexport const UNICITY_IPFS_NODES = [\n {\n host: 'unicity-ipfs1.dyndns.org',\n peerId: '12D3KooWDKJqEMAhH4nsSSiKtK1VLcas5coUqSPZAfbWbZpxtL4u',\n httpPort: 9080,\n httpsPort: 443,\n },\n] as const;\n\n/**\n * Get IPFS gateway URLs for HTTP API access.\n * @param isSecure - Use HTTPS (default: true). Set false for development.\n */\nexport function getIpfsGatewayUrls(isSecure?: boolean): string[] {\n return UNICITY_IPFS_NODES.map((node) =>\n isSecure !== false\n ? `https://${node.host}`\n : `http://${node.host}:${node.httpPort}`,\n );\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 /** ALPHA token (L1 blockchain) */\n ALPHA: 'ALPHA',\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// L1 (ALPHA Blockchain) Defaults\n// =============================================================================\n\n/** Default Fulcrum electrum server for mainnet */\nexport const DEFAULT_ELECTRUM_URL = 'wss://fulcrum.unicity.network:50004' as const;\n\n/** Testnet Fulcrum electrum server */\nexport const TEST_ELECTRUM_URL = 'wss://fulcrum.unicity.network:50004' 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/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: DEFAULT_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n testnet: {\n name: 'Testnet',\n aggregatorUrl: TEST_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const;\n\nexport type NetworkType = keyof typeof NETWORKS;\nexport type NetworkConfig = (typeof NETWORKS)[NetworkType];\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, getAddressId } 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 /** 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 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 }\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.l1Address);\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\n if (isPerAddressKey && this.identity?.directAddress) {\n // Add address ID prefix for per-address data\n const addressId = getAddressId(this.identity.directAddress);\n return `${this.config.prefix}${addressId}_${key}`;\n }\n\n // Global key - no address prefix\n return `${this.config.prefix}${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, getAddressId } 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 /** 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 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.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.l1Address);\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 // 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\n if (isPerAddressKey && this.identity?.directAddress) {\n // Add address ID prefix for per-address data\n const addressId = getAddressId(this.identity.directAddress);\n return `${this.prefix}${addressId}_${key}`;\n }\n\n // Global key - no address prefix\n return `${this.prefix}${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 * IndexedDB Token Storage Provider for Browser\n * Stores tokens in IndexedDB for persistent browser storage\n * Each address gets its own database for multi-address support\n */\n\nimport { logger } from '../../../core/logger';\nimport type { TokenStorageProvider, TxfStorageDataBase, SyncResult, SaveResult, LoadResult, HistoryRecord } from '../../../storage';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport { getAddressId } from '../../../constants';\n\n// Re-export HistoryRecord for backwards compat\nexport type { HistoryRecord } from '../../../storage';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-token-storage';\nconst DB_VERSION = 2;\nconst STORE_TOKENS = 'tokens';\nconst STORE_META = 'meta';\nconst STORE_HISTORY = 'history';\n\nexport interface IndexedDBTokenStorageConfig {\n /** Database name prefix (default: 'sphere-token-storage') */\n dbNamePrefix?: string;\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 IndexedDBTokenStorageProvider implements TokenStorageProvider<TxfStorageDataBase> {\n readonly id = 'indexeddb-token-storage';\n readonly name = 'IndexedDB Token Storage';\n readonly type = 'local' as const;\n\n private dbNamePrefix: string;\n private dbName: string;\n private debug: boolean;\n private db: IDBDatabase | null = null;\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBTokenStorageConfig) {\n this.dbNamePrefix = config?.dbNamePrefix ?? DB_NAME;\n this.dbName = this.dbNamePrefix;\n this.debug = config?.debug ?? false;\n }\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n // Scope database to address using consistent addressId format\n if (identity.directAddress) {\n const addressId = getAddressId(identity.directAddress);\n this.dbName = `${this.dbNamePrefix}-${addressId}`;\n }\n logger.debug('IndexedDBToken', `setIdentity: db=${this.dbName}`);\n }\n\n async initialize(): Promise<boolean> {\n const prevConnId = this.connId;\n const t0 = Date.now();\n try {\n // Close any existing connection before opening a new one\n // (e.g. when switching addresses — prevents leaked IDB connections)\n if (this.db) {\n logger.debug('IndexedDBToken', `initialize: closing existing connId=${prevConnId} before re-open (db=${this.dbName})`);\n this.db.close();\n this.db = null;\n }\n\n logger.debug('IndexedDBToken', `initialize: opening db=${this.dbName}`);\n this.db = await this.openDatabase();\n this.status = 'connected';\n logger.debug('IndexedDBToken', `initialize: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return true;\n } catch (error) {\n logger.error('IndexedDBToken', `initialize: failed db=${this.dbName} (${Date.now() - t0}ms):`, error);\n this.status = 'error';\n return false;\n }\n }\n\n async shutdown(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDBToken', `shutdown: 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 async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n await this.shutdown();\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 async load(): Promise<LoadResult<TxfStorageDataBase>> {\n if (!this.db) {\n logger.warn('IndexedDBToken', `load: db not initialized (db=${this.dbName})`);\n return {\n success: false,\n error: 'Database not initialized',\n source: 'local',\n timestamp: Date.now(),\n };\n }\n\n try {\n const data: TxfStorageDataBase = {\n _meta: {\n version: 1,\n address: this.identity?.l1Address ?? '',\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n };\n\n // Load meta\n const meta = await this.getFromStore<TxfStorageDataBase['_meta']>(STORE_META, 'meta');\n if (meta) {\n data._meta = meta;\n }\n\n // Load all tokens from store\n const tokens = await this.getAllFromStore<{ id: string; data: unknown }>(STORE_TOKENS);\n for (const token of tokens) {\n // Skip file-format entries (token-, nametag-) - they are loaded via loadTokensFromFileStorage\n if (token.id.startsWith('token-') || token.id.startsWith('nametag-')) {\n continue;\n }\n\n if (token.id.startsWith('archived-')) {\n // Archived tokens: keep as-is (archived-tokenId key)\n data[token.id as keyof TxfStorageDataBase] = token.data;\n } else {\n // Other entries: add _ prefix for TXF format\n const key = `_${token.id}` as `_${string}`;\n data[key] = token.data;\n }\n }\n\n // Load tombstones\n const tombstones = await this.getFromStore<TxfStorageDataBase['_tombstones']>(STORE_META, 'tombstones');\n if (tombstones) {\n data._tombstones = tombstones;\n }\n\n // Load outbox\n const outbox = await this.getFromStore<TxfStorageDataBase['_outbox']>(STORE_META, 'outbox');\n if (outbox) {\n data._outbox = outbox;\n }\n\n // Load sent\n const sent = await this.getFromStore<TxfStorageDataBase['_sent']>(STORE_META, 'sent');\n if (sent) {\n data._sent = sent;\n }\n\n // Load invalid\n const invalid = await this.getFromStore<TxfStorageDataBase['_invalid']>(STORE_META, 'invalid');\n if (invalid) {\n data._invalid = invalid;\n }\n\n const tokenKeys = Object.keys(data).filter(k => k.startsWith('_') && !['_meta', '_tombstones', '_outbox', '_sent', '_invalid'].includes(k));\n logger.debug('IndexedDBToken', `load: db=${this.dbName}, tokens=${tokenKeys.length}`);\n\n return {\n success: true,\n data,\n source: 'local',\n timestamp: Date.now(),\n };\n } catch (error) {\n logger.error('IndexedDBToken', `load failed: db=${this.dbName}`, error);\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n source: 'local',\n timestamp: Date.now(),\n };\n }\n }\n\n async save(data: TxfStorageDataBase): Promise<SaveResult> {\n if (!this.db) {\n logger.warn('IndexedDBToken', `save: db not initialized (db=${this.dbName})`);\n return {\n success: false,\n error: 'Database not initialized',\n timestamp: Date.now(),\n };\n }\n\n try {\n const tokenKeys = Object.keys(data).filter(k => k.startsWith('_') && !['_meta', '_tombstones', '_outbox', '_sent', '_invalid'].includes(k));\n const archivedKeys = Object.keys(data).filter(k => k.startsWith('archived-'));\n logger.debug('IndexedDBToken', `save: db=${this.dbName}, tokens=${tokenKeys.length}, archived=${archivedKeys.length}, tombstones=${data._tombstones?.length ?? 0}`);\n\n // Save meta\n await this.putToStore(STORE_META, 'meta', data._meta);\n\n // Save special arrays\n if (data._tombstones) {\n await this.putToStore(STORE_META, 'tombstones', data._tombstones);\n }\n if (data._outbox) {\n await this.putToStore(STORE_META, 'outbox', data._outbox);\n }\n if (data._sent) {\n await this.putToStore(STORE_META, 'sent', data._sent);\n }\n if (data._invalid) {\n await this.putToStore(STORE_META, 'invalid', data._invalid);\n }\n\n // Save each token (active tokens start with _, archived with archived-)\n const reservedKeys = ['_meta', '_tombstones', '_outbox', '_sent', '_invalid'];\n for (const [key, value] of Object.entries(data)) {\n if (reservedKeys.includes(key)) continue;\n\n if (key.startsWith('_')) {\n // Active token: _tokenId -> tokenId\n const tokenId = key.slice(1);\n await this.putToStore(STORE_TOKENS, tokenId, { id: tokenId, data: value });\n } else if (key.startsWith('archived-')) {\n // Archived token: archived-tokenId -> archived-tokenId (keep prefix)\n await this.putToStore(STORE_TOKENS, key, { id: key, data: value });\n }\n }\n\n // Handle tombstones - delete tokens\n if (data._tombstones) {\n for (const tombstone of data._tombstones) {\n await this.deleteFromStore(STORE_TOKENS, tombstone.tokenId);\n }\n }\n\n return {\n success: true,\n timestamp: Date.now(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n timestamp: Date.now(),\n };\n }\n }\n\n async sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>> {\n // For local IndexedDB storage, just save and return\n const saveResult = await this.save(localData);\n return {\n success: saveResult.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: saveResult.error,\n };\n }\n\n async exists(): Promise<boolean> {\n if (!this.db) return false;\n const meta = await this.getFromStore(STORE_META, 'meta');\n return meta !== null;\n }\n\n async clear(): Promise<boolean> {\n // Uses IDBObjectStore.clear() instead of deleteDatabase().\n // deleteDatabase() is a schema operation that gets blocked by leaked IDB\n // connections (React StrictMode, multiple tabs) and leaves a pending delete\n // that blocks ALL subsequent open() calls, bricking the wallet.\n // store.clear() is a normal readwrite transaction — cannot be blocked.\n const t0 = Date.now();\n try {\n // 1. Close own connection\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n\n // 2. Collect all databases with our prefix (current + other addresses)\n const dbNames = new Set<string>([this.dbName]);\n for (const name of await this.findPrefixedDatabases()) {\n dbNames.add(name);\n }\n\n // 3. Clear stores in each database in parallel\n logger.debug('IndexedDBToken', `clear: clearing ${dbNames.size} database(s) (${[...dbNames].join(', ')})`);\n const results = await Promise.allSettled(\n [...dbNames].map((name) => this.clearDatabaseStores(name)),\n );\n\n const failed = results.filter((r) => r.status === 'rejected');\n if (failed.length > 0) {\n logger.warn('IndexedDBToken', `clear: ${failed.length}/${dbNames.size} failed (${Date.now() - t0}ms)`,\n failed.map((r) => (r as PromiseRejectedResult).reason));\n }\n\n logger.debug('IndexedDBToken', `clear: done ${dbNames.size} database(s) (${Date.now() - t0}ms)`);\n return failed.length === 0;\n } catch (err) {\n logger.warn('IndexedDBToken', `clear: failed (${Date.now() - t0}ms)`, err);\n return false;\n }\n }\n\n // =========================================================================\n // Private IndexedDB helpers\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('IndexedDBToken', `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 holds the database.\n // Log it — onsuccess will follow once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDBToken', `open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create tokens store\n if (!db.objectStoreNames.contains(STORE_TOKENS)) {\n db.createObjectStore(STORE_TOKENS, { keyPath: 'id' });\n }\n\n // Create meta store\n if (!db.objectStoreNames.contains(STORE_META)) {\n db.createObjectStore(STORE_META);\n }\n\n // Create history store (v2)\n if (!db.objectStoreNames.contains(STORE_HISTORY)) {\n db.createObjectStore(STORE_HISTORY, { keyPath: 'dedupKey' });\n }\n };\n });\n }\n\n private getFromStore<T>(storeName: string, key: string): Promise<T | null> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve(null);\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readonly');\n const store = transaction.objectStore(storeName);\n const request = store.get(key);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? null);\n });\n }\n\n private getAllFromStore<T>(storeName: string): Promise<T[]> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve([]);\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readonly');\n const store = transaction.objectStore(storeName);\n const request = store.getAll();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private putToStore(storeName: string, key: string, value: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n\n // Meta store: no keyPath, use explicit key\n // Tokens store: keyPath 'id'\n // History store: keyPath 'dedupKey'\n const request = storeName === STORE_META\n ? store.put(value, key)\n : store.put(value);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private deleteFromStore(storeName: string, key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve();\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n const request = store.delete(key);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n /**\n * Find all IndexedDB databases with our prefix.\n * Returns empty array if indexedDB.databases() is unavailable (older browsers).\n */\n private async findPrefixedDatabases(): Promise<string[]> {\n if (typeof indexedDB.databases !== 'function') return [];\n try {\n const allDbs = await Promise.race([\n indexedDB.databases(),\n new Promise<IDBDatabaseInfo[]>((_, reject) =>\n setTimeout(() => reject(new Error('databases() timed out')), 1500),\n ),\n ]);\n return allDbs\n .map((info) => info.name)\n .filter((name): name is string => !!name && name.startsWith(this.dbNamePrefix));\n } catch {\n return [];\n }\n }\n\n // =========================================================================\n // Public: History operations\n // =========================================================================\n\n /**\n * Add a history entry. Uses `put` (upsert by dedupKey) so duplicate\n * calls with the same dedupKey simply overwrite — no duplicates.\n */\n async addHistoryEntry(entry: HistoryRecord): Promise<void> {\n await this.putToStore(STORE_HISTORY, entry.dedupKey, entry);\n }\n\n /**\n * Get all history entries sorted by timestamp descending.\n */\n async getHistoryEntries(): Promise<HistoryRecord[]> {\n const entries = await this.getAllFromStore<HistoryRecord>(STORE_HISTORY);\n return entries.sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Check if a history entry with the given dedupKey exists.\n */\n async hasHistoryEntry(dedupKey: string): Promise<boolean> {\n const entry = await this.getFromStore<HistoryRecord>(STORE_HISTORY, dedupKey);\n return entry !== null;\n }\n\n /**\n * Clear all history entries.\n */\n async clearHistory(): Promise<void> {\n if (!this.db) return;\n await new Promise<void>((resolve, reject) => {\n const tx = this.db!.transaction(STORE_HISTORY, 'readwrite');\n const req = tx.objectStore(STORE_HISTORY).clear();\n req.onerror = () => reject(req.error);\n req.onsuccess = () => resolve();\n });\n }\n\n /**\n * Bulk import history entries. Entries with existing dedupKeys are\n * skipped (first-write-wins). Returns the number of newly imported entries.\n */\n async importHistoryEntries(entries: HistoryRecord[]): Promise<number> {\n if (!this.db || entries.length === 0) return 0;\n let imported = 0;\n for (const entry of entries) {\n const exists = await this.hasHistoryEntry(entry.dedupKey);\n if (!exists) {\n await this.addHistoryEntry(entry);\n imported++;\n }\n }\n return imported;\n }\n\n // =========================================================================\n // Private IndexedDB helpers (clear)\n // =========================================================================\n\n /**\n * Clear all object stores in a single database.\n * Opens a temporary connection, clears STORE_TOKENS and STORE_META, then closes.\n * Uses IDBObjectStore.clear() which is a normal readwrite transaction — cannot\n * be blocked by other connections (unlike deleteDatabase()).\n */\n private async clearDatabaseStores(dbName: string): Promise<void> {\n const db = await Promise.race([\n new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(dbName, DB_VERSION);\n req.onerror = () => reject(req.error);\n req.onsuccess = () => {\n const db = req.result;\n db.onversionchange = () => { db.close(); };\n resolve(db);\n };\n req.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_TOKENS)) {\n db.createObjectStore(STORE_TOKENS, { keyPath: 'id' });\n }\n if (!db.objectStoreNames.contains(STORE_META)) {\n db.createObjectStore(STORE_META);\n }\n if (!db.objectStoreNames.contains(STORE_HISTORY)) {\n db.createObjectStore(STORE_HISTORY, { keyPath: 'dedupKey' });\n }\n };\n }),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`open timed out: ${dbName}`)), 3000),\n ),\n ]);\n\n try {\n for (const storeName of [STORE_TOKENS, STORE_META, STORE_HISTORY]) {\n if (db.objectStoreNames.contains(storeName)) {\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readwrite');\n const req = tx.objectStore(storeName).clear();\n req.onerror = () => reject(req.error);\n req.onsuccess = () => resolve();\n });\n }\n }\n } finally {\n db.close();\n }\n }\n}\n\nexport function createIndexedDBTokenStorageProvider(\n config?: IndexedDBTokenStorageConfig\n): IndexedDBTokenStorageProvider {\n return new IndexedDBTokenStorageProvider(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 { hkdf } from '@noble/hashes/hkdf.js';\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 hashNametag,\n NostrClient,\n Filter,\n isChatMessage,\n isReadReceipt,\n} from '@unicitylabs/nostr-js-sdk';\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';\nimport { getPublicKey, publicKeyToAddress } from '../core/crypto';\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 TokenTransferHandler,\n BroadcastHandler,\n PaymentRequestHandler,\n PaymentRequestResponseHandler,\n IncomingMessage,\n IncomingTokenTransfer,\n IncomingBroadcast,\n IncomingPaymentRequest,\n IncomingPaymentRequestResponse,\n TokenTransferPayload,\n PaymentRequestPayload,\n PaymentRequestResponsePayload,\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// Address Hashing Utility\n// =============================================================================\n\n/**\n * Hash an address (DIRECT:// or PROXY://) for use as indexed 't' tag value.\n * Enables reverse lookup: address → binding event → transport pubkey.\n * @param address - Address string (e.g., DIRECT://... or PROXY://...)\n * @returns Hex-encoded SHA-256 hash\n */\nfunction hashAddressForTag(address: string): string {\n const bytes = new TextEncoder().encode('unicity:address:' + address);\n return Buffer.from(sha256Noble(bytes)).toString('hex');\n}\n\n// =============================================================================\n// Nametag Encryption Utilities\n// =============================================================================\n\n/**\n * Derive encryption key from private key using HKDF\n * @param privateKeyHex - 32-byte private key as hex\n * @returns 32-byte derived key as Uint8Array\n */\nfunction deriveNametagEncryptionKey(privateKeyHex: string): Uint8Array {\n const privateKeyBytes = Buffer.from(privateKeyHex, 'hex');\n // Use HKDF with SHA-256, salt derived from constant, info = \"nametag-encryption\"\n const saltInput = new TextEncoder().encode('sphere-nametag-salt');\n const salt = sha256Noble(saltInput);\n const info = new TextEncoder().encode('nametag-encryption');\n return hkdf(sha256Noble, privateKeyBytes, salt, info, 32);\n}\n\n/**\n * Encrypt nametag with AES-GCM using derived key\n * @param nametag - Plain text nametag\n * @param privateKeyHex - Private key for key derivation\n * @returns Base64 encoded encrypted data (iv + ciphertext + tag)\n */\nasync function encryptNametag(nametag: string, privateKeyHex: string): Promise<string> {\n const key = deriveNametagEncryptionKey(privateKeyHex);\n const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit IV for AES-GCM\n const encoder = new TextEncoder();\n const data = encoder.encode(nametag);\n\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n new Uint8Array(key).buffer as ArrayBuffer,\n { name: 'AES-GCM' },\n false,\n ['encrypt']\n );\n\n const encrypted = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv: new Uint8Array(iv).buffer as ArrayBuffer },\n cryptoKey,\n new Uint8Array(data).buffer as ArrayBuffer\n );\n\n // Combine IV + ciphertext (includes auth tag)\n const combined = new Uint8Array(iv.length + encrypted.byteLength);\n combined.set(iv, 0);\n combined.set(new Uint8Array(encrypted), iv.length);\n\n return Buffer.from(combined).toString('base64');\n}\n\n/**\n * Decrypt nametag with AES-GCM using derived key\n * @param encryptedBase64 - Base64 encoded encrypted data (iv + ciphertext + tag)\n * @param privateKeyHex - Private key for key derivation\n * @returns Decrypted nametag or null if decryption fails\n */\nasync function decryptNametag(encryptedBase64: string, privateKeyHex: string): Promise<string | null> {\n try {\n const key = deriveNametagEncryptionKey(privateKeyHex);\n const combined = Buffer.from(encryptedBase64, 'base64');\n\n const iv = combined.slice(0, 12);\n const ciphertext = combined.slice(12);\n\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n new Uint8Array(key).buffer as ArrayBuffer,\n { name: 'AES-GCM' },\n false,\n ['decrypt']\n );\n\n const decrypted = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: new Uint8Array(iv).buffer as ArrayBuffer },\n cryptoKey,\n new Uint8Array(ciphertext).buffer as ArrayBuffer\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decrypted);\n } catch {\n return null;\n }\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 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 transferHandlers: Set<TokenTransferHandler> = new Set();\n private paymentRequestHandlers: Set<PaymentRequestHandler> = new Set();\n private paymentRequestResponseHandlers: Set<PaymentRequestResponseHandler> = 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 // 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 });\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 },\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.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 // 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 });\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 /**\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.publishEvent(giftWrap);\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 async sendTokenTransfer(\n recipientPubkey: string,\n payload: TokenTransferPayload\n ): Promise<string> {\n this.ensureReady();\n\n // Create encrypted token transfer event\n // Content must have \"token_transfer:\" prefix for nostr-js-sdk compatibility\n const content = 'token_transfer:' + JSON.stringify(payload);\n\n // IMPORTANT: kind 31113 is a Parameterized Replaceable Event (NIP-01).\n // The relay keeps only the LATEST event per (pubkey, kind, d-tag).\n // A static d-tag like 'token-transfer' caused subsequent sends to OVERWRITE\n // previous ones on the relay — the recipient only saw the last token sent.\n // Fix: use a unique d-tag per event so each transfer is its own slot.\n const uniqueD = `token-transfer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.TOKEN_TRANSFER,\n content,\n [\n ['p', recipientPubkey],\n ['d', uniqueD],\n ['type', 'token_transfer'],\n ]\n );\n\n await this.publishEvent(event);\n\n this.emitEvent({\n type: 'transfer:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return event.id;\n }\n\n onTokenTransfer(handler: TokenTransferHandler): () => void {\n this.transferHandlers.add(handler);\n return () => this.transferHandlers.delete(handler);\n }\n\n async sendPaymentRequest(\n recipientPubkey: string,\n payload: PaymentRequestPayload\n ): Promise<string> {\n this.ensureReady();\n\n const requestId = this.config.generateUUID();\n const amount = typeof payload.amount === 'bigint' ? payload.amount.toString() : payload.amount;\n\n // Build request content matching nostr-js-sdk format\n const requestContent = {\n requestId,\n amount,\n coinId: payload.coinId,\n message: payload.message,\n recipientNametag: payload.recipientNametag,\n deadline: Date.now() + 5 * 60 * 1000, // 5 minutes default\n };\n\n // Content must have \"payment_request:\" prefix for nostr-js-sdk compatibility\n const content = 'payment_request:' + JSON.stringify(requestContent);\n\n // Build tags matching nostr-js-sdk format\n const tags: string[][] = [\n ['p', recipientPubkey],\n ['type', 'payment_request'],\n ['amount', amount],\n ];\n if (payload.recipientNametag) {\n tags.push(['recipient', payload.recipientNametag]);\n }\n\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.PAYMENT_REQUEST,\n content,\n tags\n );\n\n await this.publishEvent(event);\n\n logger.debug('Nostr', 'Sent payment request:', event.id);\n\n return event.id;\n }\n\n onPaymentRequest(handler: PaymentRequestHandler): () => void {\n this.paymentRequestHandlers.add(handler);\n return () => this.paymentRequestHandlers.delete(handler);\n }\n\n async sendPaymentRequestResponse(\n recipientPubkey: string,\n payload: PaymentRequestResponsePayload\n ): Promise<string> {\n this.ensureReady();\n\n // Build response content\n const responseContent = {\n requestId: payload.requestId,\n responseType: payload.responseType,\n message: payload.message,\n transferId: payload.transferId,\n };\n\n // Create encrypted payment request response event\n // Content must have \"payment_response:\" prefix for nostr-js-sdk compatibility\n const content = 'payment_response:' + JSON.stringify(responseContent);\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n content,\n [\n ['p', recipientPubkey],\n ['e', payload.requestId], // Reference to original request\n ['d', 'payment-request-response'],\n ['type', 'payment_response'],\n ]\n );\n\n await this.publishEvent(event);\n\n logger.debug('Nostr', 'Sent payment request response:', event.id, 'type:', payload.responseType);\n\n return event.id;\n }\n\n onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void {\n this.paymentRequestResponseHandlers.add(handler);\n return () => this.paymentRequestResponseHandlers.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 // ===========================================================================\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:// or PROXY:// address\n if (identifier.startsWith('DIRECT:') || identifier.startsWith('PROXY:')) {\n return this.resolveAddressInfo(identifier);\n }\n\n // L1 address (alpha1... or alphat1...)\n if (identifier.startsWith('alpha1') || identifier.startsWith('alphat1')) {\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 this.ensureConnected();\n\n // Query for nametag binding events using hashed nametag (privacy-preserving)\n // Try both '#d' and '#t' filters for compatibility with nostr-js-sdk\n const hashedNametag = hashNametag(nametag);\n\n // First try '#t' tag (nostr-js-sdk format)\n let events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n '#t': [hashedNametag],\n limit: 1,\n });\n\n // Fallback to '#d' tag (legacy format)\n if (events.length === 0) {\n events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n '#d': [hashedNametag],\n limit: 1,\n });\n }\n\n if (events.length === 0) return null;\n\n // Parse binding event\n const bindingEvent = events[0];\n\n // For Nostr messaging (NIP-04 encryption), we MUST use the event author's pubkey.\n // The 'address' tag contains the Unicity blockchain address (not a hex pubkey),\n // which cannot be used for Nostr encryption.\n // The event.pubkey is always the hex pubkey of the nametag owner.\n if (bindingEvent.pubkey) {\n return bindingEvent.pubkey;\n }\n\n // Fallback: try 'p' tag (our SDK format uses hex pubkey here)\n const pubkeyTag = bindingEvent.tags.find((t: string[]) => t[0] === 'p');\n if (pubkeyTag?.[1]) return pubkeyTag[1];\n\n return null;\n }\n\n async resolveNametagInfo(nametag: string): Promise<PeerInfo | null> {\n this.ensureConnected();\n\n // Query for nametag binding events using hashed nametag (privacy-preserving)\n const hashedNametag = hashNametag(nametag);\n\n // First try '#t' tag (nostr-js-sdk format)\n let events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n '#t': [hashedNametag],\n limit: 1,\n });\n\n // Fallback to '#d' tag (legacy format)\n if (events.length === 0) {\n events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n '#d': [hashedNametag],\n limit: 1,\n });\n }\n\n if (events.length === 0) {\n logger.debug('Nostr', `resolveNametagInfo: no binding events found for Unicity ID \"${nametag}\"`);\n return null;\n }\n\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n\n // Compute proper PROXY address using state-transition-sdk\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n const proxyAddress = proxyAddr.toString();\n\n // Check if event has extended fields\n if (content.public_key && content.l1_address) {\n return {\n nametag,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key,\n l1Address: content.l1_address,\n directAddress: content.direct_address || '',\n proxyAddress,\n timestamp: bindingEvent.created_at * 1000,\n };\n }\n\n // Legacy event - only has Nostr pubkey\n // Cannot derive l1_address or l3_address without 33-byte pubkey\n logger.debug('Nostr', 'Legacy nametag event without extended fields:', nametag);\n\n // Try to get info from tags as fallback\n const pubkeyTag = bindingEvent.tags.find((t: string[]) => t[0] === 'pubkey');\n const l1Tag = bindingEvent.tags.find((t: string[]) => t[0] === 'l1');\n\n if (pubkeyTag?.[1] && l1Tag?.[1]) {\n return {\n nametag,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: pubkeyTag[1],\n l1Address: l1Tag[1],\n directAddress: '',\n proxyAddress,\n timestamp: bindingEvent.created_at * 1000,\n };\n }\n\n // Return partial info with empty addresses for legacy events\n return {\n nametag,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '', // Cannot derive from 32-byte Nostr pubkey\n l1Address: '', // Cannot derive without 33-byte pubkey\n directAddress: '',\n proxyAddress,\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n // If content is not JSON, try legacy format\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n return {\n nametag,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n l1Address: '',\n directAddress: '',\n proxyAddress: proxyAddr.toString(),\n timestamp: bindingEvent.created_at * 1000,\n };\n }\n }\n\n /**\n * Resolve a DIRECT://, PROXY://, or L1 address to full peer info.\n * Performs reverse lookup: hash(address) → query '#t' tag → parse binding event.\n * Works with both new identity binding events and legacy nametag binding events.\n */\n async resolveAddressInfo(address: string): Promise<PeerInfo | null> {\n this.ensureConnected();\n\n const addressHash = hashAddressForTag(address);\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n '#t': [addressHash],\n limit: 1,\n });\n\n if (events.length === 0) return null;\n\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n\n return {\n nametag: content.nametag || undefined,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key || '',\n l1Address: content.l1_address || '',\n directAddress: content.direct_address || '',\n proxyAddress: content.proxy_address || undefined,\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n return {\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n l1Address: '',\n directAddress: '',\n timestamp: bindingEvent.created_at * 1000,\n };\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 this.ensureConnected();\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\n return {\n nametag: content.nametag || undefined,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key || '',\n l1Address: content.l1_address || '',\n directAddress: content.direct_address || '',\n proxyAddress: content.proxy_address || undefined,\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n return {\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n l1Address: '',\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 this.ensureConnected();\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 results.push({\n nametag: content.nametag || undefined,\n transportPubkey: pubkey,\n chainPubkey: content.public_key || '',\n l1Address: content.l1_address || '',\n directAddress: content.direct_address || '',\n proxyAddress: content.proxy_address || undefined,\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 this.ensureReady();\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, l1Address, directAddress).\n * With nametag: also publishes nametag hash, proxy address, encrypted nametag for recovery.\n *\n * Uses kind 30078 parameterized replaceable event with d=SHA256('unicity:identity:' + nostrPubkey).\n * Each HD address index has its own Nostr key → its own binding event.\n *\n * @returns true if successful, false if nametag is taken by another pubkey\n */\n async publishIdentityBinding(\n chainPubkey: string,\n l1Address: 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 // Deterministic d-tag: SHA256('unicity:identity:' + nostrPubkey) — privacy-preserving\n const dTagBytes = new TextEncoder().encode('unicity:identity:' + nostrPubkey);\n const dTag = Buffer.from(sha256Noble(dTagBytes)).toString('hex');\n\n // Content — event.pubkey already identifies the author, event.created_at provides timestamp\n const contentObj: Record<string, unknown> = {\n public_key: chainPubkey,\n l1_address: l1Address,\n direct_address: directAddress,\n };\n\n // Tags — 'd' for replacement, 't' for indexed lookups\n const tags: string[][] = [\n ['d', dTag],\n ['t', hashAddressForTag(chainPubkey)],\n ['t', hashAddressForTag(directAddress)],\n ['t', hashAddressForTag(l1Address)],\n ];\n\n // If nametag provided, check availability and add nametag-specific fields\n if (nametag) {\n const existing = await this.resolveNametag(nametag);\n if (existing && existing !== nostrPubkey) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag, '- owner:', existing);\n return false;\n }\n\n // Compute proxy address\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n const proxyAddress = proxyAddr.toString();\n\n // Encrypt nametag for recovery\n const encryptedNametag = await encryptNametag(nametag, this.identity.privateKey);\n const hashedNametag = hashNametag(nametag);\n\n // Add nametag fields to content\n contentObj.nametag = nametag;\n contentObj.encrypted_nametag = encryptedNametag;\n contentObj.proxy_address = proxyAddress;\n\n // Add nametag-specific 't' tags for indexed lookup\n tags.push(['t', hashedNametag]);\n tags.push(['t', hashAddressForTag(proxyAddress)]);\n }\n\n const content = JSON.stringify(contentObj);\n const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);\n await this.publishEvent(event);\n\n if (nametag) {\n logger.debug('Nostr', 'Published identity binding with Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...');\n } else {\n logger.debug('Nostr', 'Published identity binding (no Unicity ID) for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n\n return true;\n }\n\n /** @deprecated Use publishIdentityBinding instead */\n async publishNametag(nametag: string, address: string): Promise<void> {\n this.ensureReady();\n\n // Use hashed nametag (privacy-preserving)\n const hashedNametag = hashNametag(nametag);\n const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, address, [\n ['d', hashedNametag],\n ['a', address],\n ]);\n\n await this.publishEvent(event);\n logger.debug('Nostr', 'Published Unicity ID binding:', nametag);\n }\n\n async registerNametag(nametag: string, _publicKey: string, directAddress: string = ''): Promise<boolean> {\n this.ensureReady();\n\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n // Always use 32-byte Nostr-format pubkey from keyManager (not the 33-byte compressed key)\n const nostrPubkey = this.getNostrPubkey();\n\n // Check if nametag is already taken by someone else\n const existing = await this.resolveNametag(nametag);\n\n logger.debug('Nostr', 'registerNametag:', nametag, 'existing:', existing, 'myPubkey:', nostrPubkey);\n\n if (existing && existing !== nostrPubkey) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag, '- owner:', existing);\n return false;\n }\n\n // Always (re)publish to ensure event has correct format with all required tags\n // This is a parameterized replaceable event (kind 30078), so publishing with same 'd' tag\n // will replace any old event. This ensures the event has ['t', hash] tag for nostr-js-sdk.\n\n // Derive extended address info for full nametag support:\n // - encrypted_nametag: AES-GCM encrypted nametag for recovery\n // - public_key: 33-byte compressed public key for L3 operations\n // - l1_address: L1 address (alpha1...) for L1 transfers\n const privateKeyHex = this.identity.privateKey;\n const compressedPubkey = getPublicKey(privateKeyHex, true); // 33-byte compressed\n const l1Address = publicKeyToAddress(compressedPubkey, 'alpha'); // alpha1...\n const encryptedNametag = await encryptNametag(nametag, privateKeyHex);\n\n // Compute PROXY address for reverse lookup\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n const proxyAddress = proxyAddr.toString();\n\n // Publish nametag binding with extended info\n const hashedNametag = hashNametag(nametag);\n const content = JSON.stringify({\n nametag_hash: hashedNametag,\n address: nostrPubkey,\n verified: Date.now(),\n // Extended fields for nametag recovery and address lookup\n encrypted_nametag: encryptedNametag,\n public_key: compressedPubkey,\n l1_address: l1Address,\n direct_address: directAddress,\n proxy_address: proxyAddress,\n });\n\n // Build tags with indexed 't' tags for reverse lookup by nametag and address\n const tags: string[][] = [\n ['d', hashedNametag],\n ['nametag', hashedNametag],\n ['t', hashedNametag],\n ['t', hashAddressForTag(directAddress)],\n ['t', hashAddressForTag(proxyAddress)],\n ['address', nostrPubkey],\n ['pubkey', compressedPubkey],\n ['l1', l1Address],\n ];\n\n const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);\n\n await this.publishEvent(event);\n logger.debug('Nostr', 'Registered Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...', 'l1:', l1Address.slice(0, 12) + '...');\n return true;\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.TOKEN_TRANSFER:\n await this.handleTokenTransfer(event);\n break;\n case EVENT_KINDS.PAYMENT_REQUEST:\n await this.handlePaymentRequest(event);\n break;\n case EVENT_KINDS.PAYMENT_REQUEST_RESPONSE:\n await this.handlePaymentRequestResponse(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 const kind = event.kind;\n if (\n kind === EVENT_KINDS.DIRECT_MESSAGE ||\n kind === EVENT_KINDS.TOKEN_TRANSFER ||\n kind === EVENT_KINDS.PAYMENT_REQUEST ||\n kind === EVENT_KINDS.PAYMENT_REQUEST_RESPONSE\n ) {\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 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 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 async handleTokenTransfer(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const payload = JSON.parse(content) as TokenTransferPayload;\n\n const transfer: IncomingTokenTransfer = {\n id: event.id,\n senderTransportPubkey: event.pubkey,\n payload,\n timestamp: event.created_at * 1000,\n };\n\n this.emitEvent({ type: 'transfer:received', timestamp: Date.now() });\n\n for (const handler of this.transferHandlers) {\n try {\n await handler(transfer);\n } catch (error) {\n logger.debug('Nostr', 'Transfer handler error:', error);\n }\n }\n }\n\n private async handlePaymentRequest(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n try {\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const requestData = JSON.parse(content) as {\n requestId: string;\n amount: string;\n coinId: string;\n message?: string;\n recipientNametag?: string;\n metadata?: Record<string, unknown>;\n };\n\n const request: IncomingPaymentRequest = {\n id: event.id,\n senderTransportPubkey: event.pubkey,\n senderNametag: requestData.recipientNametag,\n request: {\n requestId: requestData.requestId,\n amount: requestData.amount,\n coinId: requestData.coinId,\n message: requestData.message,\n recipientNametag: requestData.recipientNametag,\n metadata: requestData.metadata,\n },\n timestamp: event.created_at * 1000,\n };\n\n logger.debug('Nostr', 'Received payment request:', request.id);\n\n for (const handler of this.paymentRequestHandlers) {\n try {\n handler(request);\n } catch (error) {\n logger.debug('Nostr', 'Payment request handler error:', error);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle payment request:', error);\n }\n }\n\n private async handlePaymentRequestResponse(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n try {\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const responseData = JSON.parse(content) as {\n requestId: string;\n responseType: 'accepted' | 'rejected' | 'paid';\n message?: string;\n transferId?: string;\n };\n\n const response: IncomingPaymentRequestResponse = {\n id: event.id,\n responderTransportPubkey: event.pubkey,\n response: {\n requestId: responseData.requestId,\n responseType: responseData.responseType,\n message: responseData.message,\n transferId: responseData.transferId,\n },\n timestamp: event.created_at * 1000,\n };\n\n logger.debug('Nostr', 'Received payment request response:', response.id, 'type:', responseData.responseType);\n\n for (const handler of this.paymentRequestResponseHandlers) {\n try {\n handler(response);\n } catch (error) {\n logger.debug('Nostr', 'Payment request response handler error:', error);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle payment request response:', error);\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 // Convert to nostr-js-sdk Event and publish\n const sdkEvent = NostrEventClass.fromJSON(event);\n await this.nostrClient.publishEvent(sdkEvent);\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 EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n ];\n walletFilter['#p'] = [nostrPubkey];\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n if (subId) this.nostrClient?.unsubscribe(subId);\n resolve();\n }, 5000);\n\n const subId = this.nostrClient!.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: () => {\n clearTimeout(timeout);\n this.nostrClient?.unsubscribe(subId);\n resolve();\n },\n });\n });\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 const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (subId) {\n this.nostrClient?.unsubscribe(subId);\n }\n logger.warn('Nostr', `queryEvents timed out after 5s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 5000);\n\n const subId = this.nostrClient!.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: () => {\n clearTimeout(timeout);\n this.nostrClient?.unsubscribe(subId);\n resolve(events);\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 private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\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 logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else {\n // No stored timestamp = 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 }\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 (token transfers, payment requests) with since filter\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\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 // Subscribe to chat events (NIP-17 gift wrap) WITHOUT since filter\n // This matches Sphere app's approach - chat messages rely on deduplication\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NO since filter for chat - we want real-time messages\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 },\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 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 const senderPubkey = this.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), this.keyManager!.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(this.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/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */\nexport function isBytes(a: unknown): a is Uint8Array {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n\n/** Asserts something is positive integer. */\nexport function anumber(n: number, title: string = ''): void {\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new Error(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/** Asserts something is Uint8Array. */\nexport function abytes(value: Uint8Array, length?: number, title: string = ''): 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 throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n}\n\n/** Asserts something is hash */\nexport function ahash(h: CHash): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new Error('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n}\n\n/** Asserts a hash instance has not been destroyed / finished */\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/** Asserts output is properly-sized byte array */\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 Error('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/** Cast u8 / u16 / u32 to u8. */\nexport function u8(arr: TypedArray): Uint8Array {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** Cast u8 / u16 / u32 to u32. */\nexport function u32(arr: TypedArray): Uint32Array {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n\n/** Zeroize a byte array. Warning: JS provides no guarantees. */\nexport function clean(...arrays: TypedArray[]): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/** Create DataView of an array for easy byte-level manipulation. */\nexport function createView(arr: TypedArray): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** The rotate right (circular right shift) operation for uint32 */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/** The rotate left (circular left shift) operation for uint32 */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Is current platform little-endian? Most are. Big-Endian platform: IBM */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/** The byte swap operation for uint32 */\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/** Conditionally byte swap if on a big-endian platform */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n);\n\n/** In place byte swap for Uint32Array */\nexport function byteSwap32(arr: Uint32Array): Uint32Array {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n\nexport const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE\n ? (u: Uint32Array) => u\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. Uses built-in function, when available.\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes: 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 * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex: string): Uint8Array {\n if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);\n // @ts-ignore\n if (hasHexBuiltin) return Uint8Array.fromHex(hex);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new Error('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 Error('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\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 * Call of async fn will return Promise, which will be fullfiled only on\n * next scheduler queue processing step and this is exactly what we need.\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/** Returns control to thread each 'tick' ms to avoid blocking. */\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 * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])\n */\nexport function utf8ToBytes(str: string): Uint8Array {\n if (typeof str !== 'string') throw new Error('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 * When string is passed, does utf8 decoding, using TextDecoder.\n */\nexport function kdfInputToBytes(data: KDFInput, errorTitle = ''): Uint8Array {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/** Copies several Uint8Arrays into one. */\nexport function concatBytes(...arrays: Uint8Array[]): 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/** Merges default options and passed options. */\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 Error('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 hashes. */\nexport interface Hash<T> {\n blockLen: number; // Bytes per block\n outputLen: number; // Bytes in output\n update(buf: Uint8Array): this;\n digestInto(buf: Uint8Array): void;\n digest(): Uint8Array;\n destroy(): void;\n _cloneInto(to?: T): T;\n clone(): T;\n}\n\n/** PseudoRandom (number) Generator */\nexport interface PRG {\n addEntropy(seed: Uint8Array): void;\n randomBytes(length: number): Uint8Array;\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 xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream\n xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf\n};\n\n/** Hash constructor */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash params. */\nexport type HashInfo = {\n oid?: Uint8Array; // DER encoded OID in bytes\n};\n/** Hash function */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n outputLen: number;\n blockLen: number;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: Uint8Array): Uint8Array;\n create(): T;\n }\n : {\n (msg: Uint8Array, opts?: Opts): Uint8Array;\n create(opts?: Opts): T;\n });\n/** XOF with output */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/** Creates function with outputLen, blockLen, create properties from a class constructor. */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: HashInfo = {}\n): CHash<T, Opts> {\n const hashC: any = (msg: Uint8Array, opts?: Opts) => hashCons(opts).update(msg).digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC);\n}\n\n/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */\nexport function randomBytes(bytesLength = 32): Uint8Array {\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 return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/** Creates OID opts for NIST hashes, with prefix 06 09 60 86 48 01 65 03 04 02. */\nexport const oidNist = (suffix: number): Required<HashInfo> => ({\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport { abytes, aexists, ahash, clean, type CHash, type Hash } from './utils.ts';\n\n/** Internal class for HMAC. */\nexport class _HMAC<T extends Hash<T>> implements Hash<_HMAC<T>> {\n oHash: T;\n iHash: T;\n blockLen: number;\n outputLen: number;\n private finished = false;\n private destroyed = false;\n\n constructor(hash: CHash, key: Uint8Array) {\n ahash(hash);\n abytes(key, undefined, 'key');\n this.iHash = hash.create() as T;\n if (typeof this.iHash.update !== 'function')\n throw new Error('Expected instance of class which extends utils.Hash');\n this.blockLen = this.iHash.blockLen;\n this.outputLen = this.iHash.outputLen;\n const blockLen = this.blockLen;\n const pad = new Uint8Array(blockLen);\n // blockLen can be bigger than outputLen\n pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;\n this.iHash.update(pad);\n // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone\n this.oHash = hash.create() as T;\n // Undo internal XOR && apply outer XOR\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;\n this.oHash.update(pad);\n clean(pad);\n }\n update(buf: Uint8Array): this {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n abytes(out, this.outputLen, 'output');\n this.finished = true;\n this.iHash.digestInto(out);\n this.oHash.update(out);\n this.oHash.digestInto(out);\n this.destroy();\n }\n digest(): Uint8Array {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out;\n }\n _cloneInto(to?: _HMAC<T>): _HMAC<T> {\n // Create new instance without calling constructor since key already in state and we don't know it.\n to ||= Object.create(Object.getPrototypeOf(this), {});\n const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;\n to = to as this;\n to.finished = finished;\n to.destroyed = destroyed;\n to.blockLen = blockLen;\n to.outputLen = outputLen;\n to.oHash = oHash._cloneInto(to.oHash);\n to.iHash = iHash._cloneInto(to.iHash);\n return to;\n }\n clone(): _HMAC<T> {\n return this._cloneInto();\n }\n destroy(): void {\n this.destroyed = true;\n this.oHash.destroy();\n this.iHash.destroy();\n }\n}\n\n/**\n * HMAC: RFC2104 message authentication code.\n * @param hash - function that would be used e.g. sha256\n * @param key - message key\n * @param message - message data\n * @example\n * import { hmac } from '@noble/hashes/hmac';\n * import { sha256 } from '@noble/hashes/sha2';\n * const mac1 = hmac(sha256, 'key', 'message');\n */\nexport const hmac: {\n (hash: CHash, key: Uint8Array, message: Uint8Array): Uint8Array;\n create(hash: CHash, key: Uint8Array): _HMAC<any>;\n} = (hash: CHash, key: Uint8Array, message: Uint8Array): Uint8Array =>\n new _HMAC<any>(hash, key).update(message).digest();\nhmac.create = (hash: CHash, key: Uint8Array) => new _HMAC<any>(hash, key);\n","/**\n * HKDF (RFC 5869): extract + expand in one step.\n * See https://soatok.blog/2021/11/17/understanding-hkdf/.\n * @module\n */\nimport { hmac } from './hmac.ts';\nimport { abytes, ahash, anumber, type CHash, clean } from './utils.ts';\n\n/**\n * HKDF-extract from spec. Less important part. `HKDF-Extract(IKM, salt) -> PRK`\n * Arguments position differs from spec (IKM is first one, since it is not optional)\n * @param hash - hash function that would be used (e.g. sha256)\n * @param ikm - input keying material, the initial key\n * @param salt - optional salt value (a non-secret random value)\n */\nexport function extract(hash: CHash, ikm: Uint8Array, salt?: Uint8Array): Uint8Array {\n ahash(hash);\n // NOTE: some libraries treat zero-length array as 'not provided';\n // we don't, since we have undefined as 'not provided'\n // https://github.com/RustCrypto/KDFs/issues/15\n if (salt === undefined) salt = new Uint8Array(hash.outputLen);\n return hmac(hash, salt, ikm);\n}\n\nconst HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);\nconst EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();\n\n/**\n * HKDF-expand from the spec. The most important part. `HKDF-Expand(PRK, info, L) -> OKM`\n * @param hash - hash function that would be used (e.g. sha256)\n * @param prk - a pseudorandom key of at least HashLen octets (usually, the output from the extract step)\n * @param info - optional context and application specific information (can be a zero-length string)\n * @param length - length of output keying material in bytes\n */\nexport function expand(\n hash: CHash,\n prk: Uint8Array,\n info?: Uint8Array,\n length: number = 32\n): Uint8Array {\n ahash(hash);\n anumber(length, 'length');\n const olen = hash.outputLen;\n if (length > 255 * olen) throw new Error('Length must be <= 255*HashLen');\n const blocks = Math.ceil(length / olen);\n if (info === undefined) info = EMPTY_BUFFER;\n else abytes(info, undefined, 'info');\n // first L(ength) octets of T\n const okm = new Uint8Array(blocks * olen);\n // Re-use HMAC instance between blocks\n const HMAC = hmac.create(hash, prk);\n const HMACTmp = HMAC._cloneInto();\n const T = new Uint8Array(HMAC.outputLen);\n for (let counter = 0; counter < blocks; counter++) {\n HKDF_COUNTER[0] = counter + 1;\n // T(0) = empty string (zero length)\n // T(N) = HMAC-Hash(PRK, T(N-1) | info | N)\n HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)\n .update(info)\n .update(HKDF_COUNTER)\n .digestInto(T);\n okm.set(T, olen * counter);\n HMAC._cloneInto(HMACTmp);\n }\n HMAC.destroy();\n HMACTmp.destroy();\n clean(T, HKDF_COUNTER);\n return okm.slice(0, length);\n}\n\n/**\n * HKDF (RFC 5869): derive keys from an initial input.\n * Combines hkdf_extract + hkdf_expand in one step\n * @param hash - hash function that would be used (e.g. sha256)\n * @param ikm - input keying material, the initial key\n * @param salt - optional salt value (a non-secret random value)\n * @param info - optional context and application specific information (can be a zero-length string)\n * @param length - length of output keying material in bytes\n * @example\n * import { hkdf } from '@noble/hashes/hkdf';\n * import { sha256 } from '@noble/hashes/sha2';\n * import { randomBytes } from '@noble/hashes/utils';\n * const inputKey = randomBytes(32);\n * const salt = randomBytes(32);\n * const info = 'application-key';\n * const hk1 = hkdf(sha256, inputKey, salt, info, 32);\n */\nexport const hkdf = (\n hash: CHash,\n ikm: Uint8Array,\n salt: Uint8Array | undefined,\n info: Uint8Array | undefined,\n length: number\n): Uint8Array => expand(hash, extract(hash, ikm, salt), info, length);\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport { abytes, aexists, aoutput, clean, createView, type Hash } from './utils.ts';\n\n/** Choice: a ? b : c */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/** Majority function, true if any two inputs is true. */\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 */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\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 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: 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: we have at least one block in input, cast it to view and process\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: 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 // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\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(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\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 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. Bits 0..32 of frac part of sqrt of primes 2..19 */\nexport const SHA256_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state. Bits 32..64 of frac part of sqrt of primes 23..53 */\nexport const SHA224_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state. Bits 0..64 of frac part of sqrt of primes 23..53 */\nexport const SHA384_IV: 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. Bits 0..64 of frac part of sqrt of primes 2..19 */\nexport const SHA512_IV: 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 [RFC 4634](https://www.rfc-editor.org/rfc/rfc4634) and\n * [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\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 } from './utils.ts';\n\n/**\n * Round constants:\n * First 32 bits of fractional parts 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 temporary buffer. \"W\" comes straight from spec. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal 32-byte base SHA2 hash class. */\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 this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA2-256 hash class. */\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 SHA2-224 hash class. */\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// Round contants\n// First 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409\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 temporary buffers\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal 64-byte base SHA2 hash class. */\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 // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_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 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 SHA2-512 hash class. */\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 SHA2-384 hash class. */\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 `test/misc/sha2-gen-iv.js`.\n */\n\n/** SHA512/224 IV */\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/** SHA512/256 IV */\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 SHA2-512/224 hash class. */\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 SHA2-512/256 hash class. */\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 */\nexport const sha256: CHash<_SHA256> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/** SHA2-224 hash function from RFC 4634 */\nexport const sha224: CHash<_SHA224> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/** SHA2-512 hash function from RFC 4634. */\nexport const sha512: CHash<_SHA512> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/** SHA2-384 hash function from RFC 4634. */\nexport const sha384: 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 [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_256: 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 [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_224: CHash<_SHA512_224> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n","/**\n * Cryptographic utilities for SDK2\n *\n * Provides BIP39 mnemonic and BIP32 key derivation functions.\n * Platform-independent - no browser-specific APIs.\n */\n\nimport * as bip39 from 'bip39';\nimport CryptoJS from 'crypto-js';\nimport elliptic from 'elliptic';\nimport { encodeBech32 } from './bech32';\nimport { SphereError } from './errors';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst ec = new elliptic.ec('secp256k1');\n\n/** secp256k1 curve order */\nconst CURVE_ORDER = BigInt(\n '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'\n);\n\n/** Default derivation path for Unicity (BIP44) */\nexport const DEFAULT_DERIVATION_PATH = \"m/44'/0'/0'\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface MasterKey {\n privateKey: string;\n chainCode: string;\n}\n\nexport interface DerivedKey {\n privateKey: string;\n chainCode: string;\n}\n\nexport interface KeyPair {\n privateKey: string;\n publicKey: string;\n}\n\nexport interface AddressInfo extends KeyPair {\n address: string;\n path: string;\n index: number;\n}\n\n// =============================================================================\n// BIP39 Mnemonic Functions\n// =============================================================================\n\n/**\n * Generate a new BIP39 mnemonic phrase\n * @param strength - Entropy bits (128 = 12 words, 256 = 24 words)\n */\nexport function generateMnemonic(strength: 128 | 256 = 128): string {\n return bip39.generateMnemonic(strength);\n}\n\n/**\n * Validate a BIP39 mnemonic phrase\n */\nexport function validateMnemonic(mnemonic: string): boolean {\n return bip39.validateMnemonic(mnemonic);\n}\n\n/**\n * Convert mnemonic to seed (64-byte hex string)\n * @param mnemonic - BIP39 mnemonic phrase\n * @param passphrase - Optional passphrase for additional security\n */\nexport async function mnemonicToSeed(\n mnemonic: string,\n passphrase: string = ''\n): Promise<string> {\n const seedBuffer = await bip39.mnemonicToSeed(mnemonic, passphrase);\n return Buffer.from(seedBuffer).toString('hex');\n}\n\n/**\n * Synchronous version of mnemonicToSeed\n */\nexport function mnemonicToSeedSync(\n mnemonic: string,\n passphrase: string = ''\n): string {\n const seedBuffer = bip39.mnemonicToSeedSync(mnemonic, passphrase);\n return Buffer.from(seedBuffer).toString('hex');\n}\n\n/**\n * Convert mnemonic to entropy (for recovery purposes)\n */\nexport function mnemonicToEntropy(mnemonic: string): string {\n return bip39.mnemonicToEntropy(mnemonic);\n}\n\n/**\n * Convert entropy to mnemonic\n */\nexport function entropyToMnemonic(entropy: string): string {\n return bip39.entropyToMnemonic(entropy);\n}\n\n// =============================================================================\n// BIP32 Key Derivation\n// =============================================================================\n\n/**\n * Generate master key from seed (BIP32 standard)\n * Uses HMAC-SHA512 with key \"Bitcoin seed\"\n */\nexport function generateMasterKey(seedHex: string): MasterKey {\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(seedHex),\n CryptoJS.enc.Utf8.parse('Bitcoin seed')\n ).toString();\n\n const IL = I.substring(0, 64); // Left 32 bytes - master private key\n const IR = I.substring(64); // Right 32 bytes - master chain code\n\n // Validate master key\n const masterKeyBigInt = BigInt('0x' + IL);\n if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {\n throw new SphereError('Invalid master key generated', 'VALIDATION_ERROR');\n }\n\n return {\n privateKey: IL,\n chainCode: IR,\n };\n}\n\n/**\n * Derive child key using BIP32 standard\n * @param parentPrivKey - Parent private key (64 hex chars)\n * @param parentChainCode - Parent chain code (64 hex chars)\n * @param index - Child index (>= 0x80000000 for hardened)\n */\nexport function deriveChildKey(\n parentPrivKey: string,\n parentChainCode: string,\n index: number\n): DerivedKey {\n const isHardened = index >= 0x80000000;\n let data: string;\n\n if (isHardened) {\n // Hardened derivation: 0x00 || parentPrivKey || index\n const indexHex = index.toString(16).padStart(8, '0');\n data = '00' + parentPrivKey + indexHex;\n } else {\n // Non-hardened derivation: compressedPubKey || index\n const keyPair = ec.keyFromPrivate(parentPrivKey, 'hex');\n const compressedPubKey = keyPair.getPublic(true, 'hex');\n const indexHex = index.toString(16).padStart(8, '0');\n data = compressedPubKey + indexHex;\n }\n\n // HMAC-SHA512 with chain code as key\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(data),\n CryptoJS.enc.Hex.parse(parentChainCode)\n ).toString();\n\n const IL = I.substring(0, 64); // Left 32 bytes\n const IR = I.substring(64); // Right 32 bytes (new chain code)\n\n // Add IL to parent key mod n (curve order)\n const ilBigInt = BigInt('0x' + IL);\n const parentKeyBigInt = BigInt('0x' + parentPrivKey);\n\n // Check IL is valid (less than curve order)\n if (ilBigInt >= CURVE_ORDER) {\n throw new SphereError('Invalid key: IL >= curve order', 'VALIDATION_ERROR');\n }\n\n const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;\n\n // Check child key is valid (not zero)\n if (childKeyBigInt === 0n) {\n throw new SphereError('Invalid key: child key is zero', 'VALIDATION_ERROR');\n }\n\n const childPrivKey = childKeyBigInt.toString(16).padStart(64, '0');\n\n return {\n privateKey: childPrivKey,\n chainCode: IR,\n };\n}\n\n/**\n * Derive key at a full BIP32/BIP44 path\n * @param masterPrivKey - Master private key\n * @param masterChainCode - Master chain code\n * @param path - BIP44 path like \"m/44'/0'/0'/0/0\"\n */\nexport function deriveKeyAtPath(\n masterPrivKey: string,\n masterChainCode: string,\n path: string\n): DerivedKey {\n const pathParts = path.replace('m/', '').split('/');\n\n let currentKey = masterPrivKey;\n let currentChainCode = masterChainCode;\n\n for (const part of pathParts) {\n const isHardened = part.endsWith(\"'\") || part.endsWith('h');\n const indexStr = part.replace(/['h]$/, '');\n let index = parseInt(indexStr, 10);\n\n if (isHardened) {\n index += 0x80000000; // Add hardened offset\n }\n\n const derived = deriveChildKey(currentKey, currentChainCode, index);\n currentKey = derived.privateKey;\n currentChainCode = derived.chainCode;\n }\n\n return {\n privateKey: currentKey,\n chainCode: currentChainCode,\n };\n}\n\n// =============================================================================\n// Key Pair Operations\n// =============================================================================\n\n/**\n * Get public key from private key\n * @param privateKey - Private key as hex string\n * @param compressed - Return compressed public key (default: true)\n */\nexport function getPublicKey(privateKey: string, compressed: boolean = true): string {\n const keyPair = ec.keyFromPrivate(privateKey, 'hex');\n return keyPair.getPublic(compressed, 'hex');\n}\n\n/**\n * Create key pair from private key\n */\nexport function createKeyPair(privateKey: string): KeyPair {\n return {\n privateKey,\n publicKey: getPublicKey(privateKey),\n };\n}\n\n// =============================================================================\n// Hash Functions\n// =============================================================================\n\n/**\n * Compute SHA256 hash\n */\nexport function sha256(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const parsed =\n inputEncoding === 'hex'\n ? CryptoJS.enc.Hex.parse(data)\n : CryptoJS.enc.Utf8.parse(data);\n return CryptoJS.SHA256(parsed).toString();\n}\n\n/**\n * Compute RIPEMD160 hash\n */\nexport function ripemd160(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const parsed =\n inputEncoding === 'hex'\n ? CryptoJS.enc.Hex.parse(data)\n : CryptoJS.enc.Utf8.parse(data);\n return CryptoJS.RIPEMD160(parsed).toString();\n}\n\n/**\n * Compute HASH160 (SHA256 -> RIPEMD160)\n */\nexport function hash160(data: string): string {\n const sha = sha256(data, 'hex');\n return ripemd160(sha, 'hex');\n}\n\n/**\n * Compute double SHA256\n */\nexport function doubleSha256(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const first = sha256(data, inputEncoding);\n return sha256(first, 'hex');\n}\n\n/**\n * Alias for hash160 (L1 SDK compatibility)\n */\nexport const computeHash160 = hash160;\n\n/**\n * Convert hex string to Uint8Array for witness program\n */\nexport function hash160ToBytes(hash160Hex: string): Uint8Array {\n const matches = hash160Hex.match(/../g);\n if (!matches) return new Uint8Array(0);\n return Uint8Array.from(matches.map((x) => parseInt(x, 16)));\n}\n\n/**\n * Generate bech32 address from public key\n * @param publicKey - Compressed public key as hex string\n * @param prefix - Address prefix (default: \"alpha\")\n * @param witnessVersion - Witness version (default: 0 for P2WPKH)\n * @returns Bech32 encoded address\n */\nexport function publicKeyToAddress(\n publicKey: string,\n prefix: string = 'alpha',\n witnessVersion: number = 0\n): string {\n const pubKeyHash = hash160(publicKey);\n const programBytes = hash160ToBytes(pubKeyHash);\n return encodeBech32(prefix, witnessVersion, programBytes);\n}\n\n/**\n * Get address info from private key\n */\nexport function privateKeyToAddressInfo(\n privateKey: string,\n prefix: string = 'alpha'\n): { address: string; publicKey: string } {\n const publicKey = getPublicKey(privateKey);\n const address = publicKeyToAddress(publicKey, prefix);\n return { address, publicKey };\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Convert hex string to Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const matches = hex.match(/../g);\n if (!matches) {\n return new Uint8Array(0);\n }\n return Uint8Array.from(matches.map((x) => parseInt(x, 16)));\n}\n\n/**\n * Convert Uint8Array to hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate random bytes as hex string\n */\nexport function randomBytes(length: number): string {\n const words = CryptoJS.lib.WordArray.random(length);\n return words.toString(CryptoJS.enc.Hex);\n}\n\n// =============================================================================\n// High-Level Functions\n// =============================================================================\n\n/**\n * Generate identity from mnemonic\n * Returns master key derived from mnemonic seed\n */\nexport async function identityFromMnemonic(\n mnemonic: string,\n passphrase: string = ''\n): Promise<MasterKey> {\n if (!validateMnemonic(mnemonic)) {\n throw new SphereError('Invalid mnemonic phrase', 'INVALID_IDENTITY');\n }\n const seedHex = await mnemonicToSeed(mnemonic, passphrase);\n return generateMasterKey(seedHex);\n}\n\n/**\n * Synchronous version of identityFromMnemonic\n */\nexport function identityFromMnemonicSync(\n mnemonic: string,\n passphrase: string = ''\n): MasterKey {\n if (!validateMnemonic(mnemonic)) {\n throw new SphereError('Invalid mnemonic phrase', 'INVALID_IDENTITY');\n }\n const seedHex = mnemonicToSeedSync(mnemonic, passphrase);\n return generateMasterKey(seedHex);\n}\n\n/**\n * Derive address info at a specific path\n * @param masterKey - Master key with privateKey and chainCode\n * @param basePath - Base derivation path (e.g., \"m/44'/0'/0'\")\n * @param index - Address index\n * @param isChange - Whether this is a change address (chain 1 vs 0)\n * @param prefix - Address prefix (default: \"alpha\")\n */\nexport function deriveAddressInfo(\n masterKey: MasterKey,\n basePath: string,\n index: number,\n isChange: boolean = false,\n prefix: string = 'alpha'\n): AddressInfo {\n const chain = isChange ? 1 : 0;\n const fullPath = `${basePath}/${chain}/${index}`;\n\n const derived = deriveKeyAtPath(masterKey.privateKey, masterKey.chainCode, fullPath);\n const publicKey = getPublicKey(derived.privateKey);\n const address = publicKeyToAddress(publicKey, prefix);\n\n return {\n privateKey: derived.privateKey,\n publicKey,\n address,\n path: fullPath,\n index,\n };\n}\n\n/**\n * Generate full address info from private key with index and path\n * (L1 SDK compatibility)\n */\nexport function generateAddressInfo(\n privateKey: string,\n index: number,\n path: string,\n prefix: string = 'alpha'\n): AddressInfo {\n const { address, publicKey } = privateKeyToAddressInfo(privateKey, prefix);\n return {\n privateKey,\n publicKey,\n address,\n path,\n index,\n };\n}\n\n// =============================================================================\n// Message Signing (secp256k1 ECDSA with recoverable signature)\n// =============================================================================\n\n/** Prefix prepended to all signed messages (Bitcoin-like signed message format) */\nexport const SIGN_MESSAGE_PREFIX = 'Sphere Signed Message:\\n';\n\n/** Encode an integer as a Bitcoin-style compact varint */\nfunction varint(n: number): Uint8Array {\n if (n < 253) return new Uint8Array([n]);\n const buf = new Uint8Array(3);\n buf[0] = 253;\n buf[1] = n & 0xff;\n buf[2] = (n >> 8) & 0xff;\n return buf;\n}\n\n/**\n * Hash a message for signing using the Bitcoin-like double-SHA256 scheme:\n * SHA256(SHA256(varint(prefix.length) + prefix + varint(msg.length) + msg))\n *\n * @returns 64-char lowercase hex hash\n */\nexport function hashSignMessage(message: string): string {\n const prefix = new TextEncoder().encode(SIGN_MESSAGE_PREFIX);\n const msg = new TextEncoder().encode(message);\n const prefixLen = varint(prefix.length);\n const msgLen = varint(msg.length);\n const full = new Uint8Array(prefixLen.length + prefix.length + msgLen.length + msg.length);\n let off = 0;\n full.set(prefixLen, off); off += prefixLen.length;\n full.set(prefix, off); off += prefix.length;\n full.set(msgLen, off); off += msgLen.length;\n full.set(msg, off);\n const hex = Array.from(full).map(b => b.toString(16).padStart(2, '0')).join('');\n const h1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(hex)).toString();\n return CryptoJS.SHA256(CryptoJS.enc.Hex.parse(h1)).toString();\n}\n\n/**\n * Sign a message with a secp256k1 private key.\n *\n * Returns a 130-character hex string: v (2 chars) + r (64 chars) + s (64 chars).\n * The recovery byte `v` is `31 + recoveryParam` (0-3).\n *\n * @param privateKeyHex - 64-char hex private key\n * @param message - plaintext message to sign\n */\nexport function signMessage(privateKeyHex: string, message: string): string {\n const keyPair = ec.keyFromPrivate(privateKeyHex, 'hex');\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n const sig = keyPair.sign(hashBytes, { canonical: true });\n\n // Find recovery parameter\n const pub = keyPair.getPublic();\n let recoveryParam = -1;\n for (let i = 0; i < 4; i++) {\n try {\n if (ec.recoverPubKey(hashBytes, sig, i).eq(pub)) {\n recoveryParam = i;\n break;\n }\n } catch { /* try next */ }\n }\n if (recoveryParam === -1) {\n throw new SphereError('Could not find recovery parameter', 'SIGNING_ERROR');\n }\n\n const v = (31 + recoveryParam).toString(16).padStart(2, '0');\n const r = sig.r.toString('hex').padStart(64, '0');\n const s = sig.s.toString('hex').padStart(64, '0');\n return v + r + s;\n}\n\n/**\n * Verify a signed message against a compressed secp256k1 public key.\n *\n * @param message - The original plaintext message\n * @param signature - 130-char hex signature (v + r + s)\n * @param expectedPubkey - 66-char compressed public key hex\n * @returns `true` if the signature is valid and matches the expected public key\n */\nexport function verifySignedMessage(\n message: string,\n signature: string,\n expectedPubkey: string,\n): boolean {\n if (signature.length !== 130) return false;\n\n const v = parseInt(signature.slice(0, 2), 16) - 31;\n const r = signature.slice(2, 66);\n const s = signature.slice(66, 130);\n\n if (v < 0 || v > 3) return false;\n\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n\n try {\n const recovered = ec.recoverPubKey(hashBytes, { r, s }, v);\n const recoveredHex = recovered.encode('hex', true); // compressed\n return recoveredHex === expectedPubkey;\n } catch {\n return false;\n }\n}\n\n// Re-export elliptic instance for advanced use cases\nexport { ec };\n","/**\n * Bech32 Encoding/Decoding\n * BIP-173 implementation for address encoding\n */\n\nimport { SphereError } from './errors';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Bech32 character set from BIP-173 */\nexport const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\n/** Generator polynomial for checksum */\nconst GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];\n\n// =============================================================================\n// Bit Conversion\n// =============================================================================\n\n/**\n * Convert between bit arrays (8→5 or 5→8)\n */\nexport function convertBits(\n data: number[],\n fromBits: number,\n toBits: number,\n pad: boolean\n): number[] | null {\n let acc = 0;\n let bits = 0;\n const ret: number[] = [];\n const maxv = (1 << toBits) - 1;\n\n for (let i = 0; i < data.length; i++) {\n const value = data[i];\n if (value < 0 || value >> fromBits !== 0) return null;\n acc = (acc << fromBits) | value;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n ret.push((acc >> bits) & maxv);\n }\n }\n\n if (pad) {\n if (bits > 0) {\n ret.push((acc << (toBits - bits)) & maxv);\n }\n } else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) {\n return null;\n }\n\n return ret;\n}\n\n// =============================================================================\n// Internal Functions\n// =============================================================================\n\n/**\n * Expand HRP for checksum calculation\n */\nfunction hrpExpand(hrp: string): number[] {\n const ret: number[] = [];\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);\n ret.push(0);\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);\n return ret;\n}\n\n/**\n * Calculate polymod checksum\n */\nfunction bech32Polymod(values: number[]): number {\n let chk = 1;\n for (let p = 0; p < values.length; p++) {\n const top = chk >> 25;\n chk = ((chk & 0x1ffffff) << 5) ^ values[p];\n for (let i = 0; i < 5; i++) {\n if ((top >> i) & 1) chk ^= GENERATOR[i];\n }\n }\n return chk;\n}\n\n/**\n * Create checksum for bech32\n */\nfunction bech32Checksum(hrp: string, data: number[]): number[] {\n const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);\n const mod = bech32Polymod(values) ^ 1;\n\n const ret: number[] = [];\n for (let p = 0; p < 6; p++) {\n ret.push((mod >> (5 * (5 - p))) & 31);\n }\n return ret;\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Encode data to bech32 address\n *\n * @example\n * ```ts\n * const address = encodeBech32('alpha', 1, pubkeyHash);\n * // 'alpha1qw...'\n * ```\n */\nexport function encodeBech32(\n hrp: string,\n version: number,\n program: Uint8Array\n): string {\n if (version < 0 || version > 16) {\n throw new SphereError('Invalid witness version', 'VALIDATION_ERROR');\n }\n\n const converted = convertBits(Array.from(program), 8, 5, true);\n if (!converted) {\n throw new SphereError('Failed to convert bits', 'VALIDATION_ERROR');\n }\n\n const data = [version].concat(converted);\n const checksum = bech32Checksum(hrp, data);\n const combined = data.concat(checksum);\n\n let out = hrp + '1';\n for (let i = 0; i < combined.length; i++) {\n out += CHARSET[combined[i]];\n }\n\n return out;\n}\n\n/**\n * Decode bech32 address\n *\n * @example\n * ```ts\n * const result = decodeBech32('alpha1qw...');\n * // { hrp: 'alpha', witnessVersion: 1, data: Uint8Array }\n * ```\n */\nexport function decodeBech32(\n addr: string\n): { hrp: string; witnessVersion: number; data: Uint8Array } | null {\n addr = addr.toLowerCase();\n\n const pos = addr.lastIndexOf('1');\n if (pos < 1) return null;\n\n const hrp = addr.substring(0, pos);\n const dataStr = addr.substring(pos + 1);\n\n const data: number[] = [];\n for (let i = 0; i < dataStr.length; i++) {\n const val = CHARSET.indexOf(dataStr[i]);\n if (val === -1) return null;\n data.push(val);\n }\n\n // Validate checksum\n const checksum = bech32Checksum(hrp, data.slice(0, -6));\n for (let i = 0; i < 6; i++) {\n if (checksum[i] !== data[data.length - 6 + i]) {\n return null;\n }\n }\n\n const version = data[0];\n const program = convertBits(data.slice(1, -6), 5, 8, false);\n if (!program) return null;\n\n return {\n hrp,\n witnessVersion: version,\n data: Uint8Array.from(program),\n };\n}\n\n/**\n * Create address from public key hash\n *\n * @example\n * ```ts\n * const address = createAddress('alpha', pubkeyHash);\n * // 'alpha1...'\n * ```\n */\nexport function createAddress(hrp: string, pubkeyHash: Uint8Array | string): string {\n const hashBytes = typeof pubkeyHash === 'string'\n ? Uint8Array.from(Buffer.from(pubkeyHash, 'hex'))\n : pubkeyHash;\n\n return encodeBech32(hrp, 1, hashBytes);\n}\n\n/**\n * Validate bech32 address\n */\nexport function isValidBech32(addr: string): boolean {\n return decodeBech32(addr) !== null;\n}\n\n/**\n * Get HRP from address\n */\nexport function getAddressHrp(addr: string): string | null {\n const result = decodeBech32(addr);\n return result?.hrp ?? null;\n}\n\n// =============================================================================\n// Aliases for L1 SDK compatibility\n// =============================================================================\n\n/**\n * Alias for encodeBech32 (L1 SDK compatibility)\n */\nexport const createBech32 = encodeBech32;\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 * Platform-independent implementation using @unicitylabs/state-transition-sdk\n *\n * The oracle is a trusted service that provides verifiable truth\n * about token state through cryptographic inclusion proofs.\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 TransferCommitment,\n SubmitResult,\n InclusionProof,\n WaitOptions,\n ValidationResult,\n TokenState,\n MintParams,\n MintResult,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT, TIMEOUTS } from '../constants';\nimport { SphereError } from '../core/errors';\n\n// SDK imports - using direct imports from the SDK\nimport { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/AggregatorClient';\nimport { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { TransferCommitment as SdkTransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\n\n// SDK MintCommitment type - using interface to avoid generic complexity\ninterface SdkMintCommitment {\n requestId?: { toString(): string };\n [key: string]: unknown;\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base verification (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// RPC Response Types\n// =============================================================================\n\ninterface RpcSubmitResponse {\n requestId?: string;\n}\n\ninterface RpcProofResponse {\n proof?: unknown;\n roundNumber?: number;\n}\n\ninterface RpcValidateResponse {\n valid?: boolean;\n spent?: boolean;\n stateHash?: string;\n error?: string;\n}\n\ninterface RpcSpentResponse {\n spent?: boolean;\n}\n\ninterface RpcTokenStateResponse {\n state?: {\n stateHash?: string;\n spent?: boolean;\n roundNumber?: number;\n };\n}\n\ninterface RpcMintResponse {\n requestId?: string;\n tokenId?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Unicity Aggregator Provider\n * Concrete implementation of OracleProvider using Unicity's aggregator service\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 state transition aggregator (oracle implementation)';\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 // SDK clients\n private aggregatorClient: AggregatorClient | null = null;\n private stateTransitionClient: StateTransitionClient | null = null;\n private trustBase: RootTrustBase | null = null;\n\n /** Get the current trust base */\n getTrustBase(): RootTrustBase | null {\n return this.trustBase;\n }\n\n /** Get the state transition client */\n getStateTransitionClient(): StateTransitionClient | null {\n return this.stateTransitionClient;\n }\n\n /** Get the aggregator client */\n getAggregatorClient(): AggregatorClient | null {\n return this.aggregatorClient;\n }\n\n // Cache for spent states (immutable)\n private spentCache: Map<string, boolean> = new Map();\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 // 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 will be verified on first operation\n // The aggregator requires requestId in params even for status checks,\n // which the SDK client doesn't support directly\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(trustBase?: RootTrustBase): Promise<void> {\n // Initialize SDK clients with optional API key\n this.aggregatorClient = new AggregatorClient(\n this.config.url,\n this.config.apiKey || null\n );\n this.stateTransitionClient = new StateTransitionClient(this.aggregatorClient);\n\n if (trustBase) {\n this.trustBase = trustBase;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n // Load trust base using injected loader\n try {\n const trustBaseJson = await this.config.trustBaseLoader.load();\n if (trustBaseJson) {\n this.trustBase = RootTrustBase.fromJSON(trustBaseJson);\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:', !!this.trustBase);\n }\n\n /**\n * Submit a transfer commitment to the aggregator.\n * Accepts either an SDK TransferCommitment or a simple commitment object.\n */\n async submitCommitment(commitment: TransferCommitment | SdkTransferCommitment): Promise<SubmitResult> {\n this.ensureConnected();\n\n try {\n let requestId: string;\n\n // Check if it's an SDK commitment (has submitTransferCommitment method signature)\n if (this.isSdkTransferCommitment(commitment)) {\n // Use SDK client directly\n const response = await this.stateTransitionClient!.submitTransferCommitment(commitment);\n requestId = commitment.requestId?.toString() ?? response.status;\n } else {\n // Fallback to RPC for simple commitment objects\n const response = await this.rpcCall<RpcSubmitResponse>('submitCommitment', {\n sourceToken: commitment.sourceToken,\n recipient: commitment.recipient,\n salt: Array.from(commitment.salt),\n data: commitment.data,\n });\n requestId = response.requestId ?? '';\n }\n\n this.emitEvent({\n type: 'commitment:submitted',\n timestamp: Date.now(),\n data: { requestId },\n });\n\n return {\n success: true,\n requestId,\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMsg,\n timestamp: Date.now(),\n };\n }\n }\n\n /**\n * Submit a mint commitment to the aggregator (SDK only)\n * @param commitment - SDK MintCommitment instance\n */\n async submitMintCommitment(commitment: SdkMintCommitment): Promise<SubmitResult> {\n this.ensureConnected();\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const response = await this.stateTransitionClient!.submitMintCommitment(commitment as any);\n const requestId = commitment.requestId?.toString() ?? response.status;\n\n this.emitEvent({\n type: 'commitment:submitted',\n timestamp: Date.now(),\n data: { requestId },\n });\n\n return {\n success: true,\n requestId,\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMsg,\n timestamp: Date.now(),\n };\n }\n }\n\n private isSdkTransferCommitment(commitment: unknown): commitment is SdkTransferCommitment {\n return (\n commitment !== null &&\n typeof commitment === 'object' &&\n 'requestId' in commitment &&\n typeof (commitment as SdkTransferCommitment).requestId?.toString === 'function'\n );\n }\n\n async getProof(requestId: string): Promise<InclusionProof | null> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcProofResponse>('getInclusionProof', { requestId });\n\n if (!response.proof) {\n return null;\n }\n\n return {\n requestId,\n roundNumber: response.roundNumber ?? 0,\n proof: response.proof,\n timestamp: Date.now(),\n };\n } catch (error) {\n logger.warn('Aggregator', 'getProof failed', error);\n return null;\n }\n }\n\n async waitForProof(requestId: string, options?: WaitOptions): Promise<InclusionProof> {\n const timeout = options?.timeout ?? this.config.timeout;\n const pollInterval = options?.pollInterval ?? TIMEOUTS.PROOF_POLL_INTERVAL;\n const startTime = Date.now();\n let attempt = 0;\n\n while (Date.now() - startTime < timeout) {\n options?.onPoll?.(++attempt);\n\n const proof = await this.getProof(requestId);\n if (proof) {\n this.emitEvent({\n type: 'proof:received',\n timestamp: Date.now(),\n data: { requestId, roundNumber: proof.roundNumber },\n });\n return proof;\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new SphereError(`Timeout waiting for proof: ${requestId}`, 'TIMEOUT');\n }\n\n async validateToken(tokenData: unknown): Promise<ValidationResult> {\n this.ensureConnected();\n\n try {\n // Try SDK validation first if we have trust base\n if (this.trustBase && !this.config.skipVerification) {\n try {\n const sdkToken = await SdkToken.fromJSON(tokenData);\n const verifyResult = await sdkToken.verify(this.trustBase);\n\n // Calculate state hash\n const stateHash = await sdkToken.state.calculateHash();\n const stateHashStr = stateHash.toJSON();\n\n const valid = verifyResult.isSuccessful;\n\n this.emitEvent({\n type: 'validation:completed',\n timestamp: Date.now(),\n data: { valid },\n });\n\n return {\n valid,\n spent: false, // Spend check is separate\n stateHash: stateHashStr,\n error: valid ? undefined : 'SDK verification failed',\n };\n } catch (sdkError) {\n this.log('SDK validation failed, falling back to RPC:', sdkError);\n }\n }\n\n // Fallback to RPC validation\n const response = await this.rpcCall<RpcValidateResponse>('validateToken', { token: tokenData });\n\n const valid = response.valid ?? false;\n const spent = response.spent ?? false;\n\n this.emitEvent({\n type: 'validation:completed',\n timestamp: Date.now(),\n data: { valid },\n });\n\n // Cache spent state if spent\n if (response.stateHash && spent) {\n this.spentCache.set(response.stateHash, true);\n }\n\n return {\n valid,\n spent,\n stateHash: response.stateHash,\n error: response.error,\n };\n } catch (error) {\n return {\n valid: false,\n spent: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Wait for inclusion proof using SDK (for SDK commitments)\n */\n async waitForProofSdk(\n commitment: SdkTransferCommitment | SdkMintCommitment,\n signal?: AbortSignal\n ): Promise<unknown> {\n this.ensureConnected();\n\n if (!this.trustBase) {\n throw new SphereError('Trust base not initialized', 'NOT_INITIALIZED');\n }\n\n return await waitInclusionProof(\n this.trustBase,\n this.stateTransitionClient!,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n commitment as any,\n signal\n );\n }\n\n async isSpent(stateHash: string): Promise<boolean> {\n // Check cache first (spent is immutable)\n if (this.spentCache.has(stateHash)) {\n return this.spentCache.get(stateHash)!;\n }\n\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcSpentResponse>('isSpent', { stateHash });\n const spent = response.spent ?? false;\n\n // Cache result\n if (spent) {\n this.spentCache.set(stateHash, true);\n }\n\n return spent;\n } catch (error) {\n logger.warn('Aggregator', 'isSpent check failed, assuming unspent', error);\n return false;\n }\n }\n\n async getTokenState(tokenId: string): Promise<TokenState | null> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcTokenStateResponse>('getTokenState', { tokenId });\n\n if (!response.state) {\n return null;\n }\n\n return {\n tokenId,\n stateHash: response.state.stateHash ?? '',\n spent: response.state.spent ?? false,\n roundNumber: response.state.roundNumber,\n lastUpdated: Date.now(),\n };\n } catch (error) {\n logger.warn('Aggregator', 'getTokenState failed', error);\n return null;\n }\n }\n\n async getCurrentRound(): Promise<number> {\n if (this.aggregatorClient) {\n const blockHeight = await this.aggregatorClient.getBlockHeight();\n return Number(blockHeight);\n }\n return 0;\n }\n\n async mint(params: MintParams): Promise<MintResult> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcMintResponse>('mint', {\n coinId: params.coinId,\n amount: params.amount,\n recipientAddress: params.recipientAddress,\n recipientPubkey: params.recipientPubkey,\n });\n\n return {\n success: true,\n requestId: response.requestId,\n tokenId: response.tokenId,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\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: RPC\n // ===========================================================================\n\n private async rpcCall<T>(method: string, params: unknown): Promise<T> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(this.config.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method,\n params,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new SphereError(`HTTP ${response.status}: ${response.statusText}`, 'AGGREGATOR_ERROR');\n }\n\n const result = await response.json();\n\n if (result.error) {\n throw new SphereError(result.error.message ?? 'RPC error', 'AGGREGATOR_ERROR');\n }\n\n return (result.result ?? {}) as T;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('UnicityAggregatorProvider not connected', 'NOT_INITIALIZED');\n }\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// 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_TESTNET, TRUSTBASE_MAINNET, TRUSTBASE_DEV } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\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 case 'testnet':\n return TRUSTBASE_TESTNET;\n case 'dev':\n return TRUSTBASE_DEV;\n default:\n return TRUSTBASE_TESTNET;\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';\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 constructor(networkOrUrl: NetworkType | string = 'testnet') {\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n super('testnet');\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(networkOrUrl?: NetworkType | string): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl);\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 return new UnicityAggregatorProvider({\n ...restConfig,\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network ?? 'testnet'),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\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 * IPFS Error Classification\n * Categorizes errors for proper handling (e.g., NOT_FOUND should not trigger circuit breaker)\n */\n\n// =============================================================================\n// Error Categories\n// =============================================================================\n\nexport type IpfsErrorCategory =\n | 'NOT_FOUND' // IPNS record never published (expected for new wallets)\n | 'NETWORK_ERROR' // Connectivity / server issues\n | 'TIMEOUT' // Request timed out\n | 'GATEWAY_ERROR' // Gateway returned error (5xx, etc.)\n | 'INVALID_RESPONSE' // Response parsing failed\n | 'CID_MISMATCH' // Content hash doesn't match CID\n | 'SEQUENCE_DOWNGRADE'; // Remote sequence < local (stale data)\n\n// =============================================================================\n// Error Class\n// =============================================================================\n\nexport class IpfsError extends Error {\n readonly category: IpfsErrorCategory;\n readonly gateway?: string;\n readonly cause?: Error;\n\n constructor(\n message: string,\n category: IpfsErrorCategory,\n gateway?: string,\n cause?: Error,\n ) {\n super(message);\n this.name = 'IpfsError';\n this.category = category;\n this.gateway = gateway;\n this.cause = cause;\n }\n\n /** Whether this error should trigger the circuit breaker */\n get shouldTriggerCircuitBreaker(): boolean {\n // NOT_FOUND is expected for new wallets, don't penalize the gateway\n return this.category !== 'NOT_FOUND' && this.category !== 'SEQUENCE_DOWNGRADE';\n }\n}\n\n// =============================================================================\n// Error Classification Helpers\n// =============================================================================\n\n/**\n * Classify a fetch exception into an IpfsErrorCategory\n */\nexport function classifyFetchError(error: unknown): IpfsErrorCategory {\n if (error instanceof DOMException && error.name === 'AbortError') {\n return 'TIMEOUT';\n }\n if (error instanceof TypeError) {\n // TypeError typically means network failure (DNS, connection refused, etc.)\n return 'NETWORK_ERROR';\n }\n if (error instanceof Error && error.name === 'TimeoutError') {\n return 'TIMEOUT';\n }\n return 'NETWORK_ERROR';\n}\n\n/**\n * Classify an HTTP status code into an IpfsErrorCategory\n * @param status - HTTP status code\n * @param responseBody - Optional response body for additional context\n */\nexport function classifyHttpStatus(\n status: number,\n responseBody?: string,\n): IpfsErrorCategory {\n if (status === 404) {\n return 'NOT_FOUND';\n }\n\n if (status === 500 && responseBody) {\n // Kubo returns 500 with \"routing: not found\" for IPNS records that don't exist\n if (/routing:\\s*not\\s*found/i.test(responseBody)) {\n return 'NOT_FOUND';\n }\n }\n\n if (status >= 500) {\n return 'GATEWAY_ERROR';\n }\n\n if (status >= 400) {\n return 'GATEWAY_ERROR';\n }\n\n return 'GATEWAY_ERROR';\n}\n","/**\n * IPFS State Persistence\n * Interface and in-memory implementation for persisting IPFS/IPNS state\n */\n\nimport type { IpfsStatePersistence, IpfsPersistedState } from './ipfs-types';\n\n// Re-export for convenience\nexport type { IpfsStatePersistence, IpfsPersistedState };\n\n// =============================================================================\n// In-Memory Implementation (for testing)\n// =============================================================================\n\nexport class InMemoryIpfsStatePersistence implements IpfsStatePersistence {\n private readonly states = new Map<string, IpfsPersistedState>();\n\n async load(ipnsName: string): Promise<IpfsPersistedState | null> {\n return this.states.get(ipnsName) ?? null;\n }\n\n async save(ipnsName: string, state: IpfsPersistedState): Promise<void> {\n this.states.set(ipnsName, { ...state });\n }\n\n async clear(ipnsName: string): Promise<void> {\n this.states.delete(ipnsName);\n }\n}\n","/**\n * IPNS Key Derivation\n * Deterministic IPNS identity from secp256k1 private key using HKDF\n *\n * Derivation path:\n * secp256k1 privateKey (hex)\n * -> HKDF(sha256, key, info=\"ipfs-storage-ed25519-v1\", 32 bytes)\n * -> Ed25519 key pair\n * -> libp2p PeerId\n * -> IPNS name (e.g., \"12D3KooW...\")\n */\n\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { hexToBytes } from '../../../core/crypto';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * HKDF info string for deriving Ed25519 keys from wallet keys.\n * Must match sphere webapp for cross-device compatibility.\n */\nexport const IPNS_HKDF_INFO = 'ipfs-storage-ed25519-v1';\n\n// =============================================================================\n// Dynamic Import Cache\n// =============================================================================\n\nlet libp2pCryptoModule: typeof import('@libp2p/crypto/keys') | null = null;\nlet libp2pPeerIdModule: typeof import('@libp2p/peer-id') | null = null;\n\nasync function loadLibp2pModules() {\n if (!libp2pCryptoModule) {\n [libp2pCryptoModule, libp2pPeerIdModule] = await Promise.all([\n import('@libp2p/crypto/keys'),\n import('@libp2p/peer-id'),\n ]);\n }\n return {\n generateKeyPairFromSeed: libp2pCryptoModule!.generateKeyPairFromSeed,\n peerIdFromPrivateKey: libp2pPeerIdModule!.peerIdFromPrivateKey,\n };\n}\n\n// =============================================================================\n// Key Derivation\n// =============================================================================\n\n/**\n * Derive Ed25519 key material from a secp256k1 private key using HKDF.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @param info - HKDF info string (default: IPNS_HKDF_INFO)\n * @returns 32-byte derived key material suitable for Ed25519 seed\n */\nexport function deriveEd25519KeyMaterial(\n privateKeyHex: string,\n info: string = IPNS_HKDF_INFO,\n): Uint8Array {\n const walletSecret = hexToBytes(privateKeyHex);\n const infoBytes = new TextEncoder().encode(info);\n return hkdf(sha256, walletSecret, undefined, infoBytes, 32);\n}\n\n/**\n * Derive full IPNS identity (key pair + IPNS name) from a secp256k1 private key.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @returns Object with keyPair and ipnsName\n */\nexport async function deriveIpnsIdentity(\n privateKeyHex: string,\n): Promise<{ keyPair: unknown; ipnsName: string }> {\n const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadLibp2pModules();\n\n const derivedKey = deriveEd25519KeyMaterial(privateKeyHex);\n const keyPair = await generateKeyPairFromSeed('Ed25519', derivedKey);\n const peerId = peerIdFromPrivateKey(keyPair);\n\n return {\n keyPair,\n ipnsName: peerId.toString(),\n };\n}\n\n/**\n * Derive just the IPNS name from a secp256k1 private key.\n * Lighter than deriveIpnsIdentity when you don't need the key pair.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @returns IPNS name string (e.g., \"12D3KooW...\")\n */\nexport async function deriveIpnsName(\n privateKeyHex: string,\n): Promise<string> {\n const { ipnsName } = await deriveIpnsIdentity(privateKeyHex);\n return ipnsName;\n}\n","/**\n * IPNS Record Manager\n * Creates, marshals, and parses IPNS records for publishing and resolution\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default IPNS record lifetime: 99 years (effectively permanent) */\nconst DEFAULT_LIFETIME_MS = 99 * 365 * 24 * 60 * 60 * 1000;\n\n// =============================================================================\n// Dynamic Import Cache\n// =============================================================================\n\nlet ipnsModule: {\n createIPNSRecord: typeof import('ipns')['createIPNSRecord'];\n marshalIPNSRecord: typeof import('ipns')['marshalIPNSRecord'];\n unmarshalIPNSRecord: typeof import('ipns')['unmarshalIPNSRecord'];\n} | null = null;\n\nasync function loadIpnsModule() {\n if (!ipnsModule) {\n const mod = await import('ipns');\n ipnsModule = {\n createIPNSRecord: mod.createIPNSRecord,\n marshalIPNSRecord: mod.marshalIPNSRecord,\n unmarshalIPNSRecord: mod.unmarshalIPNSRecord,\n };\n }\n return ipnsModule;\n}\n\n// =============================================================================\n// Record Creation\n// =============================================================================\n\n/**\n * Create a signed IPNS record and marshal it to bytes.\n *\n * @param keyPair - Ed25519 private key (from deriveIpnsIdentity)\n * @param cid - CID to point the IPNS record at\n * @param sequenceNumber - Monotonically increasing sequence number\n * @param lifetimeMs - Record validity period (default: 99 years)\n * @returns Marshalled IPNS record bytes\n */\nexport async function createSignedRecord(\n keyPair: unknown,\n cid: string,\n sequenceNumber: bigint,\n lifetimeMs: number = DEFAULT_LIFETIME_MS,\n): Promise<Uint8Array> {\n const { createIPNSRecord, marshalIPNSRecord } = await loadIpnsModule();\n\n const record = await createIPNSRecord(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n keyPair as any,\n `/ipfs/${cid}`,\n sequenceNumber,\n lifetimeMs,\n );\n\n return marshalIPNSRecord(record);\n}\n\n// =============================================================================\n// Record Parsing\n// =============================================================================\n\n/**\n * Parse a routing API response (NDJSON) to extract CID and sequence number.\n * The routing API returns newline-delimited JSON with an \"Extra\" field\n * containing a base64-encoded marshalled IPNS record.\n *\n * @param responseText - Raw text from the routing API response\n * @returns Parsed result with cid, sequence, and recordData, or null\n */\nexport async function parseRoutingApiResponse(\n responseText: string,\n): Promise<{ cid: string; sequence: bigint; recordData: Uint8Array } | null> {\n const { unmarshalIPNSRecord } = await loadIpnsModule();\n\n const lines = responseText.trim().split('\\n');\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n try {\n const obj = JSON.parse(line);\n\n if (obj.Extra) {\n const recordData = base64ToUint8Array(obj.Extra);\n const record = unmarshalIPNSRecord(recordData);\n\n // Extract CID from the value field\n const valueBytes = typeof record.value === 'string'\n ? new TextEncoder().encode(record.value)\n : record.value as Uint8Array;\n const valueStr = new TextDecoder().decode(valueBytes);\n const cidMatch = valueStr.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n\n if (cidMatch) {\n return {\n cid: cidMatch[1],\n sequence: record.sequence,\n recordData,\n };\n }\n }\n } catch {\n // Skip malformed lines\n continue;\n }\n }\n\n return null;\n}\n\n/**\n * Verify that a new sequence number represents a valid progression.\n *\n * @param newSeq - Proposed new sequence number\n * @param lastKnownSeq - Last known sequence number\n * @returns true if the new sequence is valid (greater than last known)\n */\nexport function verifySequenceProgression(\n newSeq: bigint,\n lastKnownSeq: bigint,\n): boolean {\n return newSeq > lastKnownSeq;\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * IPFS Multi-Tier Cache\n * Caches IPNS records, content by CID, and tracks gateway failures\n */\n\nimport type { IpnsGatewayResult } from './ipfs-types';\nimport type { TxfStorageDataBase } from '../../../storage';\n\n// =============================================================================\n// Cache Entry Type\n// =============================================================================\n\ninterface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\n// =============================================================================\n// Default Config\n// =============================================================================\n\nconst DEFAULT_IPNS_TTL_MS = 60_000; // 60s for IPNS records\nconst DEFAULT_FAILURE_COOLDOWN_MS = 60_000; // 60s circuit breaker cooldown\nconst DEFAULT_FAILURE_THRESHOLD = 3; // 3 consecutive failures\nconst DEFAULT_KNOWN_FRESH_WINDOW_MS = 30_000; // 30s after publish\n\n// =============================================================================\n// Cache Implementation\n// =============================================================================\n\nexport interface IpfsCacheConfig {\n ipnsTtlMs?: number;\n failureCooldownMs?: number;\n failureThreshold?: number;\n knownFreshWindowMs?: number;\n}\n\nexport class IpfsCache {\n private readonly ipnsRecords = new Map<string, CacheEntry<IpnsGatewayResult>>();\n private readonly content = new Map<string, CacheEntry<TxfStorageDataBase>>();\n private readonly gatewayFailures = new Map<string, { count: number; lastFailure: number }>();\n private readonly knownFreshTimestamps = new Map<string, number>();\n\n private readonly ipnsTtlMs: number;\n private readonly failureCooldownMs: number;\n private readonly failureThreshold: number;\n private readonly knownFreshWindowMs: number;\n\n constructor(config?: IpfsCacheConfig) {\n this.ipnsTtlMs = config?.ipnsTtlMs ?? DEFAULT_IPNS_TTL_MS;\n this.failureCooldownMs = config?.failureCooldownMs ?? DEFAULT_FAILURE_COOLDOWN_MS;\n this.failureThreshold = config?.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;\n this.knownFreshWindowMs = config?.knownFreshWindowMs ?? DEFAULT_KNOWN_FRESH_WINDOW_MS;\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Record Cache (60s TTL)\n // ---------------------------------------------------------------------------\n\n getIpnsRecord(ipnsName: string): IpnsGatewayResult | null {\n const entry = this.ipnsRecords.get(ipnsName);\n if (!entry) return null;\n\n if (Date.now() - entry.timestamp > this.ipnsTtlMs) {\n this.ipnsRecords.delete(ipnsName);\n return null;\n }\n\n return entry.data;\n }\n\n /**\n * Get cached IPNS record ignoring TTL (for known-fresh optimization).\n */\n getIpnsRecordIgnoreTtl(ipnsName: string): IpnsGatewayResult | null {\n const entry = this.ipnsRecords.get(ipnsName);\n return entry?.data ?? null;\n }\n\n setIpnsRecord(ipnsName: string, result: IpnsGatewayResult): void {\n this.ipnsRecords.set(ipnsName, {\n data: result,\n timestamp: Date.now(),\n });\n }\n\n invalidateIpns(ipnsName: string): void {\n this.ipnsRecords.delete(ipnsName);\n }\n\n // ---------------------------------------------------------------------------\n // Content Cache (infinite TTL - content is immutable by CID)\n // ---------------------------------------------------------------------------\n\n getContent(cid: string): TxfStorageDataBase | null {\n const entry = this.content.get(cid);\n return entry?.data ?? null;\n }\n\n setContent(cid: string, data: TxfStorageDataBase): void {\n this.content.set(cid, {\n data,\n timestamp: Date.now(),\n });\n }\n\n // ---------------------------------------------------------------------------\n // Gateway Failure Tracking (Circuit Breaker)\n // ---------------------------------------------------------------------------\n\n /**\n * Record a gateway failure. After threshold consecutive failures,\n * the gateway enters cooldown and is considered unhealthy.\n */\n recordGatewayFailure(gateway: string): void {\n const existing = this.gatewayFailures.get(gateway);\n this.gatewayFailures.set(gateway, {\n count: (existing?.count ?? 0) + 1,\n lastFailure: Date.now(),\n });\n }\n\n /** Reset failure count for a gateway (on successful request) */\n recordGatewaySuccess(gateway: string): void {\n this.gatewayFailures.delete(gateway);\n }\n\n /**\n * Check if a gateway is currently in circuit breaker cooldown.\n * A gateway is considered unhealthy if it has had >= threshold\n * consecutive failures and the cooldown period hasn't elapsed.\n */\n isGatewayInCooldown(gateway: string): boolean {\n const failure = this.gatewayFailures.get(gateway);\n if (!failure) return false;\n\n if (failure.count < this.failureThreshold) return false;\n\n const elapsed = Date.now() - failure.lastFailure;\n if (elapsed >= this.failureCooldownMs) {\n // Cooldown expired, reset\n this.gatewayFailures.delete(gateway);\n return false;\n }\n\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Known-Fresh Flag (FAST mode optimization)\n // ---------------------------------------------------------------------------\n\n /**\n * Mark IPNS cache as \"known-fresh\" (after local publish or push notification).\n * Within the fresh window, we can skip network resolution.\n */\n markIpnsFresh(ipnsName: string): void {\n this.knownFreshTimestamps.set(ipnsName, Date.now());\n }\n\n /**\n * Check if the cache is known-fresh (within the fresh window).\n */\n isIpnsKnownFresh(ipnsName: string): boolean {\n const timestamp = this.knownFreshTimestamps.get(ipnsName);\n if (!timestamp) return false;\n\n if (Date.now() - timestamp > this.knownFreshWindowMs) {\n this.knownFreshTimestamps.delete(ipnsName);\n return false;\n }\n\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Cache Management\n // ---------------------------------------------------------------------------\n\n clear(): void {\n this.ipnsRecords.clear();\n this.content.clear();\n this.gatewayFailures.clear();\n this.knownFreshTimestamps.clear();\n }\n}\n","/**\n * IPFS HTTP Client\n * Fetch-based HTTP API client for IPFS gateway operations.\n * Works in both browser and Node.js (native fetch available since Node 18+).\n */\n\nimport { logger } from '../../../core/logger';\nimport type {\n IpnsGatewayResult,\n IpnsProgressiveResult,\n IpnsPublishResult,\n GatewayHealthResult,\n} from './ipfs-types';\nimport type { TxfStorageDataBase } from '../../../storage';\nimport {\n IpfsError,\n classifyFetchError,\n classifyHttpStatus,\n} from './ipfs-error-types';\nimport { IpfsCache } from './ipfs-cache';\nimport { parseRoutingApiResponse } from './ipns-record-manager';\n\n// =============================================================================\n// Default Timeouts\n// =============================================================================\n\nconst DEFAULT_CONNECTIVITY_TIMEOUT_MS = 5000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 15000;\nconst DEFAULT_RESOLVE_TIMEOUT_MS = 10000;\nconst DEFAULT_PUBLISH_TIMEOUT_MS = 30000;\nconst DEFAULT_GATEWAY_PATH_TIMEOUT_MS = 3000;\nconst DEFAULT_ROUTING_API_TIMEOUT_MS = 2000;\n\n// =============================================================================\n// HTTP Client\n// =============================================================================\n\nexport interface IpfsHttpClientConfig {\n gateways: string[];\n fetchTimeoutMs?: number;\n resolveTimeoutMs?: number;\n publishTimeoutMs?: number;\n connectivityTimeoutMs?: number;\n debug?: boolean;\n}\n\nexport class IpfsHttpClient {\n private readonly gateways: string[];\n private readonly fetchTimeoutMs: number;\n private readonly resolveTimeoutMs: number;\n private readonly publishTimeoutMs: number;\n private readonly connectivityTimeoutMs: number;\n private readonly debug: boolean;\n private readonly cache: IpfsCache;\n\n constructor(config: IpfsHttpClientConfig, cache: IpfsCache) {\n this.gateways = config.gateways;\n this.fetchTimeoutMs = config.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.resolveTimeoutMs = config.resolveTimeoutMs ?? DEFAULT_RESOLVE_TIMEOUT_MS;\n this.publishTimeoutMs = config.publishTimeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS;\n this.connectivityTimeoutMs = config.connectivityTimeoutMs ?? DEFAULT_CONNECTIVITY_TIMEOUT_MS;\n this.debug = config.debug ?? false;\n this.cache = cache;\n }\n\n // ---------------------------------------------------------------------------\n // Public Accessors\n // ---------------------------------------------------------------------------\n\n /**\n * Get configured gateway URLs.\n */\n getGateways(): string[] {\n return [...this.gateways];\n }\n\n // ---------------------------------------------------------------------------\n // Gateway Health\n // ---------------------------------------------------------------------------\n\n /**\n * Test connectivity to a single gateway.\n */\n async testConnectivity(gateway: string): Promise<GatewayHealthResult> {\n const start = Date.now();\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/version`,\n this.connectivityTimeoutMs,\n { method: 'POST' },\n );\n\n if (!response.ok) {\n return { gateway, healthy: false, error: `HTTP ${response.status}` };\n }\n\n return {\n gateway,\n healthy: true,\n responseTimeMs: Date.now() - start,\n };\n } catch (error) {\n return {\n gateway,\n healthy: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Find healthy gateways from the configured list.\n */\n async findHealthyGateways(): Promise<string[]> {\n const results = await Promise.allSettled(\n this.gateways.map((gw) => this.testConnectivity(gw)),\n );\n\n return results\n .filter((r): r is PromiseFulfilledResult<GatewayHealthResult> =>\n r.status === 'fulfilled' && r.value.healthy)\n .map((r) => r.value.gateway);\n }\n\n /**\n * Get gateways that are not in circuit breaker cooldown.\n */\n getAvailableGateways(): string[] {\n return this.gateways.filter((gw) => !this.cache.isGatewayInCooldown(gw));\n }\n\n // ---------------------------------------------------------------------------\n // Content Upload\n // ---------------------------------------------------------------------------\n\n /**\n * Upload JSON content to IPFS.\n * Tries all gateways in parallel, returns first success.\n */\n async upload(data: unknown, gateways?: string[]): Promise<{ cid: string }> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n throw new IpfsError('No gateways available for upload', 'NETWORK_ERROR');\n }\n\n const jsonBytes = new TextEncoder().encode(JSON.stringify(data));\n\n const promises = targets.map(async (gateway) => {\n try {\n const formData = new FormData();\n formData.append('file', new Blob([jsonBytes], { type: 'application/json' }), 'data.json');\n\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/add?pin=true&cid-version=1`,\n this.publishTimeoutMs,\n { method: 'POST', body: formData },\n );\n\n if (!response.ok) {\n throw new IpfsError(\n `Upload failed: HTTP ${response.status}`,\n classifyHttpStatus(response.status),\n gateway,\n );\n }\n\n const result = await response.json();\n this.cache.recordGatewaySuccess(gateway);\n this.log(`Uploaded to ${gateway}: CID=${result.Hash}`);\n return { cid: result.Hash as string, gateway };\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n throw error;\n }\n });\n\n try {\n const result = await Promise.any(promises);\n return { cid: result.cid };\n } catch (error) {\n if (error instanceof AggregateError) {\n throw new IpfsError(\n `Upload failed on all gateways: ${error.errors.map(e => e.message).join('; ')}`,\n 'NETWORK_ERROR',\n );\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Content Fetch\n // ---------------------------------------------------------------------------\n\n /**\n * Fetch content by CID from IPFS gateways.\n * Checks content cache first. Races all gateways for fastest response.\n */\n async fetchContent<T extends TxfStorageDataBase>(\n cid: string,\n gateways?: string[],\n ): Promise<T> {\n // Check content cache (infinite TTL for immutable content)\n const cached = this.cache.getContent(cid);\n if (cached) {\n this.log(`Content cache hit for CID=${cid}`);\n return cached as T;\n }\n\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n throw new IpfsError('No gateways available for fetch', 'NETWORK_ERROR');\n }\n\n const promises = targets.map(async (gateway) => {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/ipfs/${cid}`,\n this.fetchTimeoutMs,\n { headers: { Accept: 'application/octet-stream' } },\n );\n\n if (!response.ok) {\n const body = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n throw new IpfsError(\n `Fetch failed: HTTP ${response.status}`,\n classifyHttpStatus(response.status, body),\n gateway,\n );\n }\n\n const data = await response.json() as T;\n this.cache.recordGatewaySuccess(gateway);\n this.cache.setContent(cid, data);\n this.log(`Fetched from ${gateway}: CID=${cid}`);\n return data;\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n throw error;\n }\n });\n\n try {\n return await Promise.any(promises);\n } catch (error) {\n if (error instanceof AggregateError) {\n throw new IpfsError(\n `Fetch failed on all gateways for CID=${cid}`,\n 'NETWORK_ERROR',\n );\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve IPNS via Routing API (returns record with sequence number).\n * POST /api/v0/routing/get?arg=/ipns/{name}\n */\n async resolveIpnsViaRoutingApi(\n gateway: string,\n ipnsName: string,\n timeoutMs: number = DEFAULT_ROUTING_API_TIMEOUT_MS,\n ): Promise<IpnsGatewayResult | null> {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/routing/get?arg=/ipns/${ipnsName}`,\n timeoutMs,\n { method: 'POST' },\n );\n\n if (!response.ok) {\n const body = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n const category = classifyHttpStatus(response.status, body);\n if (category === 'NOT_FOUND') return null;\n throw new IpfsError(`Routing API: HTTP ${response.status}`, category, gateway);\n }\n\n const text = await response.text();\n const parsed = await parseRoutingApiResponse(text);\n\n if (!parsed) return null;\n\n this.cache.recordGatewaySuccess(gateway);\n return {\n cid: parsed.cid,\n sequence: parsed.sequence,\n gateway,\n recordData: parsed.recordData,\n };\n } catch (error) {\n if (error instanceof IpfsError) throw error;\n const category = classifyFetchError(error);\n if (category !== 'NOT_FOUND') {\n this.cache.recordGatewayFailure(gateway);\n }\n return null;\n }\n }\n\n /**\n * Resolve IPNS via gateway path (simpler, no sequence number).\n * GET /ipns/{name}?format=dag-json\n */\n async resolveIpnsViaGatewayPath(\n gateway: string,\n ipnsName: string,\n timeoutMs: number = DEFAULT_GATEWAY_PATH_TIMEOUT_MS,\n ): Promise<{ cid: string; content?: unknown } | null> {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/ipns/${ipnsName}`,\n timeoutMs,\n { headers: { Accept: 'application/json' } },\n );\n\n if (!response.ok) return null;\n\n const content = await response.json();\n const cidHeader = response.headers.get('X-Ipfs-Path');\n if (cidHeader) {\n const match = cidHeader.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (match) {\n this.cache.recordGatewaySuccess(gateway);\n return { cid: match[1], content };\n }\n }\n\n return { cid: '', content };\n } catch (err) {\n logger.debug('IPFS-HTTP', 'IPNS gateway resolution failed', err);\n return null;\n }\n }\n\n /**\n * Progressive IPNS resolution across all gateways.\n * Queries all gateways in parallel, returns highest sequence number.\n */\n async resolveIpns(\n ipnsName: string,\n gateways?: string[],\n ): Promise<IpnsProgressiveResult> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n return { best: null, allResults: [], respondedCount: 0, totalGateways: 0 };\n }\n\n const results: IpnsGatewayResult[] = [];\n let respondedCount = 0;\n\n const promises = targets.map(async (gateway) => {\n const result = await this.resolveIpnsViaRoutingApi(\n gateway,\n ipnsName,\n this.resolveTimeoutMs,\n );\n if (result) results.push(result);\n respondedCount++;\n return result;\n });\n\n // Wait for all to complete (with overall timeout)\n await Promise.race([\n Promise.allSettled(promises),\n new Promise<void>((resolve) =>\n setTimeout(resolve, this.resolveTimeoutMs + 1000)),\n ]);\n\n // Find best result (highest sequence)\n let best: IpnsGatewayResult | null = null;\n for (const result of results) {\n if (!best || result.sequence > best.sequence) {\n best = result;\n }\n }\n\n if (best) {\n this.cache.setIpnsRecord(ipnsName, best);\n }\n\n return {\n best,\n allResults: results,\n respondedCount,\n totalGateways: targets.length,\n };\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Publishing\n // ---------------------------------------------------------------------------\n\n /**\n * Publish IPNS record to a single gateway via routing API.\n */\n async publishIpnsViaRoutingApi(\n gateway: string,\n ipnsName: string,\n marshalledRecord: Uint8Array,\n timeoutMs: number = DEFAULT_PUBLISH_TIMEOUT_MS,\n ): Promise<boolean> {\n try {\n const formData = new FormData();\n formData.append(\n 'file',\n new Blob([new Uint8Array(marshalledRecord)]),\n 'record',\n );\n\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/routing/put?arg=/ipns/${ipnsName}&allow-offline=true`,\n timeoutMs,\n { method: 'POST', body: formData },\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n throw new IpfsError(\n `IPNS publish: HTTP ${response.status}: ${errorText.slice(0, 100)}`,\n classifyHttpStatus(response.status, errorText),\n gateway,\n );\n }\n\n this.cache.recordGatewaySuccess(gateway);\n this.log(`IPNS published to ${gateway}: ${ipnsName}`);\n return true;\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n this.log(`IPNS publish to ${gateway} failed: ${error}`);\n return false;\n }\n }\n\n /**\n * Publish IPNS record to all gateways in parallel.\n */\n async publishIpns(\n ipnsName: string,\n marshalledRecord: Uint8Array,\n gateways?: string[],\n ): Promise<IpnsPublishResult> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n return { success: false, error: 'No gateways available' };\n }\n\n const results = await Promise.allSettled(\n targets.map((gw) =>\n this.publishIpnsViaRoutingApi(gw, ipnsName, marshalledRecord, this.publishTimeoutMs)),\n );\n\n const successfulGateways: string[] = [];\n results.forEach((result, index) => {\n if (result.status === 'fulfilled' && result.value) {\n successfulGateways.push(targets[index]);\n }\n });\n\n return {\n success: successfulGateways.length > 0,\n ipnsName,\n successfulGateways,\n error: successfulGateways.length === 0 ? 'All gateways failed' : undefined,\n };\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Verification\n // ---------------------------------------------------------------------------\n\n /**\n * Verify IPNS record persistence after publishing.\n * Retries resolution to confirm the record was accepted.\n */\n async verifyIpnsRecord(\n ipnsName: string,\n expectedSeq: bigint,\n expectedCid: string,\n retries: number = 3,\n delayMs: number = 1000,\n ): Promise<boolean> {\n for (let i = 0; i < retries; i++) {\n if (i > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n const { best } = await this.resolveIpns(ipnsName);\n if (best && best.sequence >= expectedSeq && best.cid === expectedCid) {\n return true;\n }\n }\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number,\n options?: RequestInit,\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n return await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private log(message: string): void {\n logger.debug('IPFS-HTTP', message);\n }\n}\n","/**\n * TXF Data Merge / Conflict Resolution\n * Merges local and remote TXF storage data with proper conflict handling\n */\n\nimport type { TxfStorageDataBase, TxfTombstone } from '../../../storage';\nimport type { MergeResult } from './ipfs-types';\n\n// =============================================================================\n// Merge Logic\n// =============================================================================\n\n/**\n * Merge local and remote TXF data.\n *\n * Rules:\n * 1. Meta: Higher version wins as base; increment by 1\n * 2. Tombstones: Union by composite key (tokenId, stateHash)\n * 3. Token entries: present in only one source -> add; both -> local wins\n * 4. Tombstone filtering: exclude tokens present in merged tombstones\n * 5. Outbox/Sent: Union with dedup by id/tokenId\n */\nexport function mergeTxfData<T extends TxfStorageDataBase>(\n local: T,\n remote: T,\n): MergeResult<T> {\n let added = 0;\n let removed = 0;\n let conflicts = 0;\n\n // 1. Merge meta — use higher version as base, increment\n const localVersion = local._meta?.version ?? 0;\n const remoteVersion = remote._meta?.version ?? 0;\n const baseMeta = localVersion >= remoteVersion ? local._meta : remote._meta;\n const mergedMeta = {\n ...baseMeta,\n version: Math.max(localVersion, remoteVersion) + 1,\n updatedAt: Date.now(),\n };\n\n // 2. Merge tombstones — union by composite key (tokenId + stateHash)\n const mergedTombstones = mergeTombstones(\n local._tombstones ?? [],\n remote._tombstones ?? [],\n );\n const tombstoneKeys = new Set(\n mergedTombstones.map((t) => `${t.tokenId}:${t.stateHash}`),\n );\n\n // 3. Merge token entries\n const localTokenKeys = getTokenKeys(local);\n const remoteTokenKeys = getTokenKeys(remote);\n const allTokenKeys = new Set([...localTokenKeys, ...remoteTokenKeys]);\n\n const mergedTokens: Record<string, unknown> = {};\n\n for (const key of allTokenKeys) {\n const tokenId = key.startsWith('_') ? key.slice(1) : key;\n const localToken = (local as Record<string, unknown>)[key];\n const remoteToken = (remote as Record<string, unknown>)[key];\n\n // Check tombstone filter\n if (isTokenTombstoned(tokenId, localToken, remoteToken, tombstoneKeys)) {\n if (localTokenKeys.has(key)) removed++;\n continue;\n }\n\n if (localToken && !remoteToken) {\n // Only in local\n mergedTokens[key] = localToken;\n } else if (!localToken && remoteToken) {\n // Only in remote\n mergedTokens[key] = remoteToken;\n added++;\n } else if (localToken && remoteToken) {\n // In both — local wins (with conflict count)\n mergedTokens[key] = localToken;\n conflicts++;\n }\n }\n\n // 4. Merge outbox — union with dedup by id\n const mergedOutbox = mergeArrayById(\n local._outbox ?? [],\n remote._outbox ?? [],\n 'id',\n );\n\n // 5. Merge sent — union with dedup by tokenId\n const mergedSent = mergeArrayById(\n local._sent ?? [],\n remote._sent ?? [],\n 'tokenId',\n );\n\n // 6. Merge invalid — union with dedup by tokenId\n const mergedInvalid = mergeArrayById(\n local._invalid ?? [],\n remote._invalid ?? [],\n 'tokenId',\n );\n\n // 7. Merge nametags — union by name, local wins on conflict\n type NametagEntry = { name: string; [key: string]: unknown };\n const localNametags = (local._nametags ?? []) as NametagEntry[];\n const remoteNametags = (remote._nametags ?? []) as NametagEntry[];\n const mergedNametags = mergeNametagsByName(localNametags, remoteNametags);\n\n // 8. Merge history — union with dedup by dedupKey (same pattern as _outbox/_sent/_invalid)\n const localHistory = (local._history ?? []) as Array<{ dedupKey: string }>;\n const remoteHistory = (remote._history ?? []) as Array<{ dedupKey: string }>;\n const mergedHistory = mergeArrayById(localHistory, remoteHistory, 'dedupKey');\n\n // Build merged result\n const merged = {\n _meta: mergedMeta,\n _tombstones: mergedTombstones.length > 0 ? mergedTombstones : undefined,\n _nametags: mergedNametags.length > 0 ? mergedNametags : undefined,\n _outbox: mergedOutbox.length > 0 ? mergedOutbox : undefined,\n _sent: mergedSent.length > 0 ? mergedSent : undefined,\n _invalid: mergedInvalid.length > 0 ? mergedInvalid : undefined,\n _history: mergedHistory.length > 0 ? mergedHistory : undefined,\n ...mergedTokens,\n } as unknown as T;\n\n return { merged, added, removed, conflicts };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Merge tombstone arrays by composite key (tokenId + stateHash).\n * On duplicates, keep the one with the newer timestamp.\n */\nfunction mergeTombstones(\n local: TxfTombstone[],\n remote: TxfTombstone[],\n): TxfTombstone[] {\n const merged = new Map<string, TxfTombstone>();\n\n for (const tombstone of [...local, ...remote]) {\n const key = `${tombstone.tokenId}:${tombstone.stateHash}`;\n const existing = merged.get(key);\n if (!existing || tombstone.timestamp > existing.timestamp) {\n merged.set(key, tombstone);\n }\n }\n\n return Array.from(merged.values());\n}\n\n/**\n * Get all token entry keys from TXF data.\n * Token keys start with '_' but are not meta fields.\n */\nfunction getTokenKeys(data: TxfStorageDataBase): Set<string> {\n const reservedKeys = new Set([\n '_meta', '_tombstones', '_outbox', '_sent', '_invalid',\n '_nametag', '_nametags', '_mintOutbox', '_invalidatedNametags', '_integrity',\n '_history',\n ]);\n const keys = new Set<string>();\n\n for (const key of Object.keys(data)) {\n if (reservedKeys.has(key)) continue;\n // Exclude archived and forked entries from token merge\n if (key.startsWith('archived-') || key.startsWith('_forked_')) continue;\n keys.add(key);\n }\n\n return keys;\n}\n\n/**\n * Check if a token should be filtered by tombstones.\n */\nfunction isTokenTombstoned(\n tokenId: string,\n localToken: unknown,\n remoteToken: unknown,\n tombstoneKeys: Set<string>,\n): boolean {\n // Check if any variant of this token is tombstoned\n // We check generic tokenId matching since we may not have the stateHash here\n for (const key of tombstoneKeys) {\n if (key.startsWith(`${tokenId}:`)) {\n return true;\n }\n }\n // Keep token if not tombstoned\n void localToken;\n void remoteToken;\n return false;\n}\n\n/**\n * Merge nametag arrays by name, deduplicating.\n * On duplicates, keep the local entry.\n */\nfunction mergeNametagsByName(\n local: Array<{ name: string; [key: string]: unknown }>,\n remote: Array<{ name: string; [key: string]: unknown }>,\n): Array<{ name: string; [key: string]: unknown }> {\n const seen = new Map<string, { name: string; [key: string]: unknown }>();\n\n for (const item of local) {\n if (item.name) seen.set(item.name, item);\n }\n for (const item of remote) {\n if (item.name && !seen.has(item.name)) {\n seen.set(item.name, item);\n }\n }\n\n return Array.from(seen.values());\n}\n\n/**\n * Merge arrays by a key field, deduplicating.\n * On duplicates, keep the one from the first array (local).\n */\nfunction mergeArrayById<T>(\n local: T[],\n remote: T[],\n idField: keyof T,\n): T[] {\n const seen = new Map<unknown, T>();\n\n for (const item of local) {\n const id = item[idField];\n if (id !== undefined) {\n seen.set(id, item);\n }\n }\n\n for (const item of remote) {\n const id = item[idField];\n if (id !== undefined && !seen.has(id)) {\n seen.set(id, item);\n }\n }\n\n return Array.from(seen.values());\n}\n","/**\n * WebSocket client for IPNS subscription updates.\n * Connects to IPFS sidecar and receives push notifications for IPNS changes.\n *\n * Architecture:\n * - Connects to /ws/ipns endpoint on IPFS gateway\n * - Clients subscribe to specific IPNS names\n * - Server pushes updates when IPNS records change\n * - Auto-reconnects on connection loss with exponential backoff\n * - Falls back to polling when WebSocket is unavailable\n *\n * Ported from Sphere webapp's IpnsSubscriptionClient.\n */\n\nimport { logger } from '../../../core/logger';\nimport type { IWebSocket, WebSocketFactory } from '../../../transport/websocket';\nimport { WebSocketReadyState } from '../../../transport/websocket';\nimport type { IpnsUpdateEvent } from './ipfs-types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface IpnsSubscriptionClientConfig {\n /** WebSocket URL (e.g., wss://host/ws/ipns) */\n wsUrl: string;\n /** Platform-specific WebSocket factory */\n createWebSocket: WebSocketFactory;\n /** Ping interval in ms (default: 30000) */\n pingIntervalMs?: number;\n /** Initial reconnect delay in ms (default: 5000) */\n reconnectDelayMs?: number;\n /** Max reconnect delay in ms (default: 60000) */\n maxReconnectDelayMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\ntype UpdateCallback = (update: IpnsUpdateEvent) => void;\n\ninterface WebSocketMessage {\n type: string;\n name?: string;\n names?: string[];\n sequence?: number;\n cid?: string | null;\n timestamp?: string;\n message?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class IpnsSubscriptionClient {\n private ws: IWebSocket | null = null;\n private readonly subscriptions: Map<string, Set<UpdateCallback>> = new Map();\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n private pingInterval: ReturnType<typeof setInterval> | null = null;\n private fallbackPollInterval: ReturnType<typeof setInterval> | null = null;\n\n private readonly wsUrl: string;\n private readonly createWebSocket: WebSocketFactory;\n private readonly pingIntervalMs: number;\n private readonly initialReconnectDelayMs: number;\n private readonly maxReconnectDelayMs: number;\n private readonly debugEnabled: boolean;\n\n private reconnectAttempts = 0;\n private isConnecting = false;\n private connectionOpenedAt = 0;\n private destroyed = false;\n\n /** Minimum stable connection time before resetting backoff (30 seconds) */\n private readonly minStableConnectionMs = 30000;\n\n private fallbackPollFn: (() => Promise<void>) | null = null;\n private fallbackPollIntervalMs = 0;\n\n constructor(config: IpnsSubscriptionClientConfig) {\n this.wsUrl = config.wsUrl;\n this.createWebSocket = config.createWebSocket;\n this.pingIntervalMs = config.pingIntervalMs ?? 30000;\n this.initialReconnectDelayMs = config.reconnectDelayMs ?? 5000;\n this.maxReconnectDelayMs = config.maxReconnectDelayMs ?? 60000;\n this.debugEnabled = config.debug ?? false;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /**\n * Subscribe to IPNS updates for a specific name.\n * Automatically connects the WebSocket if not already connected.\n * If WebSocket is connecting, the name will be subscribed once connected.\n */\n subscribe(ipnsName: string, callback: UpdateCallback): () => void {\n if (!ipnsName || typeof ipnsName !== 'string') {\n this.log('Invalid IPNS name for subscription');\n return () => { /* noop */ };\n }\n\n const isNewSubscription = !this.subscriptions.has(ipnsName);\n\n if (isNewSubscription) {\n this.subscriptions.set(ipnsName, new Set());\n }\n\n this.subscriptions.get(ipnsName)!.add(callback);\n\n // Send subscription to server if already connected and this is a new name\n if (isNewSubscription && this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.sendSubscribe([ipnsName]);\n }\n\n // Connect if not already connected\n if (!this.ws || this.ws.readyState !== WebSocketReadyState.OPEN) {\n this.connect();\n }\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.subscriptions.get(ipnsName);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete(ipnsName);\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.sendUnsubscribe([ipnsName]);\n }\n // Disconnect if no more subscriptions\n if (this.subscriptions.size === 0) {\n this.disconnect();\n }\n }\n }\n };\n }\n\n /**\n * Register a convenience update callback for all subscriptions.\n * Returns an unsubscribe function.\n */\n onUpdate(callback: UpdateCallback): () => void {\n // Store as a special \"global\" callback keyed by '*'\n if (!this.subscriptions.has('*')) {\n this.subscriptions.set('*', new Set());\n }\n this.subscriptions.get('*')!.add(callback);\n\n return () => {\n const callbacks = this.subscriptions.get('*');\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete('*');\n }\n }\n };\n }\n\n /**\n * Set a fallback poll function to use when WebSocket is disconnected.\n * The poll function will be called at the specified interval while WS is down.\n */\n setFallbackPoll(fn: () => Promise<void>, intervalMs: number): void {\n this.fallbackPollFn = fn;\n this.fallbackPollIntervalMs = intervalMs;\n // Start polling if WS is not connected\n if (!this.isConnected()) {\n this.startFallbackPolling();\n }\n }\n\n /**\n * Connect to the WebSocket server.\n */\n connect(): void {\n if (this.destroyed) return;\n if (this.ws?.readyState === WebSocketReadyState.OPEN || this.isConnecting) {\n return;\n }\n\n this.isConnecting = true;\n\n try {\n this.log(`Connecting to ${this.wsUrl}...`);\n this.ws = this.createWebSocket(this.wsUrl);\n\n this.ws.onopen = () => {\n this.log('WebSocket connected');\n this.isConnecting = false;\n this.connectionOpenedAt = Date.now();\n\n // Resubscribe to all IPNS names (excluding '*' global key)\n const names = Array.from(this.subscriptions.keys()).filter(n => n !== '*');\n if (names.length > 0) {\n this.sendSubscribe(names);\n }\n\n // Start ping interval to keep connection alive\n this.startPingInterval();\n\n // Stop fallback polling — WS is live\n this.stopFallbackPolling();\n };\n\n this.ws.onmessage = (event) => {\n this.handleMessage(event.data);\n };\n\n this.ws.onclose = () => {\n const connectionDuration = this.connectionOpenedAt > 0\n ? Date.now() - this.connectionOpenedAt\n : 0;\n const wasStable = connectionDuration >= this.minStableConnectionMs;\n\n this.log(`WebSocket closed (duration: ${Math.round(connectionDuration / 1000)}s)`);\n\n this.isConnecting = false;\n this.connectionOpenedAt = 0;\n this.stopPingInterval();\n\n // Only reset backoff if connection was stable\n if (wasStable) {\n this.reconnectAttempts = 0;\n }\n\n // Start fallback polling while WS is down\n this.startFallbackPolling();\n\n this.scheduleReconnect();\n };\n\n this.ws.onerror = () => {\n this.log('WebSocket error');\n this.isConnecting = false;\n };\n } catch (e) {\n this.log(`Failed to connect: ${e}`);\n this.isConnecting = false;\n this.startFallbackPolling();\n this.scheduleReconnect();\n }\n }\n\n /**\n * Disconnect from the WebSocket server and clean up.\n */\n disconnect(): void {\n this.destroyed = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.stopPingInterval();\n this.stopFallbackPolling();\n\n if (this.ws) {\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n }\n\n /**\n * Check if connected to the WebSocket server.\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocketReadyState.OPEN;\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Message Handling\n // ---------------------------------------------------------------------------\n\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data) as WebSocketMessage;\n\n switch (message.type) {\n case 'update':\n if (message.name && message.sequence !== undefined) {\n this.notifySubscribers({\n type: 'update',\n name: message.name,\n sequence: message.sequence,\n cid: message.cid ?? '',\n timestamp: message.timestamp || new Date().toISOString(),\n });\n }\n break;\n\n case 'subscribed':\n this.log(`Subscribed to ${message.names?.length || 0} names`);\n break;\n\n case 'unsubscribed':\n this.log(`Unsubscribed from ${message.names?.length || 0} names`);\n break;\n\n case 'pong':\n // Keepalive response received\n break;\n\n case 'error':\n this.log(`Server error: ${message.message}`);\n break;\n\n default:\n // Unknown message type — ignore\n break;\n }\n } catch {\n this.log('Failed to parse message');\n }\n }\n\n private notifySubscribers(update: IpnsUpdateEvent & { type: string }): void {\n // Notify name-specific subscribers\n const callbacks = this.subscriptions.get(update.name);\n if (callbacks) {\n this.log(`Update: ${update.name.slice(0, 16)}... seq=${update.sequence}`);\n for (const callback of callbacks) {\n try {\n callback(update);\n } catch {\n // Don't let callback errors break the client\n }\n }\n }\n\n // Notify global subscribers\n const globalCallbacks = this.subscriptions.get('*');\n if (globalCallbacks) {\n for (const callback of globalCallbacks) {\n try {\n callback(update);\n } catch {\n // Don't let callback errors break the client\n }\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: WebSocket Send\n // ---------------------------------------------------------------------------\n\n private sendSubscribe(names: string[]): void {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'subscribe', names }));\n }\n }\n\n private sendUnsubscribe(names: string[]): void {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'unsubscribe', names }));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Reconnection\n // ---------------------------------------------------------------------------\n\n /**\n * Schedule reconnection with exponential backoff.\n * Sequence: 5s, 10s, 20s, 40s, 60s (capped)\n */\n private scheduleReconnect(): void {\n if (this.destroyed || this.reconnectTimeout) return;\n\n // Don't reconnect if no subscriptions (excluding '*')\n const realSubscriptions = Array.from(this.subscriptions.keys()).filter(n => n !== '*');\n if (realSubscriptions.length === 0) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n this.initialReconnectDelayMs * Math.pow(2, this.reconnectAttempts - 1),\n this.maxReconnectDelayMs,\n );\n\n this.log(`Reconnecting in ${(delay / 1000).toFixed(1)}s (attempt ${this.reconnectAttempts})...`);\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.connect();\n }, delay);\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Keepalive\n // ---------------------------------------------------------------------------\n\n private startPingInterval(): void {\n this.stopPingInterval();\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'ping' }));\n }\n }, this.pingIntervalMs);\n }\n\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Fallback Polling\n // ---------------------------------------------------------------------------\n\n private startFallbackPolling(): void {\n if (this.fallbackPollInterval || !this.fallbackPollFn || this.destroyed) return;\n\n this.log(`Starting fallback polling (${this.fallbackPollIntervalMs / 1000}s interval)`);\n\n // Run poll immediately once\n this.fallbackPollFn().catch((err) => { logger.warn('IPNS-WS', 'Fallback poll error:', err); });\n\n this.fallbackPollInterval = setInterval(() => {\n this.fallbackPollFn?.().catch((err) => { logger.warn('IPNS-WS', 'Fallback poll error:', err); });\n }, this.fallbackPollIntervalMs);\n }\n\n private stopFallbackPolling(): void {\n if (this.fallbackPollInterval) {\n clearInterval(this.fallbackPollInterval);\n this.fallbackPollInterval = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Logging\n // ---------------------------------------------------------------------------\n\n private log(message: string): void {\n logger.debug('IPNS-WS', message);\n }\n}\n","/**\n * Write-Behind Buffer for IpfsStorageProvider\n *\n * Provides non-blocking writes via double-buffering and a promise-chain\n * serial queue. Writes are accepted immediately into a pending buffer\n * and flushed to IPFS asynchronously in the background.\n */\n\nimport type { TxfStorageDataBase } from '../../../storage';\n\n// =============================================================================\n// AsyncSerialQueue\n// =============================================================================\n\n/**\n * Promise-chain-based async mutex. Serializes async operations\n * without external dependencies. Each enqueued operation waits for\n * the previous one to complete before starting.\n */\nexport class AsyncSerialQueue {\n private tail: Promise<void> = Promise.resolve();\n\n /** Enqueue an async operation. Returns when it completes. */\n enqueue<T>(fn: () => Promise<T>): Promise<T> {\n let resolve: (value: T) => void;\n let reject: (reason: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this.tail = this.tail.then(\n () => fn().then(resolve!, reject!),\n () => fn().then(resolve!, reject!),\n );\n\n return promise;\n }\n}\n\n// =============================================================================\n// WriteBuffer\n// =============================================================================\n\n/**\n * Collects mutations (token saves/deletes + full TXF data)\n * between flush cycles. Acts as a staging area for changes\n * that haven't been persisted to IPFS yet.\n */\nexport class WriteBuffer {\n /** Full TXF data from save() calls — latest wins */\n txfData: TxfStorageDataBase | null = null;\n\n get isEmpty(): boolean {\n return this.txfData === null;\n }\n\n clear(): void {\n this.txfData = null;\n }\n\n /**\n * Merge another buffer's contents into this one (for rollback).\n * Existing (newer) mutations in `this` take precedence over `other`.\n */\n mergeFrom(other: WriteBuffer): void {\n if (other.txfData && !this.txfData) {\n this.txfData = other.txfData;\n }\n }\n}\n","/**\n * IPFS Storage Provider\n * Main TokenStorageProvider implementation using IPFS/IPNS.\n * Shared cross-platform module (browser + Node.js via native fetch).\n *\n * Uses a write-behind buffer for non-blocking save() operations.\n * Writes are accepted immediately and flushed to IPFS asynchronously.\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity } from '../../../types';\nimport type {\n TokenStorageProvider,\n TxfStorageDataBase,\n SaveResult,\n LoadResult,\n SyncResult,\n StorageEventCallback,\n StorageEvent,\n} from '../../../storage';\nimport type {\n IpfsStorageConfig,\n IpfsStatePersistence,\n} from './ipfs-types';\nimport type { WebSocketFactory } from '../../../transport/websocket';\nimport { getIpfsGatewayUrls } from '../../../constants';\nimport { IpfsCache } from './ipfs-cache';\nimport { IpfsHttpClient } from './ipfs-http-client';\nimport { IpnsSubscriptionClient } from './ipns-subscription-client';\nimport { deriveIpnsIdentity } from './ipns-key-derivation';\nimport { createSignedRecord } from './ipns-record-manager';\nimport { mergeTxfData } from './txf-merge';\nimport { InMemoryIpfsStatePersistence } from './ipfs-state-persistence';\nimport { AsyncSerialQueue, WriteBuffer } from './write-behind-buffer';\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class IpfsStorageProvider<TData extends TxfStorageDataBase = TxfStorageDataBase>\n implements TokenStorageProvider<TData>\n{\n readonly id = 'ipfs';\n readonly name = 'IPFS Storage';\n readonly type = 'p2p' as const;\n\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n private ipnsKeyPair: unknown = null;\n private ipnsName: string | null = null;\n private ipnsSequenceNumber: bigint = 0n;\n private lastCid: string | null = null;\n private lastKnownRemoteSequence: bigint = 0n;\n private dataVersion = 0;\n\n /**\n * The CID currently stored on the sidecar for this IPNS name.\n * Used as `_meta.lastCid` in the next save to satisfy chain validation.\n * - null for bootstrap (first-ever save)\n * - set after every successful save() or load()\n */\n private remoteCid: string | null = null;\n\n private readonly cache: IpfsCache;\n private readonly httpClient: IpfsHttpClient;\n private readonly statePersistence: IpfsStatePersistence;\n private readonly eventCallbacks: Set<StorageEventCallback> = new Set();\n private readonly debug: boolean;\n private readonly ipnsLifetimeMs: number;\n\n /** WebSocket factory for push subscriptions */\n private readonly createWebSocket: WebSocketFactory | undefined;\n /** Override WS URL */\n private readonly wsUrl: string | undefined;\n /** Fallback poll interval (default: 90000) */\n private readonly fallbackPollIntervalMs: number;\n /** IPNS subscription client for push notifications */\n private subscriptionClient: IpnsSubscriptionClient | null = null;\n /** Unsubscribe function from subscription client */\n private subscriptionUnsubscribe: (() => void) | null = null;\n\n /** Write-behind buffer: serializes flush / sync / shutdown */\n private readonly flushQueue = new AsyncSerialQueue();\n /** Pending mutations not yet flushed to IPFS */\n private pendingBuffer = new WriteBuffer();\n /** Debounce timer for background flush */\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n /** Debounce interval in ms */\n private readonly flushDebounceMs: number;\n /** Set to true during shutdown to prevent new flushes */\n private isShuttingDown = false;\n\n constructor(\n config?: IpfsStorageConfig,\n statePersistence?: IpfsStatePersistence,\n ) {\n const gateways = config?.gateways ?? getIpfsGatewayUrls();\n this.debug = config?.debug ?? false;\n this.ipnsLifetimeMs = config?.ipnsLifetimeMs ?? (99 * 365 * 24 * 60 * 60 * 1000);\n this.flushDebounceMs = config?.flushDebounceMs ?? 2000;\n\n this.cache = new IpfsCache({\n ipnsTtlMs: config?.ipnsCacheTtlMs,\n failureCooldownMs: config?.circuitBreakerCooldownMs,\n failureThreshold: config?.circuitBreakerThreshold,\n knownFreshWindowMs: config?.knownFreshWindowMs,\n });\n\n this.httpClient = new IpfsHttpClient({\n gateways,\n fetchTimeoutMs: config?.fetchTimeoutMs,\n resolveTimeoutMs: config?.resolveTimeoutMs,\n publishTimeoutMs: config?.publishTimeoutMs,\n connectivityTimeoutMs: config?.connectivityTimeoutMs,\n debug: this.debug,\n }, this.cache);\n\n this.statePersistence = statePersistence ?? new InMemoryIpfsStatePersistence();\n this.createWebSocket = config?.createWebSocket;\n this.wsUrl = config?.wsUrl;\n this.fallbackPollIntervalMs = config?.fallbackPollIntervalMs ?? 90000;\n }\n\n // ---------------------------------------------------------------------------\n // BaseProvider interface\n // ---------------------------------------------------------------------------\n\n async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n await this.shutdown();\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ---------------------------------------------------------------------------\n // Identity & Initialization\n // ---------------------------------------------------------------------------\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n }\n\n async initialize(): Promise<boolean> {\n if (!this.identity) {\n this.log('Cannot initialize: no identity set');\n return false;\n }\n\n this.status = 'connecting';\n this.emitEvent({ type: 'storage:loading', timestamp: Date.now() });\n\n try {\n // Derive IPNS key pair and name from wallet private key\n const { keyPair, ipnsName } = await deriveIpnsIdentity(this.identity.privateKey);\n this.ipnsKeyPair = keyPair;\n this.ipnsName = ipnsName;\n this.log(`IPNS name derived: ${ipnsName}`);\n\n // Load persisted state\n const persisted = await this.statePersistence.load(ipnsName);\n if (persisted) {\n this.ipnsSequenceNumber = BigInt(persisted.sequenceNumber);\n this.lastCid = persisted.lastCid;\n this.remoteCid = persisted.lastCid; // chain link for next save\n this.dataVersion = persisted.version;\n this.log(`Loaded persisted state: seq=${this.ipnsSequenceNumber}, cid=${this.lastCid}`);\n }\n\n // Set up IPNS push subscription if WebSocket factory is available\n if (this.createWebSocket) {\n try {\n const wsUrlFinal = this.wsUrl ?? this.deriveWsUrl();\n if (wsUrlFinal) {\n this.subscriptionClient = new IpnsSubscriptionClient({\n wsUrl: wsUrlFinal,\n createWebSocket: this.createWebSocket,\n debug: this.debug,\n });\n\n // Subscribe to own IPNS name\n this.subscriptionUnsubscribe = this.subscriptionClient.subscribe(\n ipnsName,\n (update) => {\n this.log(`Push update: seq=${update.sequence}, cid=${update.cid}`);\n this.emitEvent({\n type: 'storage:remote-updated',\n timestamp: Date.now(),\n data: { name: update.name, sequence: update.sequence, cid: update.cid },\n });\n },\n );\n\n // Set fallback poll for when WS is disconnected\n this.subscriptionClient.setFallbackPoll(\n () => this.pollForRemoteChanges(),\n this.fallbackPollIntervalMs,\n );\n\n // Connect (non-blocking)\n this.subscriptionClient.connect();\n }\n } catch (wsError) {\n this.log(`Failed to set up IPNS subscription: ${wsError}`);\n // Non-fatal — provider works without push notifications\n }\n }\n\n // Test gateway connectivity (non-blocking, don't fail on it)\n this.httpClient.findHealthyGateways().then((healthy) => {\n if (healthy.length > 0) {\n this.log(`${healthy.length} healthy gateway(s) found`);\n } else {\n this.log('Warning: no healthy gateways found');\n }\n }).catch((err) => {\n logger.warn('IPFS-Storage', 'Gateway health check failed (non-fatal):', err);\n });\n\n this.isShuttingDown = false;\n this.status = 'connected';\n this.emitEvent({ type: 'storage:loaded', timestamp: Date.now() });\n return true;\n } catch (error) {\n this.status = 'error';\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n }\n\n async shutdown(): Promise<void> {\n this.isShuttingDown = true;\n\n // Cancel any pending debounced flush\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Final flush — drain any pending writes\n await this.flushQueue.enqueue(async () => {\n if (!this.pendingBuffer.isEmpty) {\n try {\n await this.executeFlush();\n } catch {\n this.log('Final flush on shutdown failed (data may be lost)');\n }\n }\n });\n\n // Disconnect subscription client\n if (this.subscriptionUnsubscribe) {\n this.subscriptionUnsubscribe();\n this.subscriptionUnsubscribe = null;\n }\n if (this.subscriptionClient) {\n this.subscriptionClient.disconnect();\n this.subscriptionClient = null;\n }\n\n this.cache.clear();\n this.status = 'disconnected';\n }\n\n // ---------------------------------------------------------------------------\n // Save (non-blocking — buffers data for async flush)\n // ---------------------------------------------------------------------------\n\n async save(data: TData): Promise<SaveResult> {\n if (!this.ipnsKeyPair || !this.ipnsName) {\n return { success: false, error: 'Not initialized', timestamp: Date.now() };\n }\n\n // Buffer the data for async flush\n this.pendingBuffer.txfData = data;\n this.scheduleFlush();\n\n // Return immediately — flush happens in background\n return { success: true, timestamp: Date.now() };\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Blocking save (used by sync and executeFlush)\n // ---------------------------------------------------------------------------\n\n /**\n * Perform the actual upload + IPNS publish synchronously.\n * Called by executeFlush() and sync() — never by public save().\n */\n private async _doSave(data: TData): Promise<SaveResult> {\n if (!this.ipnsKeyPair || !this.ipnsName) {\n return { success: false, error: 'Not initialized', timestamp: Date.now() };\n }\n\n this.emitEvent({ type: 'storage:saving', timestamp: Date.now() });\n\n try {\n // Update meta with chain-validation fields required by sidecar:\n // - lastCid: must equal the CID currently stored on sidecar (null for bootstrap)\n // - version: must be exactly current_version + 1 (for normal updates)\n this.dataVersion++;\n const metaUpdate: Record<string, unknown> = {\n ...data._meta,\n version: this.dataVersion,\n ipnsName: this.ipnsName,\n updatedAt: Date.now(),\n };\n if (this.remoteCid) {\n // Normal update: chain to previous CID\n metaUpdate.lastCid = this.remoteCid;\n }\n // Bootstrap (remoteCid is null): do NOT include lastCid field at all\n const updatedData = { ...data, _meta: metaUpdate } as unknown as Record<string, unknown>;\n\n // Upload to IPFS\n const { cid } = await this.httpClient.upload(updatedData);\n this.log(`Content uploaded: CID=${cid}`);\n\n // Compute new sequence: max(local, remote) + 1\n const baseSeq = this.ipnsSequenceNumber > this.lastKnownRemoteSequence\n ? this.ipnsSequenceNumber\n : this.lastKnownRemoteSequence;\n const newSeq = baseSeq + 1n;\n\n // Create signed IPNS record\n const marshalledRecord = await createSignedRecord(\n this.ipnsKeyPair,\n cid,\n newSeq,\n this.ipnsLifetimeMs,\n );\n\n // Publish to all gateways\n const publishResult = await this.httpClient.publishIpns(\n this.ipnsName,\n marshalledRecord,\n );\n\n if (!publishResult.success) {\n // Rollback version (sequence was not yet updated)\n this.dataVersion--;\n this.log(`IPNS publish failed: ${publishResult.error}`);\n return {\n success: false,\n error: publishResult.error ?? 'IPNS publish failed',\n timestamp: Date.now(),\n };\n }\n\n // Update local state\n this.ipnsSequenceNumber = newSeq;\n this.lastCid = cid;\n this.remoteCid = cid; // next save chains to this CID\n\n // Update cache\n this.cache.setIpnsRecord(this.ipnsName, {\n cid,\n sequence: newSeq,\n gateway: 'local',\n });\n this.cache.setContent(cid, updatedData as unknown as TxfStorageDataBase);\n this.cache.markIpnsFresh(this.ipnsName);\n\n // Persist state\n await this.statePersistence.save(this.ipnsName, {\n sequenceNumber: newSeq.toString(),\n lastCid: cid,\n version: this.dataVersion,\n });\n\n this.emitEvent({\n type: 'storage:saved',\n timestamp: Date.now(),\n data: { cid, sequence: newSeq.toString() },\n });\n\n this.log(`Saved: CID=${cid}, seq=${newSeq}`);\n return { success: true, cid, timestamp: Date.now() };\n } catch (error) {\n // Rollback version on any error\n this.dataVersion--;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return { success: false, error: errorMessage, timestamp: Date.now() };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Write-behind buffer: scheduling and flushing\n // ---------------------------------------------------------------------------\n\n /**\n * Schedule a debounced background flush.\n * Resets the timer on each call so rapid mutations coalesce.\n */\n private scheduleFlush(): void {\n if (this.isShuttingDown) return;\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n this.log(`Background flush failed: ${err}`);\n });\n }, this.flushDebounceMs);\n }\n\n /**\n * Execute a flush of the pending buffer to IPFS.\n * Runs inside AsyncSerialQueue for concurrency safety.\n */\n private async executeFlush(): Promise<void> {\n if (this.pendingBuffer.isEmpty) return;\n\n // 1. Swap: take pending → active, create new empty pending\n const active = this.pendingBuffer;\n this.pendingBuffer = new WriteBuffer();\n\n try {\n // 2. Build the data to save\n // Use buffered TXF data if available, otherwise build minimal payload\n const baseData = (active.txfData ?? {\n _meta: { version: 0, address: this.identity?.directAddress ?? '', formatVersion: '2.0', updatedAt: 0 },\n }) as TData;\n\n // 3. Perform the actual blocking save\n const result = await this._doSave(baseData);\n\n if (!result.success) {\n throw new SphereError(result.error ?? 'Save failed', 'STORAGE_ERROR');\n }\n\n this.log(`Flushed successfully: CID=${result.cid}`);\n } catch (error) {\n // 4. Rollback: merge active back into pending\n this.pendingBuffer.mergeFrom(active);\n\n const msg = error instanceof Error ? error.message : String(error);\n this.log(`Flush failed (will retry): ${msg}`);\n\n // Schedule retry\n this.scheduleFlush();\n\n throw error; // re-throw so callers (e.g. shutdown) know it failed\n }\n }\n\n // ---------------------------------------------------------------------------\n // Load\n // ---------------------------------------------------------------------------\n\n async load(identifier?: string): Promise<LoadResult<TData>> {\n if (!this.ipnsName && !identifier) {\n return { success: false, error: 'Not initialized', source: 'local', timestamp: Date.now() };\n }\n\n this.emitEvent({ type: 'storage:loading', timestamp: Date.now() });\n\n try {\n // If a specific CID is given, fetch directly\n if (identifier) {\n const data = await this.httpClient.fetchContent<TData>(identifier);\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n }\n\n const ipnsName = this.ipnsName!;\n\n // Check known-fresh cache\n if (this.cache.isIpnsKnownFresh(ipnsName)) {\n const cached = this.cache.getIpnsRecordIgnoreTtl(ipnsName);\n if (cached) {\n const content = this.cache.getContent(cached.cid);\n if (content) {\n this.log('Using known-fresh cached data');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n }\n }\n\n // Check IPNS cache (60s TTL)\n const cachedRecord = this.cache.getIpnsRecord(ipnsName);\n if (cachedRecord) {\n const content = this.cache.getContent(cachedRecord.cid);\n if (content) {\n this.log('IPNS cache hit');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n // Have CID but not content — fetch content\n try {\n const data = await this.httpClient.fetchContent<TData>(cachedRecord.cid);\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n } catch {\n // Fall through to full resolution\n }\n }\n\n // Resolve IPNS from network\n const { best } = await this.httpClient.resolveIpns(ipnsName);\n\n if (!best) {\n // Not found — could be a new wallet\n this.log('IPNS record not found (new wallet?)');\n return { success: false, error: 'IPNS record not found', source: 'remote', timestamp: Date.now() };\n }\n\n // Track remote sequence and CID for chain validation\n if (best.sequence > this.lastKnownRemoteSequence) {\n this.lastKnownRemoteSequence = best.sequence;\n }\n this.remoteCid = best.cid;\n\n // Fetch content\n const data = await this.httpClient.fetchContent<TData>(best.cid);\n\n // Track remote version for correct version chaining\n const remoteVersion = (data as TxfStorageDataBase)?._meta?.version;\n if (typeof remoteVersion === 'number' && remoteVersion > this.dataVersion) {\n this.dataVersion = remoteVersion;\n }\n\n this.emitEvent({\n type: 'storage:loaded',\n timestamp: Date.now(),\n data: { cid: best.cid, sequence: best.sequence.toString() },\n });\n\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n } catch (error) {\n // On network error, try to return cached data\n if (this.ipnsName) {\n const cached = this.cache.getIpnsRecordIgnoreTtl(this.ipnsName);\n if (cached) {\n const content = this.cache.getContent(cached.cid);\n if (content) {\n this.log('Network error, returning stale cache');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n }\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return { success: false, error: errorMessage, source: 'remote', timestamp: Date.now() };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Sync (enters serial queue to avoid concurrent IPNS conflicts)\n // ---------------------------------------------------------------------------\n\n async sync(localData: TData): Promise<SyncResult<TData>> {\n return this.flushQueue.enqueue(async () => {\n // Cancel any pending debounced flush (we'll save as part of sync)\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n this.emitEvent({ type: 'sync:started', timestamp: Date.now() });\n\n try {\n // Drain pending buffer — its data will be included via the sync save\n this.pendingBuffer.clear();\n\n // Load remote data\n const remoteResult = await this.load();\n\n if (!remoteResult.success || !remoteResult.data) {\n // No remote data — save local as initial\n this.log('No remote data found, uploading local data');\n const saveResult = await this._doSave(localData);\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n return {\n success: saveResult.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: saveResult.error,\n };\n }\n\n const remoteData = remoteResult.data;\n\n // Check if merge is needed\n const localVersion = localData._meta?.version ?? 0;\n const remoteVersion = remoteData._meta?.version ?? 0;\n\n if (localVersion === remoteVersion && this.lastCid) {\n // Same version — no merge needed\n this.log('Data is in sync (same version)');\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n return {\n success: true,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n };\n }\n\n // Merge\n this.log(`Merging: local v${localVersion} <-> remote v${remoteVersion}`);\n const { merged, added, removed, conflicts } = mergeTxfData(localData, remoteData);\n\n if (conflicts > 0) {\n this.emitEvent({\n type: 'sync:conflict',\n timestamp: Date.now(),\n data: { conflicts },\n });\n }\n\n // Save merged result\n const saveResult = await this._doSave(merged);\n\n this.emitEvent({\n type: 'sync:completed',\n timestamp: Date.now(),\n data: { added, removed, conflicts },\n });\n\n return {\n success: saveResult.success,\n merged: merged,\n added,\n removed,\n conflicts,\n error: saveResult.error,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'sync:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return {\n success: false,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: errorMessage,\n };\n }\n });\n }\n\n // ---------------------------------------------------------------------------\n // Private Helpers\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // Optional Methods\n // ---------------------------------------------------------------------------\n\n async exists(): Promise<boolean> {\n if (!this.ipnsName) return false;\n\n // Check cache first\n const cached = this.cache.getIpnsRecord(this.ipnsName);\n if (cached) return true;\n\n // Resolve from network\n const { best } = await this.httpClient.resolveIpns(this.ipnsName);\n return best !== null;\n }\n\n async clear(): Promise<boolean> {\n if (!this.ipnsKeyPair || !this.ipnsName) return false;\n\n // Clear pending buffer\n this.pendingBuffer.clear();\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n const emptyData = {\n _meta: {\n version: 0,\n address: this.identity?.directAddress ?? '',\n ipnsName: this.ipnsName,\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n } as TData;\n\n const result = await this._doSave(emptyData);\n if (result.success) {\n this.cache.clear();\n await this.statePersistence.clear(this.ipnsName);\n }\n return result.success;\n }\n\n onEvent(callback: StorageEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => {\n this.eventCallbacks.delete(callback);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public Accessors\n // ---------------------------------------------------------------------------\n\n getIpnsName(): string | null {\n return this.ipnsName;\n }\n\n getLastCid(): string | null {\n return this.lastCid;\n }\n\n getSequenceNumber(): bigint {\n return this.ipnsSequenceNumber;\n }\n\n getDataVersion(): number {\n return this.dataVersion;\n }\n\n getRemoteCid(): string | null {\n return this.remoteCid;\n }\n\n // ---------------------------------------------------------------------------\n // Testing helper: wait for pending flush to complete\n // ---------------------------------------------------------------------------\n\n /**\n * Wait for the pending flush timer to fire and the flush operation to\n * complete. Useful in tests to await background writes.\n * Returns immediately if no flush is pending.\n */\n async waitForFlush(): Promise<void> {\n if (this.flushTimer) {\n // Force the timer to fire now\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n await this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n logger.warn('IPFS-Storage', 'Flush on shutdown failed:', err);\n });\n } else if (!this.pendingBuffer.isEmpty) {\n // No timer but pending data — flush now\n await this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n logger.warn('IPFS-Storage', 'Flush on shutdown failed:', err);\n });\n } else {\n // Ensure any in-flight flush completes\n await this.flushQueue.enqueue(async () => {});\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Push Subscription Helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Derive WebSocket URL from the first configured gateway.\n * Converts https://host → wss://host/ws/ipns\n */\n private deriveWsUrl(): string | null {\n const gateways = this.httpClient.getGateways();\n if (gateways.length === 0) return null;\n\n const gateway = gateways[0];\n const wsProtocol = gateway.startsWith('https://') ? 'wss://' : 'ws://';\n const host = gateway.replace(/^https?:\\/\\//, '');\n return `${wsProtocol}${host}/ws/ipns`;\n }\n\n /**\n * Poll for remote IPNS changes (fallback when WS is unavailable).\n * Compares remote sequence number with last known and emits event if changed.\n */\n private async pollForRemoteChanges(): Promise<void> {\n if (!this.ipnsName) return;\n\n try {\n const { best } = await this.httpClient.resolveIpns(this.ipnsName);\n if (best && best.sequence > this.lastKnownRemoteSequence) {\n this.log(`Poll detected remote change: seq=${best.sequence} (was ${this.lastKnownRemoteSequence})`);\n this.lastKnownRemoteSequence = best.sequence;\n this.emitEvent({\n type: 'storage:remote-updated',\n timestamp: Date.now(),\n data: { name: this.ipnsName, sequence: Number(best.sequence), cid: best.cid },\n });\n }\n } catch {\n // Non-fatal — poll will retry on next interval\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private emitEvent(event: StorageEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch {\n // Don't let event handler errors break the provider\n }\n }\n }\n\n private log(message: string): void {\n logger.debug('IPFS-Storage', message);\n }\n\n}\n","/**\n * Browser IPFS State Persistence\n * Uses localStorage for persisting IPFS/IPNS state between sessions\n */\n\nimport type { IpfsStatePersistence, IpfsPersistedState } from '../../shared/ipfs';\n\nconst KEY_PREFIX = 'sphere_ipfs_';\n\nfunction seqKey(ipnsName: string): string {\n return `${KEY_PREFIX}seq_${ipnsName}`;\n}\n\nfunction cidKey(ipnsName: string): string {\n return `${KEY_PREFIX}cid_${ipnsName}`;\n}\n\nfunction verKey(ipnsName: string): string {\n return `${KEY_PREFIX}ver_${ipnsName}`;\n}\n\nexport class BrowserIpfsStatePersistence implements IpfsStatePersistence {\n async load(ipnsName: string): Promise<IpfsPersistedState | null> {\n try {\n const seq = localStorage.getItem(seqKey(ipnsName));\n if (!seq) return null;\n\n return {\n sequenceNumber: seq,\n lastCid: localStorage.getItem(cidKey(ipnsName)),\n version: parseInt(localStorage.getItem(verKey(ipnsName)) ?? '0', 10),\n };\n } catch {\n return null;\n }\n }\n\n async save(ipnsName: string, state: IpfsPersistedState): Promise<void> {\n try {\n localStorage.setItem(seqKey(ipnsName), state.sequenceNumber);\n if (state.lastCid) {\n localStorage.setItem(cidKey(ipnsName), state.lastCid);\n } else {\n localStorage.removeItem(cidKey(ipnsName));\n }\n localStorage.setItem(verKey(ipnsName), String(state.version));\n } catch {\n // localStorage might be full or unavailable\n }\n }\n\n async clear(ipnsName: string): Promise<void> {\n try {\n localStorage.removeItem(seqKey(ipnsName));\n localStorage.removeItem(cidKey(ipnsName));\n localStorage.removeItem(verKey(ipnsName));\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n","/**\n * Browser IPFS Storage Module\n * Factory function for browser-specific IPFS storage provider\n */\n\nimport { IpfsStorageProvider, type IpfsStorageConfig } from '../../shared/ipfs';\nimport { BrowserIpfsStatePersistence } from './browser-ipfs-state-persistence';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export for convenience\nexport { IpfsStorageProvider } from '../../shared/ipfs';\nexport { BrowserIpfsStatePersistence } from './browser-ipfs-state-persistence';\nexport type { IpfsStorageConfig as IpfsStorageProviderConfig } from '../../shared/ipfs';\n\n/**\n * Create a browser WebSocket that conforms to the IWebSocket interface.\n */\nfunction createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create a browser IPFS storage provider with localStorage-based state persistence.\n * Automatically injects the browser WebSocket factory for IPNS push subscriptions.\n */\nexport function createBrowserIpfsStorageProvider(config?: IpfsStorageConfig): IpfsStorageProvider {\n return new IpfsStorageProvider(\n { ...config, createWebSocket: config?.createWebSocket ?? createBrowserWebSocket },\n new BrowserIpfsStatePersistence(),\n );\n}\n\n/** @deprecated Use createBrowserIpfsStorageProvider instead */\nexport const createIpfsStorageProvider = createBrowserIpfsStorageProvider;\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 { 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: AbortSignal.timeout(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 * 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 [cached, cachedTs] = await Promise.all([\n this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE),\n this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS),\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 await Promise.all([\n this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE, JSON.stringify(definitions)),\n this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS, 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 * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, DEFAULT_AGGREGATOR_API_KEY, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n L1Config,\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 ?? DEFAULT_AGGREGATOR_API_KEY,\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// L1 Resolution\n// =============================================================================\n\n/**\n * Resolve L1 configuration with override pattern\n *\n * Only returns config if l1 is explicitly provided (L1 is optional)\n *\n * @example\n * ```ts\n * // No L1 config - returns undefined\n * resolveL1Config('testnet', undefined);\n *\n * // Enable L1 with defaults\n * resolveL1Config('testnet', {});\n *\n * // Override electrum URL\n * resolveL1Config('testnet', { electrumUrl: 'wss://custom.fulcrum:50004' });\n * ```\n */\nexport function resolveL1Config(\n network: NetworkType,\n config?: L1Config\n): L1Config | undefined {\n if (config === undefined) {\n return undefined;\n }\n\n const networkConfig = getNetworkConfig(network);\n\n return {\n electrumUrl: config.electrumUrl ?? networkConfig.electrumUrl,\n defaultFeeRate: config.defaultFeeRate,\n enableVesting: config.enableVesting,\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":["import_buffer","DB_NAME","DB_VERSION","connectionSeq","db","elliptic","sha256","CryptoJS","hexToBytes","message","senderNametag","NostrEventClass","SdkToken","valid","hexToBytes","data","saveResult","createBrowserWebSocket","netConfig"]}
|
|
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","../../../impl/browser/storage/IndexedDBTokenStorageProvider.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/browser/download.ts","../../../impl/shared/ipfs/ipfs-error-types.ts","../../../impl/shared/ipfs/ipfs-state-persistence.ts","../../../node_modules/@noble/hashes/src/hmac.ts","../../../node_modules/@noble/hashes/src/hkdf.ts","../../../core/crypto.ts","../../../impl/shared/ipfs/ipns-key-derivation.ts","../../../impl/shared/ipfs/ipns-record-manager.ts","../../../impl/shared/ipfs/ipfs-cache.ts","../../../impl/shared/ipfs/ipfs-http-client.ts","../../../impl/shared/ipfs/txf-merge.ts","../../../impl/shared/ipfs/ipns-subscription-client.ts","../../../impl/shared/ipfs/write-behind-buffer.ts","../../../impl/shared/ipfs/ipfs-storage-provider.ts","../../../impl/browser/ipfs/browser-ipfs-state-persistence.ts","../../../impl/browser/ipfs/index.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';\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n L1Config,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig, createIndexedDBTokenStorageProvider } from './storage';\nimport { createNostrTransportProvider } from './transport';\nimport { createUnicityAggregatorProvider } from './oracle';\nimport { createBrowserIpfsStorageProvider } from './ipfs';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } 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 L1Config,\n type BrowserTransportExtensions,\n resolveTransportConfig,\n resolveOracleConfig,\n resolveL1Config,\n resolvePriceConfig,\n resolveArrayConfig,\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 * IPFS sync backend configuration\n */\nexport interface IpfsSyncConfig {\n /** Enable IPFS sync (default: false) */\n enabled?: boolean;\n /** Replace default gateways entirely */\n gateways?: string[];\n /** Add gateways to network defaults */\n additionalGateways?: string[];\n /** Replace default bootstrap peers */\n bootstrapPeers?: string[];\n /** Add bootstrap peers to defaults */\n additionalBootstrapPeers?: string[];\n /** Use browser DHT (Helia) vs HTTP-only mode */\n useDht?: boolean;\n}\n\n/**\n * File sync backend configuration (future)\n */\nexport interface FileSyncConfig {\n /** Enable file sync (default: false) */\n enabled?: boolean;\n /** Directory path for token files */\n directory?: string;\n /** File format: 'json' | 'txf' */\n format?: 'json' | 'txf';\n}\n\n/**\n * Cloud sync backend configuration (future)\n */\nexport interface CloudSyncConfig {\n /** Enable cloud sync (default: false) */\n enabled?: boolean;\n /** Cloud provider */\n provider?: 'aws' | 'gcp' | 'azure' | 'custom';\n /** Bucket/container name */\n bucket?: string;\n /** API endpoint (for custom provider) */\n endpoint?: string;\n /** API key or credentials */\n apiKey?: string;\n}\n\n/**\n * MongoDB sync backend configuration\n */\nexport interface MongoDbSyncConfig {\n /** Enable MongoDB sync (default: false) */\n enabled?: boolean;\n /** MongoDB connection URI */\n uri?: string;\n /** Database name */\n database?: string;\n /** Collection name (default: 'tokens') */\n collection?: string;\n /** Enable authentication */\n authEnabled?: boolean;\n /** Username (if authEnabled) */\n username?: string;\n /** Password (if authEnabled) */\n password?: string;\n}\n\n/**\n * Token sync configuration - supports multiple backends\n */\nexport interface TokenSyncConfig {\n /** IPFS sync backend */\n ipfs?: IpfsSyncConfig;\n /** File sync backend (future) */\n file?: FileSyncConfig;\n /** Cloud sync backend (future) */\n cloud?: CloudSyncConfig;\n /** MongoDB sync backend */\n mongodb?: MongoDbSyncConfig;\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 /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /**\n * Token sync backends configuration\n * Supports multiple backends: IPFS, file, cloud (future)\n * Each backend can be enabled/disabled independently\n */\n tokenSync?: TokenSyncConfig;\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 /** Token storage provider for local persistence (IndexedDB) */\n tokenStorage: TokenStorageProvider<TxfStorageDataBase>;\n /** L1 configuration (for passing to Sphere.init) */\n l1?: L1Config;\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n /** IPFS token storage provider (when tokenSync.ipfs.enabled is true) */\n ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\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 * Token sync configuration (resolved from tokenSync options)\n * For advanced use cases when additional sync backends are needed\n * @deprecated Use tokenStorage provider instead. For custom sync backends,\n * use Sphere.addTokenStorageProvider() after initialization.\n */\n tokenSyncConfig?: {\n ipfs?: {\n enabled: boolean;\n gateways: string[];\n bootstrapPeers?: string[];\n useDht?: boolean;\n };\n file?: {\n enabled: boolean;\n directory?: string;\n format?: 'json' | 'txf';\n };\n cloud?: {\n enabled: boolean;\n provider?: string;\n bucket?: string;\n endpoint?: string;\n apiKey?: string;\n };\n mongodb?: {\n enabled: boolean;\n uri?: string;\n database?: string;\n collection?: string;\n };\n };\n}\n\n// =============================================================================\n// Token Sync Resolution\n// =============================================================================\n\n/**\n * Resolve IPFS sync configuration with extend/override pattern\n */\nfunction resolveIpfsSyncConfig(\n network: NetworkType,\n config?: IpfsSyncConfig\n): NonNullable<BrowserProviders['tokenSyncConfig']>['ipfs'] | undefined {\n if (!config) return undefined;\n\n const networkConfig = getNetworkConfig(network);\n const gateways = resolveArrayConfig(\n networkConfig.ipfsGateways,\n config.gateways,\n config.additionalGateways\n );\n\n return {\n enabled: config.enabled ?? false,\n gateways,\n bootstrapPeers: config.bootstrapPeers ?? config.additionalBootstrapPeers,\n useDht: config.useDht,\n };\n}\n\n/**\n * Resolve all token sync backends\n */\nfunction resolveTokenSyncConfig(\n network: NetworkType,\n config?: TokenSyncConfig\n): BrowserProviders['tokenSyncConfig'] {\n if (!config) return undefined;\n\n const result: BrowserProviders['tokenSyncConfig'] = {};\n\n // IPFS backend\n const ipfs = resolveIpfsSyncConfig(network, config.ipfs);\n if (ipfs) result.ipfs = ipfs;\n\n // File backend\n if (config.file) {\n result.file = {\n enabled: config.file.enabled ?? false,\n directory: config.file.directory,\n format: config.file.format,\n };\n }\n\n // Cloud backend\n if (config.cloud) {\n result.cloud = {\n enabled: config.cloud.enabled ?? false,\n provider: config.cloud.provider,\n bucket: config.cloud.bucket,\n endpoint: config.cloud.endpoint,\n apiKey: config.cloud.apiKey,\n };\n }\n\n // MongoDB backend\n if (config.mongodb) {\n result.mongodb = {\n enabled: config.mongodb.enabled ?? false,\n uri: config.mongodb.uri,\n database: config.mongodb.database,\n collection: config.mongodb.collection,\n };\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\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 (tokenStorage is automatically included)\n * const { sphere } = await Sphere.init({\n * ...providers,\n * autoGenerate: true,\n * });\n *\n * // Add additional sync backends dynamically after init\n * // await sphere.addTokenStorageProvider(myMongoDbProvider);\n * ```\n */\nexport function createBrowserProviders(config?: BrowserProvidersConfig): BrowserProviders {\n const network = config?.network ?? 'mainnet';\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 const l1Config = resolveL1Config(network, config?.l1);\n const tokenSyncConfig = resolveTokenSyncConfig(network, config?.tokenSync);\n\n const storage = createIndexedDBStorageProvider(config?.storage);\n const priceConfig = resolvePriceConfig(config?.price, storage);\n\n // Create IPFS storage provider if enabled\n const ipfsConfig = tokenSyncConfig?.ipfs;\n const ipfsTokenStorage = ipfsConfig?.enabled\n ? createBrowserIpfsStorageProvider({\n gateways: ipfsConfig.gateways,\n debug: config?.tokenSync?.ipfs?.useDht, // reuse debug-like flag\n })\n : undefined;\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 tokenStorage: createIndexedDBTokenStorageProvider(),\n l1: l1Config,\n price: priceConfig ? createPriceProvider(priceConfig) : undefined,\n ipfsTokenStorage,\n tokenSyncConfig,\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 | 'STORAGE_ERROR'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\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 * 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 /** 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 (tokens, tombstones, archived, forked) is stored via\n * TokenStorageProvider, not here. This avoids duplication.\n */\nexport const STORAGE_KEYS_ADDRESS = {\n /** Pending transfers for this address */\n PENDING_TRANSFERS: 'pending_transfers',\n /** Transfer outbox for this address */\n OUTBOX: 'outbox',\n /** Conversations for this address */\n CONVERSATIONS: 'conversations',\n /** Messages for this address */\n MESSAGES: 'messages',\n /** Transaction history for this address */\n TRANSACTION_HISTORY: 'transaction_history',\n /** Pending V5 finalization tokens (unconfirmed instant split tokens) */\n PENDING_V5_TOKENS: 'pending_v5_tokens',\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 /** Processed V5 split group IDs for Nostr re-delivery dedup */\n PROCESSED_SPLIT_GROUP_IDS: 'processed_split_group_ids',\n /** Processed V6 combined transfer IDs for Nostr re-delivery dedup */\n PROCESSED_COMBINED_TRANSFER_IDS: 'processed_combined_transfer_ids',\n} as const;\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\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 /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\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/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n/** Default API key for aggregator authentication */\nexport const DEFAULT_AGGREGATOR_API_KEY = 'sk_06365a9c44654841a366068bcfc68986' as const;\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://unicity-ipfs1.dyndns.org',\n] as const;\n\n/** Unicity IPFS bootstrap peers */\nexport const DEFAULT_IPFS_BOOTSTRAP_PEERS = [\n '/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh',\n '/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6',\n '/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1',\n '/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv',\n] as const;\n\n/** Unicity dedicated IPFS nodes (HTTP API access) */\nexport const UNICITY_IPFS_NODES = [\n {\n host: 'unicity-ipfs1.dyndns.org',\n peerId: '12D3KooWDKJqEMAhH4nsSSiKtK1VLcas5coUqSPZAfbWbZpxtL4u',\n httpPort: 9080,\n httpsPort: 443,\n },\n] as const;\n\n/**\n * Get IPFS gateway URLs for HTTP API access.\n * @param isSecure - Use HTTPS (default: true). Set false for development.\n */\nexport function getIpfsGatewayUrls(isSecure?: boolean): string[] {\n return UNICITY_IPFS_NODES.map((node) =>\n isSecure !== false\n ? `https://${node.host}`\n : `http://${node.host}:${node.httpPort}`,\n );\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 /** ALPHA token (L1 blockchain) */\n ALPHA: 'ALPHA',\n /** Test token */\n TEST: 'TEST',\n} as const;\n\n// =============================================================================\n// L1 (ALPHA Blockchain) Defaults\n// =============================================================================\n\n/** Default Fulcrum electrum server for mainnet */\nexport const DEFAULT_ELECTRUM_URL = 'wss://fulcrum.unicity.network:50004' as const;\n\n/** Testnet Fulcrum electrum server */\nexport const TEST_ELECTRUM_URL = 'wss://fulcrum.unicity.network:50004' 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/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: DEFAULT_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n testnet: {\n name: 'Testnet',\n aggregatorUrl: TEST_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const;\n\nexport type NetworkType = keyof typeof NETWORKS;\nexport type NetworkConfig = (typeof NETWORKS)[NetworkType];\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, getAddressId } 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 /** 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 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 }\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.l1Address);\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\n if (isPerAddressKey && this.identity?.directAddress) {\n // Add address ID prefix for per-address data\n const addressId = getAddressId(this.identity.directAddress);\n return `${this.config.prefix}${addressId}_${key}`;\n }\n\n // Global key - no address prefix\n return `${this.config.prefix}${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, getAddressId } 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 /** 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 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.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.l1Address);\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 // 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\n if (isPerAddressKey && this.identity?.directAddress) {\n // Add address ID prefix for per-address data\n const addressId = getAddressId(this.identity.directAddress);\n return `${this.prefix}${addressId}_${key}`;\n }\n\n // Global key - no address prefix\n return `${this.prefix}${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 * IndexedDB Token Storage Provider for Browser\n * Stores tokens in IndexedDB for persistent browser storage\n * Each address gets its own database for multi-address support\n */\n\nimport { logger } from '../../../core/logger';\nimport type { TokenStorageProvider, TxfStorageDataBase, SyncResult, SaveResult, LoadResult, HistoryRecord } from '../../../storage';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport { getAddressId } from '../../../constants';\n\n// Re-export HistoryRecord for backwards compat\nexport type { HistoryRecord } from '../../../storage';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-token-storage';\nconst DB_VERSION = 2;\nconst STORE_TOKENS = 'tokens';\nconst STORE_META = 'meta';\nconst STORE_HISTORY = 'history';\n\nexport interface IndexedDBTokenStorageConfig {\n /** Database name prefix (default: 'sphere-token-storage') */\n dbNamePrefix?: string;\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 IndexedDBTokenStorageProvider implements TokenStorageProvider<TxfStorageDataBase> {\n readonly id = 'indexeddb-token-storage';\n readonly name = 'IndexedDB Token Storage';\n readonly type = 'local' as const;\n\n private dbNamePrefix: string;\n private dbName: string;\n private debug: boolean;\n private db: IDBDatabase | null = null;\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBTokenStorageConfig) {\n this.dbNamePrefix = config?.dbNamePrefix ?? DB_NAME;\n this.dbName = this.dbNamePrefix;\n this.debug = config?.debug ?? false;\n }\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n // Scope database to address using consistent addressId format\n if (identity.directAddress) {\n const addressId = getAddressId(identity.directAddress);\n this.dbName = `${this.dbNamePrefix}-${addressId}`;\n }\n logger.debug('IndexedDBToken', `setIdentity: db=${this.dbName}`);\n }\n\n async initialize(): Promise<boolean> {\n const prevConnId = this.connId;\n const t0 = Date.now();\n try {\n // Close any existing connection before opening a new one\n // (e.g. when switching addresses — prevents leaked IDB connections)\n if (this.db) {\n logger.debug('IndexedDBToken', `initialize: closing existing connId=${prevConnId} before re-open (db=${this.dbName})`);\n this.db.close();\n this.db = null;\n }\n\n logger.debug('IndexedDBToken', `initialize: opening db=${this.dbName}`);\n this.db = await this.openDatabase();\n this.status = 'connected';\n logger.debug('IndexedDBToken', `initialize: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return true;\n } catch (error) {\n logger.error('IndexedDBToken', `initialize: failed db=${this.dbName} (${Date.now() - t0}ms):`, error);\n this.status = 'error';\n return false;\n }\n }\n\n async shutdown(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDBToken', `shutdown: 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 async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n await this.shutdown();\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 async load(): Promise<LoadResult<TxfStorageDataBase>> {\n if (!this.db) {\n logger.warn('IndexedDBToken', `load: db not initialized (db=${this.dbName})`);\n return {\n success: false,\n error: 'Database not initialized',\n source: 'local',\n timestamp: Date.now(),\n };\n }\n\n try {\n const data: TxfStorageDataBase = {\n _meta: {\n version: 1,\n address: this.identity?.l1Address ?? '',\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n };\n\n // Load meta\n const meta = await this.getFromStore<TxfStorageDataBase['_meta']>(STORE_META, 'meta');\n if (meta) {\n data._meta = meta;\n }\n\n // Load all tokens from store\n const tokens = await this.getAllFromStore<{ id: string; data: unknown }>(STORE_TOKENS);\n for (const token of tokens) {\n // Skip file-format entries (token-, nametag-) - they are loaded via loadTokensFromFileStorage\n if (token.id.startsWith('token-') || token.id.startsWith('nametag-')) {\n continue;\n }\n\n if (token.id.startsWith('archived-')) {\n // Archived tokens: keep as-is (archived-tokenId key)\n data[token.id as keyof TxfStorageDataBase] = token.data;\n } else {\n // Other entries: add _ prefix for TXF format\n const key = `_${token.id}` as `_${string}`;\n data[key] = token.data;\n }\n }\n\n // Load tombstones\n const tombstones = await this.getFromStore<TxfStorageDataBase['_tombstones']>(STORE_META, 'tombstones');\n if (tombstones) {\n data._tombstones = tombstones;\n }\n\n // Load outbox\n const outbox = await this.getFromStore<TxfStorageDataBase['_outbox']>(STORE_META, 'outbox');\n if (outbox) {\n data._outbox = outbox;\n }\n\n // Load sent\n const sent = await this.getFromStore<TxfStorageDataBase['_sent']>(STORE_META, 'sent');\n if (sent) {\n data._sent = sent;\n }\n\n // Load invalid\n const invalid = await this.getFromStore<TxfStorageDataBase['_invalid']>(STORE_META, 'invalid');\n if (invalid) {\n data._invalid = invalid;\n }\n\n const tokenKeys = Object.keys(data).filter(k => k.startsWith('_') && !['_meta', '_tombstones', '_outbox', '_sent', '_invalid'].includes(k));\n logger.debug('IndexedDBToken', `load: db=${this.dbName}, tokens=${tokenKeys.length}`);\n\n return {\n success: true,\n data,\n source: 'local',\n timestamp: Date.now(),\n };\n } catch (error) {\n logger.error('IndexedDBToken', `load failed: db=${this.dbName}`, error);\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n source: 'local',\n timestamp: Date.now(),\n };\n }\n }\n\n async save(data: TxfStorageDataBase): Promise<SaveResult> {\n if (!this.db) {\n logger.warn('IndexedDBToken', `save: db not initialized (db=${this.dbName})`);\n return {\n success: false,\n error: 'Database not initialized',\n timestamp: Date.now(),\n };\n }\n\n try {\n const tokenKeys = Object.keys(data).filter(k => k.startsWith('_') && !['_meta', '_tombstones', '_outbox', '_sent', '_invalid'].includes(k));\n const archivedKeys = Object.keys(data).filter(k => k.startsWith('archived-'));\n logger.debug('IndexedDBToken', `save: db=${this.dbName}, tokens=${tokenKeys.length}, archived=${archivedKeys.length}, tombstones=${data._tombstones?.length ?? 0}`);\n\n // Save meta\n await this.putToStore(STORE_META, 'meta', data._meta);\n\n // Save special arrays\n if (data._tombstones) {\n await this.putToStore(STORE_META, 'tombstones', data._tombstones);\n }\n if (data._outbox) {\n await this.putToStore(STORE_META, 'outbox', data._outbox);\n }\n if (data._sent) {\n await this.putToStore(STORE_META, 'sent', data._sent);\n }\n if (data._invalid) {\n await this.putToStore(STORE_META, 'invalid', data._invalid);\n }\n\n // Save each token (active tokens start with _, archived with archived-)\n const reservedKeys = ['_meta', '_tombstones', '_outbox', '_sent', '_invalid'];\n for (const [key, value] of Object.entries(data)) {\n if (reservedKeys.includes(key)) continue;\n\n if (key.startsWith('_')) {\n // Active token: _tokenId -> tokenId\n const tokenId = key.slice(1);\n await this.putToStore(STORE_TOKENS, tokenId, { id: tokenId, data: value });\n } else if (key.startsWith('archived-')) {\n // Archived token: archived-tokenId -> archived-tokenId (keep prefix)\n await this.putToStore(STORE_TOKENS, key, { id: key, data: value });\n }\n }\n\n // Handle tombstones - delete tokens\n if (data._tombstones) {\n for (const tombstone of data._tombstones) {\n await this.deleteFromStore(STORE_TOKENS, tombstone.tokenId);\n }\n }\n\n return {\n success: true,\n timestamp: Date.now(),\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n timestamp: Date.now(),\n };\n }\n }\n\n async sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>> {\n // For local IndexedDB storage, just save and return\n const saveResult = await this.save(localData);\n return {\n success: saveResult.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: saveResult.error,\n };\n }\n\n async exists(): Promise<boolean> {\n if (!this.db) return false;\n const meta = await this.getFromStore(STORE_META, 'meta');\n return meta !== null;\n }\n\n async clear(): Promise<boolean> {\n // Uses IDBObjectStore.clear() instead of deleteDatabase().\n // deleteDatabase() is a schema operation that gets blocked by leaked IDB\n // connections (React StrictMode, multiple tabs) and leaves a pending delete\n // that blocks ALL subsequent open() calls, bricking the wallet.\n // store.clear() is a normal readwrite transaction — cannot be blocked.\n const t0 = Date.now();\n try {\n // 1. Close own connection\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n\n // 2. Collect all databases with our prefix (current + other addresses)\n const dbNames = new Set<string>([this.dbName]);\n for (const name of await this.findPrefixedDatabases()) {\n dbNames.add(name);\n }\n\n // 3. Clear stores in each database in parallel\n logger.debug('IndexedDBToken', `clear: clearing ${dbNames.size} database(s) (${[...dbNames].join(', ')})`);\n const results = await Promise.allSettled(\n [...dbNames].map((name) => this.clearDatabaseStores(name)),\n );\n\n const failed = results.filter((r) => r.status === 'rejected');\n if (failed.length > 0) {\n logger.warn('IndexedDBToken', `clear: ${failed.length}/${dbNames.size} failed (${Date.now() - t0}ms)`,\n failed.map((r) => (r as PromiseRejectedResult).reason));\n }\n\n logger.debug('IndexedDBToken', `clear: done ${dbNames.size} database(s) (${Date.now() - t0}ms)`);\n return failed.length === 0;\n } catch (err) {\n logger.warn('IndexedDBToken', `clear: failed (${Date.now() - t0}ms)`, err);\n return false;\n }\n }\n\n // =========================================================================\n // Private IndexedDB helpers\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('IndexedDBToken', `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 holds the database.\n // Log it — onsuccess will follow once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDBToken', `open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create tokens store\n if (!db.objectStoreNames.contains(STORE_TOKENS)) {\n db.createObjectStore(STORE_TOKENS, { keyPath: 'id' });\n }\n\n // Create meta store\n if (!db.objectStoreNames.contains(STORE_META)) {\n db.createObjectStore(STORE_META);\n }\n\n // Create history store (v2)\n if (!db.objectStoreNames.contains(STORE_HISTORY)) {\n db.createObjectStore(STORE_HISTORY, { keyPath: 'dedupKey' });\n }\n };\n });\n }\n\n private getFromStore<T>(storeName: string, key: string): Promise<T | null> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve(null);\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readonly');\n const store = transaction.objectStore(storeName);\n const request = store.get(key);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? null);\n });\n }\n\n private getAllFromStore<T>(storeName: string): Promise<T[]> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve([]);\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readonly');\n const store = transaction.objectStore(storeName);\n const request = store.getAll();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private putToStore(storeName: string, key: string, value: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n reject(new Error('Database not initialized'));\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n\n // Meta store: no keyPath, use explicit key\n // Tokens store: keyPath 'id'\n // History store: keyPath 'dedupKey'\n const request = storeName === STORE_META\n ? store.put(value, key)\n : store.put(value);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private deleteFromStore(storeName: string, key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.db) {\n resolve();\n return;\n }\n\n const transaction = this.db.transaction(storeName, 'readwrite');\n const store = transaction.objectStore(storeName);\n const request = store.delete(key);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n /**\n * Find all IndexedDB databases with our prefix.\n * Returns empty array if indexedDB.databases() is unavailable (older browsers).\n */\n private async findPrefixedDatabases(): Promise<string[]> {\n if (typeof indexedDB.databases !== 'function') return [];\n try {\n const allDbs = await Promise.race([\n indexedDB.databases(),\n new Promise<IDBDatabaseInfo[]>((_, reject) =>\n setTimeout(() => reject(new Error('databases() timed out')), 1500),\n ),\n ]);\n return allDbs\n .map((info) => info.name)\n .filter((name): name is string => !!name && name.startsWith(this.dbNamePrefix));\n } catch {\n return [];\n }\n }\n\n // =========================================================================\n // Public: History operations\n // =========================================================================\n\n /**\n * Add a history entry. Uses `put` (upsert by dedupKey) so duplicate\n * calls with the same dedupKey simply overwrite — no duplicates.\n */\n async addHistoryEntry(entry: HistoryRecord): Promise<void> {\n await this.putToStore(STORE_HISTORY, entry.dedupKey, entry);\n }\n\n /**\n * Get all history entries sorted by timestamp descending.\n */\n async getHistoryEntries(): Promise<HistoryRecord[]> {\n const entries = await this.getAllFromStore<HistoryRecord>(STORE_HISTORY);\n return entries.sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Check if a history entry with the given dedupKey exists.\n */\n async hasHistoryEntry(dedupKey: string): Promise<boolean> {\n const entry = await this.getFromStore<HistoryRecord>(STORE_HISTORY, dedupKey);\n return entry !== null;\n }\n\n /**\n * Clear all history entries.\n */\n async clearHistory(): Promise<void> {\n if (!this.db) return;\n await new Promise<void>((resolve, reject) => {\n const tx = this.db!.transaction(STORE_HISTORY, 'readwrite');\n const req = tx.objectStore(STORE_HISTORY).clear();\n req.onerror = () => reject(req.error);\n req.onsuccess = () => resolve();\n });\n }\n\n /**\n * Bulk import history entries. Entries with existing dedupKeys are\n * skipped (first-write-wins). Returns the number of newly imported entries.\n */\n async importHistoryEntries(entries: HistoryRecord[]): Promise<number> {\n if (!this.db || entries.length === 0) return 0;\n let imported = 0;\n for (const entry of entries) {\n const exists = await this.hasHistoryEntry(entry.dedupKey);\n if (!exists) {\n await this.addHistoryEntry(entry);\n imported++;\n }\n }\n return imported;\n }\n\n // =========================================================================\n // Private IndexedDB helpers (clear)\n // =========================================================================\n\n /**\n * Clear all object stores in a single database.\n * Opens a temporary connection, clears STORE_TOKENS and STORE_META, then closes.\n * Uses IDBObjectStore.clear() which is a normal readwrite transaction — cannot\n * be blocked by other connections (unlike deleteDatabase()).\n */\n private async clearDatabaseStores(dbName: string): Promise<void> {\n const db = await Promise.race([\n new Promise<IDBDatabase>((resolve, reject) => {\n const req = indexedDB.open(dbName, DB_VERSION);\n req.onerror = () => reject(req.error);\n req.onsuccess = () => {\n const db = req.result;\n db.onversionchange = () => { db.close(); };\n resolve(db);\n };\n req.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_TOKENS)) {\n db.createObjectStore(STORE_TOKENS, { keyPath: 'id' });\n }\n if (!db.objectStoreNames.contains(STORE_META)) {\n db.createObjectStore(STORE_META);\n }\n if (!db.objectStoreNames.contains(STORE_HISTORY)) {\n db.createObjectStore(STORE_HISTORY, { keyPath: 'dedupKey' });\n }\n };\n }),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`open timed out: ${dbName}`)), 3000),\n ),\n ]);\n\n try {\n for (const storeName of [STORE_TOKENS, STORE_META, STORE_HISTORY]) {\n if (db.objectStoreNames.contains(storeName)) {\n await new Promise<void>((resolve, reject) => {\n const tx = db.transaction(storeName, 'readwrite');\n const req = tx.objectStore(storeName).clear();\n req.onerror = () => reject(req.error);\n req.onsuccess = () => resolve();\n });\n }\n }\n } finally {\n db.close();\n }\n }\n}\n\nexport function createIndexedDBTokenStorageProvider(\n config?: IndexedDBTokenStorageConfig\n): IndexedDBTokenStorageProvider {\n return new IndexedDBTokenStorageProvider(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 TokenTransferHandler,\n BroadcastHandler,\n PaymentRequestHandler,\n PaymentRequestResponseHandler,\n IncomingMessage,\n IncomingTokenTransfer,\n IncomingBroadcast,\n IncomingPaymentRequest,\n IncomingPaymentRequestResponse,\n TokenTransferPayload,\n PaymentRequestPayload,\n PaymentRequestResponsePayload,\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// 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 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 transferHandlers: Set<TokenTransferHandler> = new Set();\n private paymentRequestHandlers: Set<PaymentRequestHandler> = new Set();\n private paymentRequestResponseHandlers: Set<PaymentRequestResponseHandler> = 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 // 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 });\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 },\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.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 // 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 });\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 /**\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.publishEvent(giftWrap);\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 async sendTokenTransfer(\n recipientPubkey: string,\n payload: TokenTransferPayload\n ): Promise<string> {\n this.ensureReady();\n\n // Create encrypted token transfer event\n // Content must have \"token_transfer:\" prefix for nostr-js-sdk compatibility\n const content = 'token_transfer:' + JSON.stringify(payload);\n\n // IMPORTANT: kind 31113 is a Parameterized Replaceable Event (NIP-01).\n // The relay keeps only the LATEST event per (pubkey, kind, d-tag).\n // A static d-tag like 'token-transfer' caused subsequent sends to OVERWRITE\n // previous ones on the relay — the recipient only saw the last token sent.\n // Fix: use a unique d-tag per event so each transfer is its own slot.\n const uniqueD = `token-transfer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.TOKEN_TRANSFER,\n content,\n [\n ['p', recipientPubkey],\n ['d', uniqueD],\n ['type', 'token_transfer'],\n ]\n );\n\n await this.publishEvent(event);\n\n this.emitEvent({\n type: 'transfer:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return event.id;\n }\n\n onTokenTransfer(handler: TokenTransferHandler): () => void {\n this.transferHandlers.add(handler);\n return () => this.transferHandlers.delete(handler);\n }\n\n async sendPaymentRequest(\n recipientPubkey: string,\n payload: PaymentRequestPayload\n ): Promise<string> {\n this.ensureReady();\n\n const requestId = this.config.generateUUID();\n const amount = typeof payload.amount === 'bigint' ? payload.amount.toString() : payload.amount;\n\n // Build request content matching nostr-js-sdk format\n const requestContent = {\n requestId,\n amount,\n coinId: payload.coinId,\n message: payload.message,\n recipientNametag: payload.recipientNametag,\n deadline: Date.now() + 5 * 60 * 1000, // 5 minutes default\n };\n\n // Content must have \"payment_request:\" prefix for nostr-js-sdk compatibility\n const content = 'payment_request:' + JSON.stringify(requestContent);\n\n // Build tags matching nostr-js-sdk format\n const tags: string[][] = [\n ['p', recipientPubkey],\n ['type', 'payment_request'],\n ['amount', amount],\n ];\n if (payload.recipientNametag) {\n tags.push(['recipient', payload.recipientNametag]);\n }\n\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.PAYMENT_REQUEST,\n content,\n tags\n );\n\n await this.publishEvent(event);\n\n logger.debug('Nostr', 'Sent payment request:', event.id);\n\n return event.id;\n }\n\n onPaymentRequest(handler: PaymentRequestHandler): () => void {\n this.paymentRequestHandlers.add(handler);\n return () => this.paymentRequestHandlers.delete(handler);\n }\n\n async sendPaymentRequestResponse(\n recipientPubkey: string,\n payload: PaymentRequestResponsePayload\n ): Promise<string> {\n this.ensureReady();\n\n // Build response content\n const responseContent = {\n requestId: payload.requestId,\n responseType: payload.responseType,\n message: payload.message,\n transferId: payload.transferId,\n };\n\n // Create encrypted payment request response event\n // Content must have \"payment_response:\" prefix for nostr-js-sdk compatibility\n const content = 'payment_response:' + JSON.stringify(responseContent);\n const event = await this.createEncryptedEvent(\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n content,\n [\n ['p', recipientPubkey],\n ['e', payload.requestId], // Reference to original request\n ['d', 'payment-request-response'],\n ['type', 'payment_response'],\n ]\n );\n\n await this.publishEvent(event);\n\n logger.debug('Nostr', 'Sent payment request response:', event.id, 'type:', payload.responseType);\n\n return event.id;\n }\n\n onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void {\n this.paymentRequestResponseHandlers.add(handler);\n return () => this.paymentRequestResponseHandlers.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 // ===========================================================================\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:// or PROXY:// address\n if (identifier.startsWith('DIRECT:') || identifier.startsWith('PROXY:')) {\n return this.resolveAddressInfo(identifier);\n }\n\n // L1 address (alpha1... or alphat1...)\n if (identifier.startsWith('alpha1') || identifier.startsWith('alphat1')) {\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 this.ensureConnected();\n // Delegate to nostr-js-sdk which implements first-seen-wins anti-hijacking\n return this.nostrClient!.queryPubkeyByNametag(nametag);\n }\n\n async resolveNametagInfo(nametag: string): Promise<PeerInfo | null> {\n this.ensureConnected();\n\n // Delegate to nostr-js-sdk which implements first-seen-wins anti-hijacking\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://, PROXY://, or L1 address to full peer info.\n * Performs reverse lookup via nostr-js-sdk with first-seen-wins anti-hijacking.\n */\n async resolveAddressInfo(address: string): Promise<PeerInfo | null> {\n this.ensureConnected();\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 * Computes PROXY address from nametag if available.\n */\n private async bindingInfoToPeerInfo(binding: BindingInfo, nametag?: string): Promise<PeerInfo> {\n const nametagValue = nametag || binding.nametag;\n let proxyAddress: string | undefined = binding.proxyAddress;\n\n // Compute PROXY address from nametag if not already in binding\n if (nametagValue && !proxyAddress) {\n try {\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametagValue);\n proxyAddress = proxyAddr.toString();\n } catch {\n // Ignore — proxy address computation is best-effort\n }\n }\n\n return {\n nametag: nametagValue,\n transportPubkey: binding.transportPubkey,\n chainPubkey: binding.publicKey || '',\n l1Address: binding.l1Address || '',\n directAddress: binding.directAddress || '',\n proxyAddress,\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 this.ensureConnected();\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\n return {\n nametag: content.nametag || undefined,\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: content.public_key || '',\n l1Address: content.l1_address || '',\n directAddress: content.direct_address || '',\n proxyAddress: content.proxy_address || undefined,\n timestamp: bindingEvent.created_at * 1000,\n };\n } catch {\n return {\n transportPubkey: bindingEvent.pubkey,\n chainPubkey: '',\n l1Address: '',\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 this.ensureConnected();\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 results.push({\n nametag: content.nametag || undefined,\n transportPubkey: pubkey,\n chainPubkey: content.public_key || '',\n l1Address: content.l1_address || '',\n directAddress: content.direct_address || '',\n proxyAddress: content.proxy_address || undefined,\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 this.ensureReady();\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, l1Address, 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 (first-seen-wins), 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 l1Address: 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 const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n\n try {\n const success = await this.nostrClient!.publishNametagBinding(\n nametag,\n nostrPubkey,\n {\n publicKey: chainPubkey,\n l1Address,\n directAddress,\n proxyAddress: proxyAddr.toString(),\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 l1Address,\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.TOKEN_TRANSFER:\n await this.handleTokenTransfer(event);\n break;\n case EVENT_KINDS.PAYMENT_REQUEST:\n await this.handlePaymentRequest(event);\n break;\n case EVENT_KINDS.PAYMENT_REQUEST_RESPONSE:\n await this.handlePaymentRequestResponse(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 const kind = event.kind;\n if (\n kind === EVENT_KINDS.DIRECT_MESSAGE ||\n kind === EVENT_KINDS.TOKEN_TRANSFER ||\n kind === EVENT_KINDS.PAYMENT_REQUEST ||\n kind === EVENT_KINDS.PAYMENT_REQUEST_RESPONSE\n ) {\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 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 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 async handleTokenTransfer(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const payload = JSON.parse(content) as TokenTransferPayload;\n\n const transfer: IncomingTokenTransfer = {\n id: event.id,\n senderTransportPubkey: event.pubkey,\n payload,\n timestamp: event.created_at * 1000,\n };\n\n this.emitEvent({ type: 'transfer:received', timestamp: Date.now() });\n\n for (const handler of this.transferHandlers) {\n try {\n await handler(transfer);\n } catch (error) {\n logger.debug('Nostr', 'Transfer handler error:', error);\n }\n }\n }\n\n private async handlePaymentRequest(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n try {\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const requestData = JSON.parse(content) as {\n requestId: string;\n amount: string;\n coinId: string;\n message?: string;\n recipientNametag?: string;\n metadata?: Record<string, unknown>;\n };\n\n const request: IncomingPaymentRequest = {\n id: event.id,\n senderTransportPubkey: event.pubkey,\n senderNametag: requestData.recipientNametag,\n request: {\n requestId: requestData.requestId,\n amount: requestData.amount,\n coinId: requestData.coinId,\n message: requestData.message,\n recipientNametag: requestData.recipientNametag,\n metadata: requestData.metadata,\n },\n timestamp: event.created_at * 1000,\n };\n\n logger.debug('Nostr', 'Received payment request:', request.id);\n\n for (const handler of this.paymentRequestHandlers) {\n try {\n handler(request);\n } catch (error) {\n logger.debug('Nostr', 'Payment request handler error:', error);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle payment request:', error);\n }\n }\n\n private async handlePaymentRequestResponse(event: NostrEvent): Promise<void> {\n if (!this.identity) return;\n\n try {\n // Decrypt content\n const content = await this.decryptContent(event.content, event.pubkey);\n const responseData = JSON.parse(content) as {\n requestId: string;\n responseType: 'accepted' | 'rejected' | 'paid';\n message?: string;\n transferId?: string;\n };\n\n const response: IncomingPaymentRequestResponse = {\n id: event.id,\n responderTransportPubkey: event.pubkey,\n response: {\n requestId: responseData.requestId,\n responseType: responseData.responseType,\n message: responseData.message,\n transferId: responseData.transferId,\n },\n timestamp: event.created_at * 1000,\n };\n\n logger.debug('Nostr', 'Received payment request response:', response.id, 'type:', responseData.responseType);\n\n for (const handler of this.paymentRequestResponseHandlers) {\n try {\n handler(response);\n } catch (error) {\n logger.debug('Nostr', 'Payment request response handler error:', error);\n }\n }\n } catch (error) {\n logger.debug('Nostr', 'Failed to handle payment request response:', error);\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 // Convert to nostr-js-sdk Event and publish\n const sdkEvent = NostrEventClass.fromJSON(event);\n await this.nostrClient.publishEvent(sdkEvent);\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 EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n ];\n walletFilter['#p'] = [nostrPubkey];\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n if (subId) this.nostrClient?.unsubscribe(subId);\n resolve();\n }, 5000);\n\n const subId = this.nostrClient!.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: () => {\n clearTimeout(timeout);\n this.nostrClient?.unsubscribe(subId);\n resolve();\n },\n });\n });\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 const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n if (subId) {\n this.nostrClient?.unsubscribe(subId);\n }\n logger.warn('Nostr', `queryEvents timed out after 5s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 5000);\n\n const subId = this.nostrClient!.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: () => {\n clearTimeout(timeout);\n this.nostrClient?.unsubscribe(subId);\n resolve(events);\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 private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\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 logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else {\n // No stored timestamp = 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 }\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 (token transfers, payment requests) with since filter\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\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 // Subscribe to chat events (NIP-17 gift wrap) WITHOUT since filter\n // This matches Sphere app's approach - chat messages rely on deduplication\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NO since filter for chat - we want real-time messages\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 },\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 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 const senderPubkey = this.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), this.keyManager!.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(this.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/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */\nexport function isBytes(a: unknown): a is Uint8Array {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n\n/** Asserts something is positive integer. */\nexport function anumber(n: number, title: string = ''): void {\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new Error(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/** Asserts something is Uint8Array. */\nexport function abytes(value: Uint8Array, length?: number, title: string = ''): 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 throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);\n }\n return value;\n}\n\n/** Asserts something is hash */\nexport function ahash(h: CHash): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new Error('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n}\n\n/** Asserts a hash instance has not been destroyed / finished */\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/** Asserts output is properly-sized byte array */\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 Error('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/** Cast u8 / u16 / u32 to u8. */\nexport function u8(arr: TypedArray): Uint8Array {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** Cast u8 / u16 / u32 to u32. */\nexport function u32(arr: TypedArray): Uint32Array {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n\n/** Zeroize a byte array. Warning: JS provides no guarantees. */\nexport function clean(...arrays: TypedArray[]): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/** Create DataView of an array for easy byte-level manipulation. */\nexport function createView(arr: TypedArray): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** The rotate right (circular right shift) operation for uint32 */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/** The rotate left (circular left shift) operation for uint32 */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Is current platform little-endian? Most are. Big-Endian platform: IBM */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/** The byte swap operation for uint32 */\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/** Conditionally byte swap if on a big-endian platform */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n);\n\n/** In place byte swap for Uint32Array */\nexport function byteSwap32(arr: Uint32Array): Uint32Array {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n\nexport const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE\n ? (u: Uint32Array) => u\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. Uses built-in function, when available.\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes: 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 * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex: string): Uint8Array {\n if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);\n // @ts-ignore\n if (hasHexBuiltin) return Uint8Array.fromHex(hex);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new Error('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 Error('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\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 * Call of async fn will return Promise, which will be fullfiled only on\n * next scheduler queue processing step and this is exactly what we need.\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/** Returns control to thread each 'tick' ms to avoid blocking. */\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 * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])\n */\nexport function utf8ToBytes(str: string): Uint8Array {\n if (typeof str !== 'string') throw new Error('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 * When string is passed, does utf8 decoding, using TextDecoder.\n */\nexport function kdfInputToBytes(data: KDFInput, errorTitle = ''): Uint8Array {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/** Copies several Uint8Arrays into one. */\nexport function concatBytes(...arrays: Uint8Array[]): 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/** Merges default options and passed options. */\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 Error('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 hashes. */\nexport interface Hash<T> {\n blockLen: number; // Bytes per block\n outputLen: number; // Bytes in output\n update(buf: Uint8Array): this;\n digestInto(buf: Uint8Array): void;\n digest(): Uint8Array;\n destroy(): void;\n _cloneInto(to?: T): T;\n clone(): T;\n}\n\n/** PseudoRandom (number) Generator */\nexport interface PRG {\n addEntropy(seed: Uint8Array): void;\n randomBytes(length: number): Uint8Array;\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 xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream\n xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf\n};\n\n/** Hash constructor */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash params. */\nexport type HashInfo = {\n oid?: Uint8Array; // DER encoded OID in bytes\n};\n/** Hash function */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n outputLen: number;\n blockLen: number;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: Uint8Array): Uint8Array;\n create(): T;\n }\n : {\n (msg: Uint8Array, opts?: Opts): Uint8Array;\n create(opts?: Opts): T;\n });\n/** XOF with output */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/** Creates function with outputLen, blockLen, create properties from a class constructor. */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: HashInfo = {}\n): CHash<T, Opts> {\n const hashC: any = (msg: Uint8Array, opts?: Opts) => hashCons(opts).update(msg).digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC);\n}\n\n/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */\nexport function randomBytes(bytesLength = 32): Uint8Array {\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 return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/** Creates OID opts for NIST hashes, with prefix 06 09 60 86 48 01 65 03 04 02. */\nexport const oidNist = (suffix: number): Required<HashInfo> => ({\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 { abytes, aexists, aoutput, clean, createView, type Hash } from './utils.ts';\n\n/** Choice: a ? b : c */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/** Majority function, true if any two inputs is true. */\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 */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\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 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: 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: we have at least one block in input, cast it to view and process\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: 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 // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\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(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\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 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. Bits 0..32 of frac part of sqrt of primes 2..19 */\nexport const SHA256_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state. Bits 32..64 of frac part of sqrt of primes 23..53 */\nexport const SHA224_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state. Bits 0..64 of frac part of sqrt of primes 23..53 */\nexport const SHA384_IV: 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. Bits 0..64 of frac part of sqrt of primes 2..19 */\nexport const SHA512_IV: 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 [RFC 4634](https://www.rfc-editor.org/rfc/rfc4634) and\n * [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\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 } from './utils.ts';\n\n/**\n * Round constants:\n * First 32 bits of fractional parts 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 temporary buffer. \"W\" comes straight from spec. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal 32-byte base SHA2 hash class. */\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 this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA2-256 hash class. */\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 SHA2-224 hash class. */\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// Round contants\n// First 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409\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 temporary buffers\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal 64-byte base SHA2 hash class. */\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 // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_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 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 SHA2-512 hash class. */\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 SHA2-384 hash class. */\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 `test/misc/sha2-gen-iv.js`.\n */\n\n/** SHA512/224 IV */\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/** SHA512/256 IV */\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 SHA2-512/224 hash class. */\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 SHA2-512/256 hash class. */\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 */\nexport const sha256: CHash<_SHA256> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/** SHA2-224 hash function from RFC 4634 */\nexport const sha224: CHash<_SHA224> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/** SHA2-512 hash function from RFC 4634. */\nexport const sha512: CHash<_SHA512> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/** SHA2-384 hash function from RFC 4634. */\nexport const sha384: 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 [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_256: 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 [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_224: 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 * Platform-independent implementation using @unicitylabs/state-transition-sdk\n *\n * The oracle is a trusted service that provides verifiable truth\n * about token state through cryptographic inclusion proofs.\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 TransferCommitment,\n SubmitResult,\n InclusionProof,\n WaitOptions,\n ValidationResult,\n TokenState,\n MintParams,\n MintResult,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT, TIMEOUTS } from '../constants';\nimport { SphereError } from '../core/errors';\n\n// SDK imports - using direct imports from the SDK\nimport { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/AggregatorClient';\nimport { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { TransferCommitment as SdkTransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\n\n// SDK MintCommitment type - using interface to avoid generic complexity\ninterface SdkMintCommitment {\n requestId?: { toString(): string };\n [key: string]: unknown;\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base verification (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// RPC Response Types\n// =============================================================================\n\ninterface RpcSubmitResponse {\n requestId?: string;\n}\n\ninterface RpcProofResponse {\n proof?: unknown;\n roundNumber?: number;\n}\n\ninterface RpcValidateResponse {\n valid?: boolean;\n spent?: boolean;\n stateHash?: string;\n error?: string;\n}\n\ninterface RpcSpentResponse {\n spent?: boolean;\n}\n\ninterface RpcTokenStateResponse {\n state?: {\n stateHash?: string;\n spent?: boolean;\n roundNumber?: number;\n };\n}\n\ninterface RpcMintResponse {\n requestId?: string;\n tokenId?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Unicity Aggregator Provider\n * Concrete implementation of OracleProvider using Unicity's aggregator service\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 state transition aggregator (oracle implementation)';\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 // SDK clients\n private aggregatorClient: AggregatorClient | null = null;\n private stateTransitionClient: StateTransitionClient | null = null;\n private trustBase: RootTrustBase | null = null;\n\n /** Get the current trust base */\n getTrustBase(): RootTrustBase | null {\n return this.trustBase;\n }\n\n /** Get the state transition client */\n getStateTransitionClient(): StateTransitionClient | null {\n return this.stateTransitionClient;\n }\n\n /** Get the aggregator client */\n getAggregatorClient(): AggregatorClient | null {\n return this.aggregatorClient;\n }\n\n // Cache for spent states (immutable)\n private spentCache: Map<string, boolean> = new Map();\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 // 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 will be verified on first operation\n // The aggregator requires requestId in params even for status checks,\n // which the SDK client doesn't support directly\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(trustBase?: RootTrustBase): Promise<void> {\n // Initialize SDK clients with optional API key\n this.aggregatorClient = new AggregatorClient(\n this.config.url,\n this.config.apiKey || null\n );\n this.stateTransitionClient = new StateTransitionClient(this.aggregatorClient);\n\n if (trustBase) {\n this.trustBase = trustBase;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n // Load trust base using injected loader\n try {\n const trustBaseJson = await this.config.trustBaseLoader.load();\n if (trustBaseJson) {\n this.trustBase = RootTrustBase.fromJSON(trustBaseJson);\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:', !!this.trustBase);\n }\n\n /**\n * Submit a transfer commitment to the aggregator.\n * Accepts either an SDK TransferCommitment or a simple commitment object.\n */\n async submitCommitment(commitment: TransferCommitment | SdkTransferCommitment): Promise<SubmitResult> {\n this.ensureConnected();\n\n try {\n let requestId: string;\n\n // Check if it's an SDK commitment (has submitTransferCommitment method signature)\n if (this.isSdkTransferCommitment(commitment)) {\n // Use SDK client directly\n const response = await this.stateTransitionClient!.submitTransferCommitment(commitment);\n requestId = commitment.requestId?.toString() ?? response.status;\n } else {\n // Fallback to RPC for simple commitment objects\n const response = await this.rpcCall<RpcSubmitResponse>('submitCommitment', {\n sourceToken: commitment.sourceToken,\n recipient: commitment.recipient,\n salt: Array.from(commitment.salt),\n data: commitment.data,\n });\n requestId = response.requestId ?? '';\n }\n\n this.emitEvent({\n type: 'commitment:submitted',\n timestamp: Date.now(),\n data: { requestId },\n });\n\n return {\n success: true,\n requestId,\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMsg,\n timestamp: Date.now(),\n };\n }\n }\n\n /**\n * Submit a mint commitment to the aggregator (SDK only)\n * @param commitment - SDK MintCommitment instance\n */\n async submitMintCommitment(commitment: SdkMintCommitment): Promise<SubmitResult> {\n this.ensureConnected();\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const response = await this.stateTransitionClient!.submitMintCommitment(commitment as any);\n const requestId = commitment.requestId?.toString() ?? response.status;\n\n this.emitEvent({\n type: 'commitment:submitted',\n timestamp: Date.now(),\n data: { requestId },\n });\n\n return {\n success: true,\n requestId,\n timestamp: Date.now(),\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMsg,\n timestamp: Date.now(),\n };\n }\n }\n\n private isSdkTransferCommitment(commitment: unknown): commitment is SdkTransferCommitment {\n return (\n commitment !== null &&\n typeof commitment === 'object' &&\n 'requestId' in commitment &&\n typeof (commitment as SdkTransferCommitment).requestId?.toString === 'function'\n );\n }\n\n async getProof(requestId: string): Promise<InclusionProof | null> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcProofResponse>('getInclusionProof', { requestId });\n\n if (!response.proof) {\n return null;\n }\n\n return {\n requestId,\n roundNumber: response.roundNumber ?? 0,\n proof: response.proof,\n timestamp: Date.now(),\n };\n } catch (error) {\n logger.warn('Aggregator', 'getProof failed', error);\n return null;\n }\n }\n\n async waitForProof(requestId: string, options?: WaitOptions): Promise<InclusionProof> {\n const timeout = options?.timeout ?? this.config.timeout;\n const pollInterval = options?.pollInterval ?? TIMEOUTS.PROOF_POLL_INTERVAL;\n const startTime = Date.now();\n let attempt = 0;\n\n while (Date.now() - startTime < timeout) {\n options?.onPoll?.(++attempt);\n\n const proof = await this.getProof(requestId);\n if (proof) {\n this.emitEvent({\n type: 'proof:received',\n timestamp: Date.now(),\n data: { requestId, roundNumber: proof.roundNumber },\n });\n return proof;\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new SphereError(`Timeout waiting for proof: ${requestId}`, 'TIMEOUT');\n }\n\n async validateToken(tokenData: unknown): Promise<ValidationResult> {\n this.ensureConnected();\n\n try {\n // Try SDK validation first if we have trust base\n if (this.trustBase && !this.config.skipVerification) {\n try {\n const sdkToken = await SdkToken.fromJSON(tokenData);\n const verifyResult = await sdkToken.verify(this.trustBase);\n\n // Calculate state hash\n const stateHash = await sdkToken.state.calculateHash();\n const stateHashStr = stateHash.toJSON();\n\n const valid = verifyResult.isSuccessful;\n\n this.emitEvent({\n type: 'validation:completed',\n timestamp: Date.now(),\n data: { valid },\n });\n\n return {\n valid,\n spent: false, // Spend check is separate\n stateHash: stateHashStr,\n error: valid ? undefined : 'SDK verification failed',\n };\n } catch (sdkError) {\n this.log('SDK validation failed, falling back to RPC:', sdkError);\n }\n }\n\n // Fallback to RPC validation\n const response = await this.rpcCall<RpcValidateResponse>('validateToken', { token: tokenData });\n\n const valid = response.valid ?? false;\n const spent = response.spent ?? false;\n\n this.emitEvent({\n type: 'validation:completed',\n timestamp: Date.now(),\n data: { valid },\n });\n\n // Cache spent state if spent\n if (response.stateHash && spent) {\n this.spentCache.set(response.stateHash, true);\n }\n\n return {\n valid,\n spent,\n stateHash: response.stateHash,\n error: response.error,\n };\n } catch (error) {\n return {\n valid: false,\n spent: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Wait for inclusion proof using SDK (for SDK commitments)\n */\n async waitForProofSdk(\n commitment: SdkTransferCommitment | SdkMintCommitment,\n signal?: AbortSignal\n ): Promise<unknown> {\n this.ensureConnected();\n\n if (!this.trustBase) {\n throw new SphereError('Trust base not initialized', 'NOT_INITIALIZED');\n }\n\n return await waitInclusionProof(\n this.trustBase,\n this.stateTransitionClient!,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n commitment as any,\n signal\n );\n }\n\n async isSpent(stateHash: string): Promise<boolean> {\n // Check cache first (spent is immutable)\n if (this.spentCache.has(stateHash)) {\n return this.spentCache.get(stateHash)!;\n }\n\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcSpentResponse>('isSpent', { stateHash });\n const spent = response.spent ?? false;\n\n // Cache result\n if (spent) {\n this.spentCache.set(stateHash, true);\n }\n\n return spent;\n } catch (error) {\n logger.warn('Aggregator', 'isSpent check failed, assuming unspent', error);\n return false;\n }\n }\n\n async getTokenState(tokenId: string): Promise<TokenState | null> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcTokenStateResponse>('getTokenState', { tokenId });\n\n if (!response.state) {\n return null;\n }\n\n return {\n tokenId,\n stateHash: response.state.stateHash ?? '',\n spent: response.state.spent ?? false,\n roundNumber: response.state.roundNumber,\n lastUpdated: Date.now(),\n };\n } catch (error) {\n logger.warn('Aggregator', 'getTokenState failed', error);\n return null;\n }\n }\n\n async getCurrentRound(): Promise<number> {\n if (this.aggregatorClient) {\n const blockHeight = await this.aggregatorClient.getBlockHeight();\n return Number(blockHeight);\n }\n return 0;\n }\n\n async mint(params: MintParams): Promise<MintResult> {\n this.ensureConnected();\n\n try {\n const response = await this.rpcCall<RpcMintResponse>('mint', {\n coinId: params.coinId,\n amount: params.amount,\n recipientAddress: params.recipientAddress,\n recipientPubkey: params.recipientPubkey,\n });\n\n return {\n success: true,\n requestId: response.requestId,\n tokenId: response.tokenId,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\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: RPC\n // ===========================================================================\n\n private async rpcCall<T>(method: string, params: unknown): Promise<T> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(this.config.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method,\n params,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new SphereError(`HTTP ${response.status}: ${response.statusText}`, 'AGGREGATOR_ERROR');\n }\n\n const result = await response.json();\n\n if (result.error) {\n throw new SphereError(result.error.message ?? 'RPC error', 'AGGREGATOR_ERROR');\n }\n\n return (result.result ?? {}) as T;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('UnicityAggregatorProvider not connected', 'NOT_INITIALIZED');\n }\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// 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_TESTNET, TRUSTBASE_MAINNET, TRUSTBASE_DEV } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\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 case 'testnet':\n return TRUSTBASE_TESTNET;\n case 'dev':\n return TRUSTBASE_DEV;\n default:\n return TRUSTBASE_TESTNET;\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';\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 constructor(networkOrUrl: NetworkType | string = 'testnet') {\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n super('testnet');\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(networkOrUrl?: NetworkType | string): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl);\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 return new UnicityAggregatorProvider({\n ...restConfig,\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network ?? 'testnet'),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\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 * IPFS Error Classification\n * Categorizes errors for proper handling (e.g., NOT_FOUND should not trigger circuit breaker)\n */\n\n// =============================================================================\n// Error Categories\n// =============================================================================\n\nexport type IpfsErrorCategory =\n | 'NOT_FOUND' // IPNS record never published (expected for new wallets)\n | 'NETWORK_ERROR' // Connectivity / server issues\n | 'TIMEOUT' // Request timed out\n | 'GATEWAY_ERROR' // Gateway returned error (5xx, etc.)\n | 'INVALID_RESPONSE' // Response parsing failed\n | 'CID_MISMATCH' // Content hash doesn't match CID\n | 'SEQUENCE_DOWNGRADE'; // Remote sequence < local (stale data)\n\n// =============================================================================\n// Error Class\n// =============================================================================\n\nexport class IpfsError extends Error {\n readonly category: IpfsErrorCategory;\n readonly gateway?: string;\n readonly cause?: Error;\n\n constructor(\n message: string,\n category: IpfsErrorCategory,\n gateway?: string,\n cause?: Error,\n ) {\n super(message);\n this.name = 'IpfsError';\n this.category = category;\n this.gateway = gateway;\n this.cause = cause;\n }\n\n /** Whether this error should trigger the circuit breaker */\n get shouldTriggerCircuitBreaker(): boolean {\n // NOT_FOUND is expected for new wallets, don't penalize the gateway\n return this.category !== 'NOT_FOUND' && this.category !== 'SEQUENCE_DOWNGRADE';\n }\n}\n\n// =============================================================================\n// Error Classification Helpers\n// =============================================================================\n\n/**\n * Classify a fetch exception into an IpfsErrorCategory\n */\nexport function classifyFetchError(error: unknown): IpfsErrorCategory {\n if (error instanceof DOMException && error.name === 'AbortError') {\n return 'TIMEOUT';\n }\n if (error instanceof TypeError) {\n // TypeError typically means network failure (DNS, connection refused, etc.)\n return 'NETWORK_ERROR';\n }\n if (error instanceof Error && error.name === 'TimeoutError') {\n return 'TIMEOUT';\n }\n return 'NETWORK_ERROR';\n}\n\n/**\n * Classify an HTTP status code into an IpfsErrorCategory\n * @param status - HTTP status code\n * @param responseBody - Optional response body for additional context\n */\nexport function classifyHttpStatus(\n status: number,\n responseBody?: string,\n): IpfsErrorCategory {\n if (status === 404) {\n return 'NOT_FOUND';\n }\n\n if (status === 500 && responseBody) {\n // Kubo returns 500 with \"routing: not found\" for IPNS records that don't exist\n if (/routing:\\s*not\\s*found/i.test(responseBody)) {\n return 'NOT_FOUND';\n }\n }\n\n if (status >= 500) {\n return 'GATEWAY_ERROR';\n }\n\n if (status >= 400) {\n return 'GATEWAY_ERROR';\n }\n\n return 'GATEWAY_ERROR';\n}\n","/**\n * IPFS State Persistence\n * Interface and in-memory implementation for persisting IPFS/IPNS state\n */\n\nimport type { IpfsStatePersistence, IpfsPersistedState } from './ipfs-types';\n\n// Re-export for convenience\nexport type { IpfsStatePersistence, IpfsPersistedState };\n\n// =============================================================================\n// In-Memory Implementation (for testing)\n// =============================================================================\n\nexport class InMemoryIpfsStatePersistence implements IpfsStatePersistence {\n private readonly states = new Map<string, IpfsPersistedState>();\n\n async load(ipnsName: string): Promise<IpfsPersistedState | null> {\n return this.states.get(ipnsName) ?? null;\n }\n\n async save(ipnsName: string, state: IpfsPersistedState): Promise<void> {\n this.states.set(ipnsName, { ...state });\n }\n\n async clear(ipnsName: string): Promise<void> {\n this.states.delete(ipnsName);\n }\n}\n","/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport { abytes, aexists, ahash, clean, type CHash, type Hash } from './utils.ts';\n\n/** Internal class for HMAC. */\nexport class _HMAC<T extends Hash<T>> implements Hash<_HMAC<T>> {\n oHash: T;\n iHash: T;\n blockLen: number;\n outputLen: number;\n private finished = false;\n private destroyed = false;\n\n constructor(hash: CHash, key: Uint8Array) {\n ahash(hash);\n abytes(key, undefined, 'key');\n this.iHash = hash.create() as T;\n if (typeof this.iHash.update !== 'function')\n throw new Error('Expected instance of class which extends utils.Hash');\n this.blockLen = this.iHash.blockLen;\n this.outputLen = this.iHash.outputLen;\n const blockLen = this.blockLen;\n const pad = new Uint8Array(blockLen);\n // blockLen can be bigger than outputLen\n pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36;\n this.iHash.update(pad);\n // By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone\n this.oHash = hash.create() as T;\n // Undo internal XOR && apply outer XOR\n for (let i = 0; i < pad.length; i++) pad[i] ^= 0x36 ^ 0x5c;\n this.oHash.update(pad);\n clean(pad);\n }\n update(buf: Uint8Array): this {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n abytes(out, this.outputLen, 'output');\n this.finished = true;\n this.iHash.digestInto(out);\n this.oHash.update(out);\n this.oHash.digestInto(out);\n this.destroy();\n }\n digest(): Uint8Array {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out;\n }\n _cloneInto(to?: _HMAC<T>): _HMAC<T> {\n // Create new instance without calling constructor since key already in state and we don't know it.\n to ||= Object.create(Object.getPrototypeOf(this), {});\n const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;\n to = to as this;\n to.finished = finished;\n to.destroyed = destroyed;\n to.blockLen = blockLen;\n to.outputLen = outputLen;\n to.oHash = oHash._cloneInto(to.oHash);\n to.iHash = iHash._cloneInto(to.iHash);\n return to;\n }\n clone(): _HMAC<T> {\n return this._cloneInto();\n }\n destroy(): void {\n this.destroyed = true;\n this.oHash.destroy();\n this.iHash.destroy();\n }\n}\n\n/**\n * HMAC: RFC2104 message authentication code.\n * @param hash - function that would be used e.g. sha256\n * @param key - message key\n * @param message - message data\n * @example\n * import { hmac } from '@noble/hashes/hmac';\n * import { sha256 } from '@noble/hashes/sha2';\n * const mac1 = hmac(sha256, 'key', 'message');\n */\nexport const hmac: {\n (hash: CHash, key: Uint8Array, message: Uint8Array): Uint8Array;\n create(hash: CHash, key: Uint8Array): _HMAC<any>;\n} = (hash: CHash, key: Uint8Array, message: Uint8Array): Uint8Array =>\n new _HMAC<any>(hash, key).update(message).digest();\nhmac.create = (hash: CHash, key: Uint8Array) => new _HMAC<any>(hash, key);\n","/**\n * HKDF (RFC 5869): extract + expand in one step.\n * See https://soatok.blog/2021/11/17/understanding-hkdf/.\n * @module\n */\nimport { hmac } from './hmac.ts';\nimport { abytes, ahash, anumber, type CHash, clean } from './utils.ts';\n\n/**\n * HKDF-extract from spec. Less important part. `HKDF-Extract(IKM, salt) -> PRK`\n * Arguments position differs from spec (IKM is first one, since it is not optional)\n * @param hash - hash function that would be used (e.g. sha256)\n * @param ikm - input keying material, the initial key\n * @param salt - optional salt value (a non-secret random value)\n */\nexport function extract(hash: CHash, ikm: Uint8Array, salt?: Uint8Array): Uint8Array {\n ahash(hash);\n // NOTE: some libraries treat zero-length array as 'not provided';\n // we don't, since we have undefined as 'not provided'\n // https://github.com/RustCrypto/KDFs/issues/15\n if (salt === undefined) salt = new Uint8Array(hash.outputLen);\n return hmac(hash, salt, ikm);\n}\n\nconst HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);\nconst EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();\n\n/**\n * HKDF-expand from the spec. The most important part. `HKDF-Expand(PRK, info, L) -> OKM`\n * @param hash - hash function that would be used (e.g. sha256)\n * @param prk - a pseudorandom key of at least HashLen octets (usually, the output from the extract step)\n * @param info - optional context and application specific information (can be a zero-length string)\n * @param length - length of output keying material in bytes\n */\nexport function expand(\n hash: CHash,\n prk: Uint8Array,\n info?: Uint8Array,\n length: number = 32\n): Uint8Array {\n ahash(hash);\n anumber(length, 'length');\n const olen = hash.outputLen;\n if (length > 255 * olen) throw new Error('Length must be <= 255*HashLen');\n const blocks = Math.ceil(length / olen);\n if (info === undefined) info = EMPTY_BUFFER;\n else abytes(info, undefined, 'info');\n // first L(ength) octets of T\n const okm = new Uint8Array(blocks * olen);\n // Re-use HMAC instance between blocks\n const HMAC = hmac.create(hash, prk);\n const HMACTmp = HMAC._cloneInto();\n const T = new Uint8Array(HMAC.outputLen);\n for (let counter = 0; counter < blocks; counter++) {\n HKDF_COUNTER[0] = counter + 1;\n // T(0) = empty string (zero length)\n // T(N) = HMAC-Hash(PRK, T(N-1) | info | N)\n HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T)\n .update(info)\n .update(HKDF_COUNTER)\n .digestInto(T);\n okm.set(T, olen * counter);\n HMAC._cloneInto(HMACTmp);\n }\n HMAC.destroy();\n HMACTmp.destroy();\n clean(T, HKDF_COUNTER);\n return okm.slice(0, length);\n}\n\n/**\n * HKDF (RFC 5869): derive keys from an initial input.\n * Combines hkdf_extract + hkdf_expand in one step\n * @param hash - hash function that would be used (e.g. sha256)\n * @param ikm - input keying material, the initial key\n * @param salt - optional salt value (a non-secret random value)\n * @param info - optional context and application specific information (can be a zero-length string)\n * @param length - length of output keying material in bytes\n * @example\n * import { hkdf } from '@noble/hashes/hkdf';\n * import { sha256 } from '@noble/hashes/sha2';\n * import { randomBytes } from '@noble/hashes/utils';\n * const inputKey = randomBytes(32);\n * const salt = randomBytes(32);\n * const info = 'application-key';\n * const hk1 = hkdf(sha256, inputKey, salt, info, 32);\n */\nexport const hkdf = (\n hash: CHash,\n ikm: Uint8Array,\n salt: Uint8Array | undefined,\n info: Uint8Array | undefined,\n length: number\n): Uint8Array => expand(hash, extract(hash, ikm, salt), info, length);\n","/**\n * Cryptographic utilities for SDK2\n *\n * Provides BIP39 mnemonic and BIP32 key derivation functions.\n * Platform-independent - no browser-specific APIs.\n */\n\nimport * as bip39 from 'bip39';\nimport CryptoJS from 'crypto-js';\nimport elliptic from 'elliptic';\nimport { encodeBech32 } from './bech32';\nimport { SphereError } from './errors';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst ec = new elliptic.ec('secp256k1');\n\n/** secp256k1 curve order */\nconst CURVE_ORDER = BigInt(\n '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'\n);\n\n/** Default derivation path for Unicity (BIP44) */\nexport const DEFAULT_DERIVATION_PATH = \"m/44'/0'/0'\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface MasterKey {\n privateKey: string;\n chainCode: string;\n}\n\nexport interface DerivedKey {\n privateKey: string;\n chainCode: string;\n}\n\nexport interface KeyPair {\n privateKey: string;\n publicKey: string;\n}\n\nexport interface AddressInfo extends KeyPair {\n address: string;\n path: string;\n index: number;\n}\n\n// =============================================================================\n// BIP39 Mnemonic Functions\n// =============================================================================\n\n/**\n * Generate a new BIP39 mnemonic phrase\n * @param strength - Entropy bits (128 = 12 words, 256 = 24 words)\n */\nexport function generateMnemonic(strength: 128 | 256 = 128): string {\n return bip39.generateMnemonic(strength);\n}\n\n/**\n * Validate a BIP39 mnemonic phrase\n */\nexport function validateMnemonic(mnemonic: string): boolean {\n return bip39.validateMnemonic(mnemonic);\n}\n\n/**\n * Convert mnemonic to seed (64-byte hex string)\n * @param mnemonic - BIP39 mnemonic phrase\n * @param passphrase - Optional passphrase for additional security\n */\nexport async function mnemonicToSeed(\n mnemonic: string,\n passphrase: string = ''\n): Promise<string> {\n const seedBuffer = await bip39.mnemonicToSeed(mnemonic, passphrase);\n return Buffer.from(seedBuffer).toString('hex');\n}\n\n/**\n * Synchronous version of mnemonicToSeed\n */\nexport function mnemonicToSeedSync(\n mnemonic: string,\n passphrase: string = ''\n): string {\n const seedBuffer = bip39.mnemonicToSeedSync(mnemonic, passphrase);\n return Buffer.from(seedBuffer).toString('hex');\n}\n\n/**\n * Convert mnemonic to entropy (for recovery purposes)\n */\nexport function mnemonicToEntropy(mnemonic: string): string {\n return bip39.mnemonicToEntropy(mnemonic);\n}\n\n/**\n * Convert entropy to mnemonic\n */\nexport function entropyToMnemonic(entropy: string): string {\n return bip39.entropyToMnemonic(entropy);\n}\n\n// =============================================================================\n// BIP32 Key Derivation\n// =============================================================================\n\n/**\n * Generate master key from seed (BIP32 standard)\n * Uses HMAC-SHA512 with key \"Bitcoin seed\"\n */\nexport function generateMasterKey(seedHex: string): MasterKey {\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(seedHex),\n CryptoJS.enc.Utf8.parse('Bitcoin seed')\n ).toString();\n\n const IL = I.substring(0, 64); // Left 32 bytes - master private key\n const IR = I.substring(64); // Right 32 bytes - master chain code\n\n // Validate master key\n const masterKeyBigInt = BigInt('0x' + IL);\n if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {\n throw new SphereError('Invalid master key generated', 'VALIDATION_ERROR');\n }\n\n return {\n privateKey: IL,\n chainCode: IR,\n };\n}\n\n/**\n * Derive child key using BIP32 standard\n * @param parentPrivKey - Parent private key (64 hex chars)\n * @param parentChainCode - Parent chain code (64 hex chars)\n * @param index - Child index (>= 0x80000000 for hardened)\n */\nexport function deriveChildKey(\n parentPrivKey: string,\n parentChainCode: string,\n index: number\n): DerivedKey {\n const isHardened = index >= 0x80000000;\n let data: string;\n\n if (isHardened) {\n // Hardened derivation: 0x00 || parentPrivKey || index\n const indexHex = index.toString(16).padStart(8, '0');\n data = '00' + parentPrivKey + indexHex;\n } else {\n // Non-hardened derivation: compressedPubKey || index\n const keyPair = ec.keyFromPrivate(parentPrivKey, 'hex');\n const compressedPubKey = keyPair.getPublic(true, 'hex');\n const indexHex = index.toString(16).padStart(8, '0');\n data = compressedPubKey + indexHex;\n }\n\n // HMAC-SHA512 with chain code as key\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(data),\n CryptoJS.enc.Hex.parse(parentChainCode)\n ).toString();\n\n const IL = I.substring(0, 64); // Left 32 bytes\n const IR = I.substring(64); // Right 32 bytes (new chain code)\n\n // Add IL to parent key mod n (curve order)\n const ilBigInt = BigInt('0x' + IL);\n const parentKeyBigInt = BigInt('0x' + parentPrivKey);\n\n // Check IL is valid (less than curve order)\n if (ilBigInt >= CURVE_ORDER) {\n throw new SphereError('Invalid key: IL >= curve order', 'VALIDATION_ERROR');\n }\n\n const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;\n\n // Check child key is valid (not zero)\n if (childKeyBigInt === 0n) {\n throw new SphereError('Invalid key: child key is zero', 'VALIDATION_ERROR');\n }\n\n const childPrivKey = childKeyBigInt.toString(16).padStart(64, '0');\n\n return {\n privateKey: childPrivKey,\n chainCode: IR,\n };\n}\n\n/**\n * Derive key at a full BIP32/BIP44 path\n * @param masterPrivKey - Master private key\n * @param masterChainCode - Master chain code\n * @param path - BIP44 path like \"m/44'/0'/0'/0/0\"\n */\nexport function deriveKeyAtPath(\n masterPrivKey: string,\n masterChainCode: string,\n path: string\n): DerivedKey {\n const pathParts = path.replace('m/', '').split('/');\n\n let currentKey = masterPrivKey;\n let currentChainCode = masterChainCode;\n\n for (const part of pathParts) {\n const isHardened = part.endsWith(\"'\") || part.endsWith('h');\n const indexStr = part.replace(/['h]$/, '');\n let index = parseInt(indexStr, 10);\n\n if (isHardened) {\n index += 0x80000000; // Add hardened offset\n }\n\n const derived = deriveChildKey(currentKey, currentChainCode, index);\n currentKey = derived.privateKey;\n currentChainCode = derived.chainCode;\n }\n\n return {\n privateKey: currentKey,\n chainCode: currentChainCode,\n };\n}\n\n// =============================================================================\n// Key Pair Operations\n// =============================================================================\n\n/**\n * Get public key from private key\n * @param privateKey - Private key as hex string\n * @param compressed - Return compressed public key (default: true)\n */\nexport function getPublicKey(privateKey: string, compressed: boolean = true): string {\n const keyPair = ec.keyFromPrivate(privateKey, 'hex');\n return keyPair.getPublic(compressed, 'hex');\n}\n\n/**\n * Create key pair from private key\n */\nexport function createKeyPair(privateKey: string): KeyPair {\n return {\n privateKey,\n publicKey: getPublicKey(privateKey),\n };\n}\n\n// =============================================================================\n// Hash Functions\n// =============================================================================\n\n/**\n * Compute SHA256 hash\n */\nexport function sha256(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const parsed =\n inputEncoding === 'hex'\n ? CryptoJS.enc.Hex.parse(data)\n : CryptoJS.enc.Utf8.parse(data);\n return CryptoJS.SHA256(parsed).toString();\n}\n\n/**\n * Compute RIPEMD160 hash\n */\nexport function ripemd160(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const parsed =\n inputEncoding === 'hex'\n ? CryptoJS.enc.Hex.parse(data)\n : CryptoJS.enc.Utf8.parse(data);\n return CryptoJS.RIPEMD160(parsed).toString();\n}\n\n/**\n * Compute HASH160 (SHA256 -> RIPEMD160)\n */\nexport function hash160(data: string): string {\n const sha = sha256(data, 'hex');\n return ripemd160(sha, 'hex');\n}\n\n/**\n * Compute double SHA256\n */\nexport function doubleSha256(data: string, inputEncoding: 'hex' | 'utf8' = 'hex'): string {\n const first = sha256(data, inputEncoding);\n return sha256(first, 'hex');\n}\n\n/**\n * Alias for hash160 (L1 SDK compatibility)\n */\nexport const computeHash160 = hash160;\n\n/**\n * Convert hex string to Uint8Array for witness program\n */\nexport function hash160ToBytes(hash160Hex: string): Uint8Array {\n const matches = hash160Hex.match(/../g);\n if (!matches) return new Uint8Array(0);\n return Uint8Array.from(matches.map((x) => parseInt(x, 16)));\n}\n\n/**\n * Generate bech32 address from public key\n * @param publicKey - Compressed public key as hex string\n * @param prefix - Address prefix (default: \"alpha\")\n * @param witnessVersion - Witness version (default: 0 for P2WPKH)\n * @returns Bech32 encoded address\n */\nexport function publicKeyToAddress(\n publicKey: string,\n prefix: string = 'alpha',\n witnessVersion: number = 0\n): string {\n const pubKeyHash = hash160(publicKey);\n const programBytes = hash160ToBytes(pubKeyHash);\n return encodeBech32(prefix, witnessVersion, programBytes);\n}\n\n/**\n * Get address info from private key\n */\nexport function privateKeyToAddressInfo(\n privateKey: string,\n prefix: string = 'alpha'\n): { address: string; publicKey: string } {\n const publicKey = getPublicKey(privateKey);\n const address = publicKeyToAddress(publicKey, prefix);\n return { address, publicKey };\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Convert hex string to Uint8Array\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const matches = hex.match(/../g);\n if (!matches) {\n return new Uint8Array(0);\n }\n return Uint8Array.from(matches.map((x) => parseInt(x, 16)));\n}\n\n/**\n * Convert Uint8Array to hex string\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate random bytes as hex string\n */\nexport function randomBytes(length: number): string {\n const words = CryptoJS.lib.WordArray.random(length);\n return words.toString(CryptoJS.enc.Hex);\n}\n\n// =============================================================================\n// High-Level Functions\n// =============================================================================\n\n/**\n * Generate identity from mnemonic\n * Returns master key derived from mnemonic seed\n */\nexport async function identityFromMnemonic(\n mnemonic: string,\n passphrase: string = ''\n): Promise<MasterKey> {\n if (!validateMnemonic(mnemonic)) {\n throw new SphereError('Invalid mnemonic phrase', 'INVALID_IDENTITY');\n }\n const seedHex = await mnemonicToSeed(mnemonic, passphrase);\n return generateMasterKey(seedHex);\n}\n\n/**\n * Synchronous version of identityFromMnemonic\n */\nexport function identityFromMnemonicSync(\n mnemonic: string,\n passphrase: string = ''\n): MasterKey {\n if (!validateMnemonic(mnemonic)) {\n throw new SphereError('Invalid mnemonic phrase', 'INVALID_IDENTITY');\n }\n const seedHex = mnemonicToSeedSync(mnemonic, passphrase);\n return generateMasterKey(seedHex);\n}\n\n/**\n * Derive address info at a specific path\n * @param masterKey - Master key with privateKey and chainCode\n * @param basePath - Base derivation path (e.g., \"m/44'/0'/0'\")\n * @param index - Address index\n * @param isChange - Whether this is a change address (chain 1 vs 0)\n * @param prefix - Address prefix (default: \"alpha\")\n */\nexport function deriveAddressInfo(\n masterKey: MasterKey,\n basePath: string,\n index: number,\n isChange: boolean = false,\n prefix: string = 'alpha'\n): AddressInfo {\n const chain = isChange ? 1 : 0;\n const fullPath = `${basePath}/${chain}/${index}`;\n\n const derived = deriveKeyAtPath(masterKey.privateKey, masterKey.chainCode, fullPath);\n const publicKey = getPublicKey(derived.privateKey);\n const address = publicKeyToAddress(publicKey, prefix);\n\n return {\n privateKey: derived.privateKey,\n publicKey,\n address,\n path: fullPath,\n index,\n };\n}\n\n/**\n * Generate full address info from private key with index and path\n * (L1 SDK compatibility)\n */\nexport function generateAddressInfo(\n privateKey: string,\n index: number,\n path: string,\n prefix: string = 'alpha'\n): AddressInfo {\n const { address, publicKey } = privateKeyToAddressInfo(privateKey, prefix);\n return {\n privateKey,\n publicKey,\n address,\n path,\n index,\n };\n}\n\n// =============================================================================\n// Message Signing (secp256k1 ECDSA with recoverable signature)\n// =============================================================================\n\n/** Prefix prepended to all signed messages (Bitcoin-like signed message format) */\nexport const SIGN_MESSAGE_PREFIX = 'Sphere Signed Message:\\n';\n\n/** Encode an integer as a Bitcoin-style compact varint */\nfunction varint(n: number): Uint8Array {\n if (n < 253) return new Uint8Array([n]);\n const buf = new Uint8Array(3);\n buf[0] = 253;\n buf[1] = n & 0xff;\n buf[2] = (n >> 8) & 0xff;\n return buf;\n}\n\n/**\n * Hash a message for signing using the Bitcoin-like double-SHA256 scheme:\n * SHA256(SHA256(varint(prefix.length) + prefix + varint(msg.length) + msg))\n *\n * @returns 64-char lowercase hex hash\n */\nexport function hashSignMessage(message: string): string {\n const prefix = new TextEncoder().encode(SIGN_MESSAGE_PREFIX);\n const msg = new TextEncoder().encode(message);\n const prefixLen = varint(prefix.length);\n const msgLen = varint(msg.length);\n const full = new Uint8Array(prefixLen.length + prefix.length + msgLen.length + msg.length);\n let off = 0;\n full.set(prefixLen, off); off += prefixLen.length;\n full.set(prefix, off); off += prefix.length;\n full.set(msgLen, off); off += msgLen.length;\n full.set(msg, off);\n const hex = Array.from(full).map(b => b.toString(16).padStart(2, '0')).join('');\n const h1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(hex)).toString();\n return CryptoJS.SHA256(CryptoJS.enc.Hex.parse(h1)).toString();\n}\n\n/**\n * Sign a message with a secp256k1 private key.\n *\n * Returns a 130-character hex string: v (2 chars) + r (64 chars) + s (64 chars).\n * The recovery byte `v` is `31 + recoveryParam` (0-3).\n *\n * @param privateKeyHex - 64-char hex private key\n * @param message - plaintext message to sign\n */\nexport function signMessage(privateKeyHex: string, message: string): string {\n const keyPair = ec.keyFromPrivate(privateKeyHex, 'hex');\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n const sig = keyPair.sign(hashBytes, { canonical: true });\n\n // Find recovery parameter\n const pub = keyPair.getPublic();\n let recoveryParam = -1;\n for (let i = 0; i < 4; i++) {\n try {\n if (ec.recoverPubKey(hashBytes, sig, i).eq(pub)) {\n recoveryParam = i;\n break;\n }\n } catch { /* try next */ }\n }\n if (recoveryParam === -1) {\n throw new SphereError('Could not find recovery parameter', 'SIGNING_ERROR');\n }\n\n const v = (31 + recoveryParam).toString(16).padStart(2, '0');\n const r = sig.r.toString('hex').padStart(64, '0');\n const s = sig.s.toString('hex').padStart(64, '0');\n return v + r + s;\n}\n\n/**\n * Verify a signed message against a compressed secp256k1 public key.\n *\n * @param message - The original plaintext message\n * @param signature - 130-char hex signature (v + r + s)\n * @param expectedPubkey - 66-char compressed public key hex\n * @returns `true` if the signature is valid and matches the expected public key\n */\nexport function verifySignedMessage(\n message: string,\n signature: string,\n expectedPubkey: string,\n): boolean {\n if (signature.length !== 130) return false;\n\n const v = parseInt(signature.slice(0, 2), 16) - 31;\n const r = signature.slice(2, 66);\n const s = signature.slice(66, 130);\n\n if (v < 0 || v > 3) return false;\n\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n\n try {\n const recovered = ec.recoverPubKey(hashBytes, { r, s }, v);\n const recoveredHex = recovered.encode('hex', true); // compressed\n return recoveredHex === expectedPubkey;\n } catch {\n return false;\n }\n}\n\n// Re-export elliptic instance for advanced use cases\nexport { ec };\n","/**\n * IPNS Key Derivation\n * Deterministic IPNS identity from secp256k1 private key using HKDF\n *\n * Derivation path:\n * secp256k1 privateKey (hex)\n * -> HKDF(sha256, key, info=\"ipfs-storage-ed25519-v1\", 32 bytes)\n * -> Ed25519 key pair\n * -> libp2p PeerId\n * -> IPNS name (e.g., \"12D3KooW...\")\n */\n\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { hexToBytes } from '../../../core/crypto';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * HKDF info string for deriving Ed25519 keys from wallet keys.\n * Must match sphere webapp for cross-device compatibility.\n */\nexport const IPNS_HKDF_INFO = 'ipfs-storage-ed25519-v1';\n\n// =============================================================================\n// Dynamic Import Cache\n// =============================================================================\n\nlet libp2pCryptoModule: typeof import('@libp2p/crypto/keys') | null = null;\nlet libp2pPeerIdModule: typeof import('@libp2p/peer-id') | null = null;\n\nasync function loadLibp2pModules() {\n if (!libp2pCryptoModule) {\n [libp2pCryptoModule, libp2pPeerIdModule] = await Promise.all([\n import('@libp2p/crypto/keys'),\n import('@libp2p/peer-id'),\n ]);\n }\n return {\n generateKeyPairFromSeed: libp2pCryptoModule!.generateKeyPairFromSeed,\n peerIdFromPrivateKey: libp2pPeerIdModule!.peerIdFromPrivateKey,\n };\n}\n\n// =============================================================================\n// Key Derivation\n// =============================================================================\n\n/**\n * Derive Ed25519 key material from a secp256k1 private key using HKDF.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @param info - HKDF info string (default: IPNS_HKDF_INFO)\n * @returns 32-byte derived key material suitable for Ed25519 seed\n */\nexport function deriveEd25519KeyMaterial(\n privateKeyHex: string,\n info: string = IPNS_HKDF_INFO,\n): Uint8Array {\n const walletSecret = hexToBytes(privateKeyHex);\n const infoBytes = new TextEncoder().encode(info);\n return hkdf(sha256, walletSecret, undefined, infoBytes, 32);\n}\n\n/**\n * Derive full IPNS identity (key pair + IPNS name) from a secp256k1 private key.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @returns Object with keyPair and ipnsName\n */\nexport async function deriveIpnsIdentity(\n privateKeyHex: string,\n): Promise<{ keyPair: unknown; ipnsName: string }> {\n const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadLibp2pModules();\n\n const derivedKey = deriveEd25519KeyMaterial(privateKeyHex);\n const keyPair = await generateKeyPairFromSeed('Ed25519', derivedKey);\n const peerId = peerIdFromPrivateKey(keyPair);\n\n return {\n keyPair,\n ipnsName: peerId.toString(),\n };\n}\n\n/**\n * Derive just the IPNS name from a secp256k1 private key.\n * Lighter than deriveIpnsIdentity when you don't need the key pair.\n *\n * @param privateKeyHex - secp256k1 private key in hex format\n * @returns IPNS name string (e.g., \"12D3KooW...\")\n */\nexport async function deriveIpnsName(\n privateKeyHex: string,\n): Promise<string> {\n const { ipnsName } = await deriveIpnsIdentity(privateKeyHex);\n return ipnsName;\n}\n","/**\n * IPNS Record Manager\n * Creates, marshals, and parses IPNS records for publishing and resolution\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default IPNS record lifetime: 99 years (effectively permanent) */\nconst DEFAULT_LIFETIME_MS = 99 * 365 * 24 * 60 * 60 * 1000;\n\n// =============================================================================\n// Dynamic Import Cache\n// =============================================================================\n\nlet ipnsModule: {\n createIPNSRecord: typeof import('ipns')['createIPNSRecord'];\n marshalIPNSRecord: typeof import('ipns')['marshalIPNSRecord'];\n unmarshalIPNSRecord: typeof import('ipns')['unmarshalIPNSRecord'];\n} | null = null;\n\nasync function loadIpnsModule() {\n if (!ipnsModule) {\n const mod = await import('ipns');\n ipnsModule = {\n createIPNSRecord: mod.createIPNSRecord,\n marshalIPNSRecord: mod.marshalIPNSRecord,\n unmarshalIPNSRecord: mod.unmarshalIPNSRecord,\n };\n }\n return ipnsModule;\n}\n\n// =============================================================================\n// Record Creation\n// =============================================================================\n\n/**\n * Create a signed IPNS record and marshal it to bytes.\n *\n * @param keyPair - Ed25519 private key (from deriveIpnsIdentity)\n * @param cid - CID to point the IPNS record at\n * @param sequenceNumber - Monotonically increasing sequence number\n * @param lifetimeMs - Record validity period (default: 99 years)\n * @returns Marshalled IPNS record bytes\n */\nexport async function createSignedRecord(\n keyPair: unknown,\n cid: string,\n sequenceNumber: bigint,\n lifetimeMs: number = DEFAULT_LIFETIME_MS,\n): Promise<Uint8Array> {\n const { createIPNSRecord, marshalIPNSRecord } = await loadIpnsModule();\n\n const record = await createIPNSRecord(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n keyPair as any,\n `/ipfs/${cid}`,\n sequenceNumber,\n lifetimeMs,\n );\n\n return marshalIPNSRecord(record);\n}\n\n// =============================================================================\n// Record Parsing\n// =============================================================================\n\n/**\n * Parse a routing API response (NDJSON) to extract CID and sequence number.\n * The routing API returns newline-delimited JSON with an \"Extra\" field\n * containing a base64-encoded marshalled IPNS record.\n *\n * @param responseText - Raw text from the routing API response\n * @returns Parsed result with cid, sequence, and recordData, or null\n */\nexport async function parseRoutingApiResponse(\n responseText: string,\n): Promise<{ cid: string; sequence: bigint; recordData: Uint8Array } | null> {\n const { unmarshalIPNSRecord } = await loadIpnsModule();\n\n const lines = responseText.trim().split('\\n');\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n try {\n const obj = JSON.parse(line);\n\n if (obj.Extra) {\n const recordData = base64ToUint8Array(obj.Extra);\n const record = unmarshalIPNSRecord(recordData);\n\n // Extract CID from the value field\n const valueBytes = typeof record.value === 'string'\n ? new TextEncoder().encode(record.value)\n : record.value as Uint8Array;\n const valueStr = new TextDecoder().decode(valueBytes);\n const cidMatch = valueStr.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n\n if (cidMatch) {\n return {\n cid: cidMatch[1],\n sequence: record.sequence,\n recordData,\n };\n }\n }\n } catch {\n // Skip malformed lines\n continue;\n }\n }\n\n return null;\n}\n\n/**\n * Verify that a new sequence number represents a valid progression.\n *\n * @param newSeq - Proposed new sequence number\n * @param lastKnownSeq - Last known sequence number\n * @returns true if the new sequence is valid (greater than last known)\n */\nexport function verifySequenceProgression(\n newSeq: bigint,\n lastKnownSeq: bigint,\n): boolean {\n return newSeq > lastKnownSeq;\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * IPFS Multi-Tier Cache\n * Caches IPNS records, content by CID, and tracks gateway failures\n */\n\nimport type { IpnsGatewayResult } from './ipfs-types';\nimport type { TxfStorageDataBase } from '../../../storage';\n\n// =============================================================================\n// Cache Entry Type\n// =============================================================================\n\ninterface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\n// =============================================================================\n// Default Config\n// =============================================================================\n\nconst DEFAULT_IPNS_TTL_MS = 60_000; // 60s for IPNS records\nconst DEFAULT_FAILURE_COOLDOWN_MS = 60_000; // 60s circuit breaker cooldown\nconst DEFAULT_FAILURE_THRESHOLD = 3; // 3 consecutive failures\nconst DEFAULT_KNOWN_FRESH_WINDOW_MS = 30_000; // 30s after publish\n\n// =============================================================================\n// Cache Implementation\n// =============================================================================\n\nexport interface IpfsCacheConfig {\n ipnsTtlMs?: number;\n failureCooldownMs?: number;\n failureThreshold?: number;\n knownFreshWindowMs?: number;\n}\n\nexport class IpfsCache {\n private readonly ipnsRecords = new Map<string, CacheEntry<IpnsGatewayResult>>();\n private readonly content = new Map<string, CacheEntry<TxfStorageDataBase>>();\n private readonly gatewayFailures = new Map<string, { count: number; lastFailure: number }>();\n private readonly knownFreshTimestamps = new Map<string, number>();\n\n private readonly ipnsTtlMs: number;\n private readonly failureCooldownMs: number;\n private readonly failureThreshold: number;\n private readonly knownFreshWindowMs: number;\n\n constructor(config?: IpfsCacheConfig) {\n this.ipnsTtlMs = config?.ipnsTtlMs ?? DEFAULT_IPNS_TTL_MS;\n this.failureCooldownMs = config?.failureCooldownMs ?? DEFAULT_FAILURE_COOLDOWN_MS;\n this.failureThreshold = config?.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD;\n this.knownFreshWindowMs = config?.knownFreshWindowMs ?? DEFAULT_KNOWN_FRESH_WINDOW_MS;\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Record Cache (60s TTL)\n // ---------------------------------------------------------------------------\n\n getIpnsRecord(ipnsName: string): IpnsGatewayResult | null {\n const entry = this.ipnsRecords.get(ipnsName);\n if (!entry) return null;\n\n if (Date.now() - entry.timestamp > this.ipnsTtlMs) {\n this.ipnsRecords.delete(ipnsName);\n return null;\n }\n\n return entry.data;\n }\n\n /**\n * Get cached IPNS record ignoring TTL (for known-fresh optimization).\n */\n getIpnsRecordIgnoreTtl(ipnsName: string): IpnsGatewayResult | null {\n const entry = this.ipnsRecords.get(ipnsName);\n return entry?.data ?? null;\n }\n\n setIpnsRecord(ipnsName: string, result: IpnsGatewayResult): void {\n this.ipnsRecords.set(ipnsName, {\n data: result,\n timestamp: Date.now(),\n });\n }\n\n invalidateIpns(ipnsName: string): void {\n this.ipnsRecords.delete(ipnsName);\n }\n\n // ---------------------------------------------------------------------------\n // Content Cache (infinite TTL - content is immutable by CID)\n // ---------------------------------------------------------------------------\n\n getContent(cid: string): TxfStorageDataBase | null {\n const entry = this.content.get(cid);\n return entry?.data ?? null;\n }\n\n setContent(cid: string, data: TxfStorageDataBase): void {\n this.content.set(cid, {\n data,\n timestamp: Date.now(),\n });\n }\n\n // ---------------------------------------------------------------------------\n // Gateway Failure Tracking (Circuit Breaker)\n // ---------------------------------------------------------------------------\n\n /**\n * Record a gateway failure. After threshold consecutive failures,\n * the gateway enters cooldown and is considered unhealthy.\n */\n recordGatewayFailure(gateway: string): void {\n const existing = this.gatewayFailures.get(gateway);\n this.gatewayFailures.set(gateway, {\n count: (existing?.count ?? 0) + 1,\n lastFailure: Date.now(),\n });\n }\n\n /** Reset failure count for a gateway (on successful request) */\n recordGatewaySuccess(gateway: string): void {\n this.gatewayFailures.delete(gateway);\n }\n\n /**\n * Check if a gateway is currently in circuit breaker cooldown.\n * A gateway is considered unhealthy if it has had >= threshold\n * consecutive failures and the cooldown period hasn't elapsed.\n */\n isGatewayInCooldown(gateway: string): boolean {\n const failure = this.gatewayFailures.get(gateway);\n if (!failure) return false;\n\n if (failure.count < this.failureThreshold) return false;\n\n const elapsed = Date.now() - failure.lastFailure;\n if (elapsed >= this.failureCooldownMs) {\n // Cooldown expired, reset\n this.gatewayFailures.delete(gateway);\n return false;\n }\n\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Known-Fresh Flag (FAST mode optimization)\n // ---------------------------------------------------------------------------\n\n /**\n * Mark IPNS cache as \"known-fresh\" (after local publish or push notification).\n * Within the fresh window, we can skip network resolution.\n */\n markIpnsFresh(ipnsName: string): void {\n this.knownFreshTimestamps.set(ipnsName, Date.now());\n }\n\n /**\n * Check if the cache is known-fresh (within the fresh window).\n */\n isIpnsKnownFresh(ipnsName: string): boolean {\n const timestamp = this.knownFreshTimestamps.get(ipnsName);\n if (!timestamp) return false;\n\n if (Date.now() - timestamp > this.knownFreshWindowMs) {\n this.knownFreshTimestamps.delete(ipnsName);\n return false;\n }\n\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Cache Management\n // ---------------------------------------------------------------------------\n\n clear(): void {\n this.ipnsRecords.clear();\n this.content.clear();\n this.gatewayFailures.clear();\n this.knownFreshTimestamps.clear();\n }\n}\n","/**\n * IPFS HTTP Client\n * Fetch-based HTTP API client for IPFS gateway operations.\n * Works in both browser and Node.js (native fetch available since Node 18+).\n */\n\nimport { logger } from '../../../core/logger';\nimport type {\n IpnsGatewayResult,\n IpnsProgressiveResult,\n IpnsPublishResult,\n GatewayHealthResult,\n} from './ipfs-types';\nimport type { TxfStorageDataBase } from '../../../storage';\nimport {\n IpfsError,\n classifyFetchError,\n classifyHttpStatus,\n} from './ipfs-error-types';\nimport { IpfsCache } from './ipfs-cache';\nimport { parseRoutingApiResponse } from './ipns-record-manager';\n\n// =============================================================================\n// Default Timeouts\n// =============================================================================\n\nconst DEFAULT_CONNECTIVITY_TIMEOUT_MS = 5000;\nconst DEFAULT_FETCH_TIMEOUT_MS = 15000;\nconst DEFAULT_RESOLVE_TIMEOUT_MS = 10000;\nconst DEFAULT_PUBLISH_TIMEOUT_MS = 30000;\nconst DEFAULT_GATEWAY_PATH_TIMEOUT_MS = 3000;\nconst DEFAULT_ROUTING_API_TIMEOUT_MS = 2000;\n\n// =============================================================================\n// HTTP Client\n// =============================================================================\n\nexport interface IpfsHttpClientConfig {\n gateways: string[];\n fetchTimeoutMs?: number;\n resolveTimeoutMs?: number;\n publishTimeoutMs?: number;\n connectivityTimeoutMs?: number;\n debug?: boolean;\n}\n\nexport class IpfsHttpClient {\n private readonly gateways: string[];\n private readonly fetchTimeoutMs: number;\n private readonly resolveTimeoutMs: number;\n private readonly publishTimeoutMs: number;\n private readonly connectivityTimeoutMs: number;\n private readonly debug: boolean;\n private readonly cache: IpfsCache;\n\n constructor(config: IpfsHttpClientConfig, cache: IpfsCache) {\n this.gateways = config.gateways;\n this.fetchTimeoutMs = config.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;\n this.resolveTimeoutMs = config.resolveTimeoutMs ?? DEFAULT_RESOLVE_TIMEOUT_MS;\n this.publishTimeoutMs = config.publishTimeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS;\n this.connectivityTimeoutMs = config.connectivityTimeoutMs ?? DEFAULT_CONNECTIVITY_TIMEOUT_MS;\n this.debug = config.debug ?? false;\n this.cache = cache;\n }\n\n // ---------------------------------------------------------------------------\n // Public Accessors\n // ---------------------------------------------------------------------------\n\n /**\n * Get configured gateway URLs.\n */\n getGateways(): string[] {\n return [...this.gateways];\n }\n\n // ---------------------------------------------------------------------------\n // Gateway Health\n // ---------------------------------------------------------------------------\n\n /**\n * Test connectivity to a single gateway.\n */\n async testConnectivity(gateway: string): Promise<GatewayHealthResult> {\n const start = Date.now();\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/version`,\n this.connectivityTimeoutMs,\n { method: 'POST' },\n );\n\n if (!response.ok) {\n return { gateway, healthy: false, error: `HTTP ${response.status}` };\n }\n\n return {\n gateway,\n healthy: true,\n responseTimeMs: Date.now() - start,\n };\n } catch (error) {\n return {\n gateway,\n healthy: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * Find healthy gateways from the configured list.\n */\n async findHealthyGateways(): Promise<string[]> {\n const results = await Promise.allSettled(\n this.gateways.map((gw) => this.testConnectivity(gw)),\n );\n\n return results\n .filter((r): r is PromiseFulfilledResult<GatewayHealthResult> =>\n r.status === 'fulfilled' && r.value.healthy)\n .map((r) => r.value.gateway);\n }\n\n /**\n * Get gateways that are not in circuit breaker cooldown.\n */\n getAvailableGateways(): string[] {\n return this.gateways.filter((gw) => !this.cache.isGatewayInCooldown(gw));\n }\n\n // ---------------------------------------------------------------------------\n // Content Upload\n // ---------------------------------------------------------------------------\n\n /**\n * Upload JSON content to IPFS.\n * Tries all gateways in parallel, returns first success.\n */\n async upload(data: unknown, gateways?: string[]): Promise<{ cid: string }> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n throw new IpfsError('No gateways available for upload', 'NETWORK_ERROR');\n }\n\n const jsonBytes = new TextEncoder().encode(JSON.stringify(data));\n\n const promises = targets.map(async (gateway) => {\n try {\n const formData = new FormData();\n formData.append('file', new Blob([jsonBytes], { type: 'application/json' }), 'data.json');\n\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/add?pin=true&cid-version=1`,\n this.publishTimeoutMs,\n { method: 'POST', body: formData },\n );\n\n if (!response.ok) {\n throw new IpfsError(\n `Upload failed: HTTP ${response.status}`,\n classifyHttpStatus(response.status),\n gateway,\n );\n }\n\n const result = await response.json();\n this.cache.recordGatewaySuccess(gateway);\n this.log(`Uploaded to ${gateway}: CID=${result.Hash}`);\n return { cid: result.Hash as string, gateway };\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n throw error;\n }\n });\n\n try {\n const result = await Promise.any(promises);\n return { cid: result.cid };\n } catch (error) {\n if (error instanceof AggregateError) {\n throw new IpfsError(\n `Upload failed on all gateways: ${error.errors.map(e => e.message).join('; ')}`,\n 'NETWORK_ERROR',\n );\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Content Fetch\n // ---------------------------------------------------------------------------\n\n /**\n * Fetch content by CID from IPFS gateways.\n * Checks content cache first. Races all gateways for fastest response.\n */\n async fetchContent<T extends TxfStorageDataBase>(\n cid: string,\n gateways?: string[],\n ): Promise<T> {\n // Check content cache (infinite TTL for immutable content)\n const cached = this.cache.getContent(cid);\n if (cached) {\n this.log(`Content cache hit for CID=${cid}`);\n return cached as T;\n }\n\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n throw new IpfsError('No gateways available for fetch', 'NETWORK_ERROR');\n }\n\n const promises = targets.map(async (gateway) => {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/ipfs/${cid}`,\n this.fetchTimeoutMs,\n { headers: { Accept: 'application/octet-stream' } },\n );\n\n if (!response.ok) {\n const body = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n throw new IpfsError(\n `Fetch failed: HTTP ${response.status}`,\n classifyHttpStatus(response.status, body),\n gateway,\n );\n }\n\n const data = await response.json() as T;\n this.cache.recordGatewaySuccess(gateway);\n this.cache.setContent(cid, data);\n this.log(`Fetched from ${gateway}: CID=${cid}`);\n return data;\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n throw error;\n }\n });\n\n try {\n return await Promise.any(promises);\n } catch (error) {\n if (error instanceof AggregateError) {\n throw new IpfsError(\n `Fetch failed on all gateways for CID=${cid}`,\n 'NETWORK_ERROR',\n );\n }\n throw error;\n }\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve IPNS via Routing API (returns record with sequence number).\n * POST /api/v0/routing/get?arg=/ipns/{name}\n */\n async resolveIpnsViaRoutingApi(\n gateway: string,\n ipnsName: string,\n timeoutMs: number = DEFAULT_ROUTING_API_TIMEOUT_MS,\n ): Promise<IpnsGatewayResult | null> {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/routing/get?arg=/ipns/${ipnsName}`,\n timeoutMs,\n { method: 'POST' },\n );\n\n if (!response.ok) {\n const body = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n const category = classifyHttpStatus(response.status, body);\n if (category === 'NOT_FOUND') return null;\n throw new IpfsError(`Routing API: HTTP ${response.status}`, category, gateway);\n }\n\n const text = await response.text();\n const parsed = await parseRoutingApiResponse(text);\n\n if (!parsed) return null;\n\n this.cache.recordGatewaySuccess(gateway);\n return {\n cid: parsed.cid,\n sequence: parsed.sequence,\n gateway,\n recordData: parsed.recordData,\n };\n } catch (error) {\n if (error instanceof IpfsError) throw error;\n const category = classifyFetchError(error);\n if (category !== 'NOT_FOUND') {\n this.cache.recordGatewayFailure(gateway);\n }\n return null;\n }\n }\n\n /**\n * Resolve IPNS via gateway path (simpler, no sequence number).\n * GET /ipns/{name}?format=dag-json\n */\n async resolveIpnsViaGatewayPath(\n gateway: string,\n ipnsName: string,\n timeoutMs: number = DEFAULT_GATEWAY_PATH_TIMEOUT_MS,\n ): Promise<{ cid: string; content?: unknown } | null> {\n try {\n const response = await this.fetchWithTimeout(\n `${gateway}/ipns/${ipnsName}`,\n timeoutMs,\n { headers: { Accept: 'application/json' } },\n );\n\n if (!response.ok) return null;\n\n const content = await response.json();\n const cidHeader = response.headers.get('X-Ipfs-Path');\n if (cidHeader) {\n const match = cidHeader.match(/\\/ipfs\\/([a-zA-Z0-9]+)/);\n if (match) {\n this.cache.recordGatewaySuccess(gateway);\n return { cid: match[1], content };\n }\n }\n\n return { cid: '', content };\n } catch (err) {\n logger.debug('IPFS-HTTP', 'IPNS gateway resolution failed', err);\n return null;\n }\n }\n\n /**\n * Progressive IPNS resolution across all gateways.\n * Queries all gateways in parallel, returns highest sequence number.\n */\n async resolveIpns(\n ipnsName: string,\n gateways?: string[],\n ): Promise<IpnsProgressiveResult> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n return { best: null, allResults: [], respondedCount: 0, totalGateways: 0 };\n }\n\n const results: IpnsGatewayResult[] = [];\n let respondedCount = 0;\n\n const promises = targets.map(async (gateway) => {\n const result = await this.resolveIpnsViaRoutingApi(\n gateway,\n ipnsName,\n this.resolveTimeoutMs,\n );\n if (result) results.push(result);\n respondedCount++;\n return result;\n });\n\n // Wait for all to complete (with overall timeout)\n await Promise.race([\n Promise.allSettled(promises),\n new Promise<void>((resolve) =>\n setTimeout(resolve, this.resolveTimeoutMs + 1000)),\n ]);\n\n // Find best result (highest sequence)\n let best: IpnsGatewayResult | null = null;\n for (const result of results) {\n if (!best || result.sequence > best.sequence) {\n best = result;\n }\n }\n\n if (best) {\n this.cache.setIpnsRecord(ipnsName, best);\n }\n\n return {\n best,\n allResults: results,\n respondedCount,\n totalGateways: targets.length,\n };\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Publishing\n // ---------------------------------------------------------------------------\n\n /**\n * Publish IPNS record to a single gateway via routing API.\n */\n async publishIpnsViaRoutingApi(\n gateway: string,\n ipnsName: string,\n marshalledRecord: Uint8Array,\n timeoutMs: number = DEFAULT_PUBLISH_TIMEOUT_MS,\n ): Promise<boolean> {\n try {\n const formData = new FormData();\n formData.append(\n 'file',\n new Blob([new Uint8Array(marshalledRecord)]),\n 'record',\n );\n\n const response = await this.fetchWithTimeout(\n `${gateway}/api/v0/routing/put?arg=/ipns/${ipnsName}&allow-offline=true`,\n timeoutMs,\n { method: 'POST', body: formData },\n );\n\n if (!response.ok) {\n const errorText = await response.text().catch((err) => { logger.debug('IPFS-HTTP', 'Failed to read error response body', err); return ''; });\n throw new IpfsError(\n `IPNS publish: HTTP ${response.status}: ${errorText.slice(0, 100)}`,\n classifyHttpStatus(response.status, errorText),\n gateway,\n );\n }\n\n this.cache.recordGatewaySuccess(gateway);\n this.log(`IPNS published to ${gateway}: ${ipnsName}`);\n return true;\n } catch (error) {\n if (error instanceof IpfsError && error.shouldTriggerCircuitBreaker) {\n this.cache.recordGatewayFailure(gateway);\n }\n this.log(`IPNS publish to ${gateway} failed: ${error}`);\n return false;\n }\n }\n\n /**\n * Publish IPNS record to all gateways in parallel.\n */\n async publishIpns(\n ipnsName: string,\n marshalledRecord: Uint8Array,\n gateways?: string[],\n ): Promise<IpnsPublishResult> {\n const targets = gateways ?? this.getAvailableGateways();\n if (targets.length === 0) {\n return { success: false, error: 'No gateways available' };\n }\n\n const results = await Promise.allSettled(\n targets.map((gw) =>\n this.publishIpnsViaRoutingApi(gw, ipnsName, marshalledRecord, this.publishTimeoutMs)),\n );\n\n const successfulGateways: string[] = [];\n results.forEach((result, index) => {\n if (result.status === 'fulfilled' && result.value) {\n successfulGateways.push(targets[index]);\n }\n });\n\n return {\n success: successfulGateways.length > 0,\n ipnsName,\n successfulGateways,\n error: successfulGateways.length === 0 ? 'All gateways failed' : undefined,\n };\n }\n\n // ---------------------------------------------------------------------------\n // IPNS Verification\n // ---------------------------------------------------------------------------\n\n /**\n * Verify IPNS record persistence after publishing.\n * Retries resolution to confirm the record was accepted.\n */\n async verifyIpnsRecord(\n ipnsName: string,\n expectedSeq: bigint,\n expectedCid: string,\n retries: number = 3,\n delayMs: number = 1000,\n ): Promise<boolean> {\n for (let i = 0; i < retries; i++) {\n if (i > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n const { best } = await this.resolveIpns(ipnsName);\n if (best && best.sequence >= expectedSeq && best.cid === expectedCid) {\n return true;\n }\n }\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n private async fetchWithTimeout(\n url: string,\n timeoutMs: number,\n options?: RequestInit,\n ): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n return await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n }\n\n private log(message: string): void {\n logger.debug('IPFS-HTTP', message);\n }\n}\n","/**\n * TXF Data Merge / Conflict Resolution\n * Merges local and remote TXF storage data with proper conflict handling\n */\n\nimport type { TxfStorageDataBase, TxfTombstone } from '../../../storage';\nimport type { MergeResult } from './ipfs-types';\n\n// =============================================================================\n// Merge Logic\n// =============================================================================\n\n/**\n * Merge local and remote TXF data.\n *\n * Rules:\n * 1. Meta: Higher version wins as base; increment by 1\n * 2. Tombstones: Union by composite key (tokenId, stateHash)\n * 3. Token entries: present in only one source -> add; both -> local wins\n * 4. Tombstone filtering: exclude tokens present in merged tombstones\n * 5. Outbox/Sent: Union with dedup by id/tokenId\n */\nexport function mergeTxfData<T extends TxfStorageDataBase>(\n local: T,\n remote: T,\n): MergeResult<T> {\n let added = 0;\n let removed = 0;\n let conflicts = 0;\n\n // 1. Merge meta — use higher version as base, increment\n const localVersion = local._meta?.version ?? 0;\n const remoteVersion = remote._meta?.version ?? 0;\n const baseMeta = localVersion >= remoteVersion ? local._meta : remote._meta;\n const mergedMeta = {\n ...baseMeta,\n version: Math.max(localVersion, remoteVersion) + 1,\n updatedAt: Date.now(),\n };\n\n // 2. Merge tombstones — union by composite key (tokenId + stateHash)\n const mergedTombstones = mergeTombstones(\n local._tombstones ?? [],\n remote._tombstones ?? [],\n );\n const tombstoneKeys = new Set(\n mergedTombstones.map((t) => `${t.tokenId}:${t.stateHash}`),\n );\n\n // 3. Merge token entries\n const localTokenKeys = getTokenKeys(local);\n const remoteTokenKeys = getTokenKeys(remote);\n const allTokenKeys = new Set([...localTokenKeys, ...remoteTokenKeys]);\n\n const mergedTokens: Record<string, unknown> = {};\n\n for (const key of allTokenKeys) {\n const tokenId = key.startsWith('_') ? key.slice(1) : key;\n const localToken = (local as Record<string, unknown>)[key];\n const remoteToken = (remote as Record<string, unknown>)[key];\n\n // Check tombstone filter\n if (isTokenTombstoned(tokenId, localToken, remoteToken, tombstoneKeys)) {\n if (localTokenKeys.has(key)) removed++;\n continue;\n }\n\n if (localToken && !remoteToken) {\n // Only in local\n mergedTokens[key] = localToken;\n } else if (!localToken && remoteToken) {\n // Only in remote\n mergedTokens[key] = remoteToken;\n added++;\n } else if (localToken && remoteToken) {\n // In both — local wins (with conflict count)\n mergedTokens[key] = localToken;\n conflicts++;\n }\n }\n\n // 4. Merge outbox — union with dedup by id\n const mergedOutbox = mergeArrayById(\n local._outbox ?? [],\n remote._outbox ?? [],\n 'id',\n );\n\n // 5. Merge sent — union with dedup by tokenId\n const mergedSent = mergeArrayById(\n local._sent ?? [],\n remote._sent ?? [],\n 'tokenId',\n );\n\n // 6. Merge invalid — union with dedup by tokenId\n const mergedInvalid = mergeArrayById(\n local._invalid ?? [],\n remote._invalid ?? [],\n 'tokenId',\n );\n\n // 7. Merge nametags — union by name, local wins on conflict\n type NametagEntry = { name: string; [key: string]: unknown };\n const localNametags = (local._nametags ?? []) as NametagEntry[];\n const remoteNametags = (remote._nametags ?? []) as NametagEntry[];\n const mergedNametags = mergeNametagsByName(localNametags, remoteNametags);\n\n // 8. Merge history — union with dedup by dedupKey (same pattern as _outbox/_sent/_invalid)\n const localHistory = (local._history ?? []) as Array<{ dedupKey: string }>;\n const remoteHistory = (remote._history ?? []) as Array<{ dedupKey: string }>;\n const mergedHistory = mergeArrayById(localHistory, remoteHistory, 'dedupKey');\n\n // Build merged result\n const merged = {\n _meta: mergedMeta,\n _tombstones: mergedTombstones.length > 0 ? mergedTombstones : undefined,\n _nametags: mergedNametags.length > 0 ? mergedNametags : undefined,\n _outbox: mergedOutbox.length > 0 ? mergedOutbox : undefined,\n _sent: mergedSent.length > 0 ? mergedSent : undefined,\n _invalid: mergedInvalid.length > 0 ? mergedInvalid : undefined,\n _history: mergedHistory.length > 0 ? mergedHistory : undefined,\n ...mergedTokens,\n } as unknown as T;\n\n return { merged, added, removed, conflicts };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Merge tombstone arrays by composite key (tokenId + stateHash).\n * On duplicates, keep the one with the newer timestamp.\n */\nfunction mergeTombstones(\n local: TxfTombstone[],\n remote: TxfTombstone[],\n): TxfTombstone[] {\n const merged = new Map<string, TxfTombstone>();\n\n for (const tombstone of [...local, ...remote]) {\n const key = `${tombstone.tokenId}:${tombstone.stateHash}`;\n const existing = merged.get(key);\n if (!existing || tombstone.timestamp > existing.timestamp) {\n merged.set(key, tombstone);\n }\n }\n\n return Array.from(merged.values());\n}\n\n/**\n * Get all token entry keys from TXF data.\n * Token keys start with '_' but are not meta fields.\n */\nfunction getTokenKeys(data: TxfStorageDataBase): Set<string> {\n const reservedKeys = new Set([\n '_meta', '_tombstones', '_outbox', '_sent', '_invalid',\n '_nametag', '_nametags', '_mintOutbox', '_invalidatedNametags', '_integrity',\n '_history',\n ]);\n const keys = new Set<string>();\n\n for (const key of Object.keys(data)) {\n if (reservedKeys.has(key)) continue;\n // Exclude archived and forked entries from token merge\n if (key.startsWith('archived-') || key.startsWith('_forked_')) continue;\n keys.add(key);\n }\n\n return keys;\n}\n\n/**\n * Check if a token should be filtered by tombstones.\n */\nfunction isTokenTombstoned(\n tokenId: string,\n localToken: unknown,\n remoteToken: unknown,\n tombstoneKeys: Set<string>,\n): boolean {\n // Check if any variant of this token is tombstoned\n // We check generic tokenId matching since we may not have the stateHash here\n for (const key of tombstoneKeys) {\n if (key.startsWith(`${tokenId}:`)) {\n return true;\n }\n }\n // Keep token if not tombstoned\n void localToken;\n void remoteToken;\n return false;\n}\n\n/**\n * Merge nametag arrays by name, deduplicating.\n * On duplicates, keep the local entry.\n */\nfunction mergeNametagsByName(\n local: Array<{ name: string; [key: string]: unknown }>,\n remote: Array<{ name: string; [key: string]: unknown }>,\n): Array<{ name: string; [key: string]: unknown }> {\n const seen = new Map<string, { name: string; [key: string]: unknown }>();\n\n for (const item of local) {\n if (item.name) seen.set(item.name, item);\n }\n for (const item of remote) {\n if (item.name && !seen.has(item.name)) {\n seen.set(item.name, item);\n }\n }\n\n return Array.from(seen.values());\n}\n\n/**\n * Merge arrays by a key field, deduplicating.\n * On duplicates, keep the one from the first array (local).\n */\nfunction mergeArrayById<T>(\n local: T[],\n remote: T[],\n idField: keyof T,\n): T[] {\n const seen = new Map<unknown, T>();\n\n for (const item of local) {\n const id = item[idField];\n if (id !== undefined) {\n seen.set(id, item);\n }\n }\n\n for (const item of remote) {\n const id = item[idField];\n if (id !== undefined && !seen.has(id)) {\n seen.set(id, item);\n }\n }\n\n return Array.from(seen.values());\n}\n","/**\n * WebSocket client for IPNS subscription updates.\n * Connects to IPFS sidecar and receives push notifications for IPNS changes.\n *\n * Architecture:\n * - Connects to /ws/ipns endpoint on IPFS gateway\n * - Clients subscribe to specific IPNS names\n * - Server pushes updates when IPNS records change\n * - Auto-reconnects on connection loss with exponential backoff\n * - Falls back to polling when WebSocket is unavailable\n *\n * Ported from Sphere webapp's IpnsSubscriptionClient.\n */\n\nimport { logger } from '../../../core/logger';\nimport type { IWebSocket, WebSocketFactory } from '../../../transport/websocket';\nimport { WebSocketReadyState } from '../../../transport/websocket';\nimport type { IpnsUpdateEvent } from './ipfs-types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface IpnsSubscriptionClientConfig {\n /** WebSocket URL (e.g., wss://host/ws/ipns) */\n wsUrl: string;\n /** Platform-specific WebSocket factory */\n createWebSocket: WebSocketFactory;\n /** Ping interval in ms (default: 30000) */\n pingIntervalMs?: number;\n /** Initial reconnect delay in ms (default: 5000) */\n reconnectDelayMs?: number;\n /** Max reconnect delay in ms (default: 60000) */\n maxReconnectDelayMs?: number;\n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\ntype UpdateCallback = (update: IpnsUpdateEvent) => void;\n\ninterface WebSocketMessage {\n type: string;\n name?: string;\n names?: string[];\n sequence?: number;\n cid?: string | null;\n timestamp?: string;\n message?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class IpnsSubscriptionClient {\n private ws: IWebSocket | null = null;\n private readonly subscriptions: Map<string, Set<UpdateCallback>> = new Map();\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n private pingInterval: ReturnType<typeof setInterval> | null = null;\n private fallbackPollInterval: ReturnType<typeof setInterval> | null = null;\n\n private readonly wsUrl: string;\n private readonly createWebSocket: WebSocketFactory;\n private readonly pingIntervalMs: number;\n private readonly initialReconnectDelayMs: number;\n private readonly maxReconnectDelayMs: number;\n private readonly debugEnabled: boolean;\n\n private reconnectAttempts = 0;\n private isConnecting = false;\n private connectionOpenedAt = 0;\n private destroyed = false;\n\n /** Minimum stable connection time before resetting backoff (30 seconds) */\n private readonly minStableConnectionMs = 30000;\n\n private fallbackPollFn: (() => Promise<void>) | null = null;\n private fallbackPollIntervalMs = 0;\n\n constructor(config: IpnsSubscriptionClientConfig) {\n this.wsUrl = config.wsUrl;\n this.createWebSocket = config.createWebSocket;\n this.pingIntervalMs = config.pingIntervalMs ?? 30000;\n this.initialReconnectDelayMs = config.reconnectDelayMs ?? 5000;\n this.maxReconnectDelayMs = config.maxReconnectDelayMs ?? 60000;\n this.debugEnabled = config.debug ?? false;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /**\n * Subscribe to IPNS updates for a specific name.\n * Automatically connects the WebSocket if not already connected.\n * If WebSocket is connecting, the name will be subscribed once connected.\n */\n subscribe(ipnsName: string, callback: UpdateCallback): () => void {\n if (!ipnsName || typeof ipnsName !== 'string') {\n this.log('Invalid IPNS name for subscription');\n return () => { /* noop */ };\n }\n\n const isNewSubscription = !this.subscriptions.has(ipnsName);\n\n if (isNewSubscription) {\n this.subscriptions.set(ipnsName, new Set());\n }\n\n this.subscriptions.get(ipnsName)!.add(callback);\n\n // Send subscription to server if already connected and this is a new name\n if (isNewSubscription && this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.sendSubscribe([ipnsName]);\n }\n\n // Connect if not already connected\n if (!this.ws || this.ws.readyState !== WebSocketReadyState.OPEN) {\n this.connect();\n }\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.subscriptions.get(ipnsName);\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete(ipnsName);\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.sendUnsubscribe([ipnsName]);\n }\n // Disconnect if no more subscriptions\n if (this.subscriptions.size === 0) {\n this.disconnect();\n }\n }\n }\n };\n }\n\n /**\n * Register a convenience update callback for all subscriptions.\n * Returns an unsubscribe function.\n */\n onUpdate(callback: UpdateCallback): () => void {\n // Store as a special \"global\" callback keyed by '*'\n if (!this.subscriptions.has('*')) {\n this.subscriptions.set('*', new Set());\n }\n this.subscriptions.get('*')!.add(callback);\n\n return () => {\n const callbacks = this.subscriptions.get('*');\n if (callbacks) {\n callbacks.delete(callback);\n if (callbacks.size === 0) {\n this.subscriptions.delete('*');\n }\n }\n };\n }\n\n /**\n * Set a fallback poll function to use when WebSocket is disconnected.\n * The poll function will be called at the specified interval while WS is down.\n */\n setFallbackPoll(fn: () => Promise<void>, intervalMs: number): void {\n this.fallbackPollFn = fn;\n this.fallbackPollIntervalMs = intervalMs;\n // Start polling if WS is not connected\n if (!this.isConnected()) {\n this.startFallbackPolling();\n }\n }\n\n /**\n * Connect to the WebSocket server.\n */\n connect(): void {\n if (this.destroyed) return;\n if (this.ws?.readyState === WebSocketReadyState.OPEN || this.isConnecting) {\n return;\n }\n\n this.isConnecting = true;\n\n try {\n this.log(`Connecting to ${this.wsUrl}...`);\n this.ws = this.createWebSocket(this.wsUrl);\n\n this.ws.onopen = () => {\n this.log('WebSocket connected');\n this.isConnecting = false;\n this.connectionOpenedAt = Date.now();\n\n // Resubscribe to all IPNS names (excluding '*' global key)\n const names = Array.from(this.subscriptions.keys()).filter(n => n !== '*');\n if (names.length > 0) {\n this.sendSubscribe(names);\n }\n\n // Start ping interval to keep connection alive\n this.startPingInterval();\n\n // Stop fallback polling — WS is live\n this.stopFallbackPolling();\n };\n\n this.ws.onmessage = (event) => {\n this.handleMessage(event.data);\n };\n\n this.ws.onclose = () => {\n const connectionDuration = this.connectionOpenedAt > 0\n ? Date.now() - this.connectionOpenedAt\n : 0;\n const wasStable = connectionDuration >= this.minStableConnectionMs;\n\n this.log(`WebSocket closed (duration: ${Math.round(connectionDuration / 1000)}s)`);\n\n this.isConnecting = false;\n this.connectionOpenedAt = 0;\n this.stopPingInterval();\n\n // Only reset backoff if connection was stable\n if (wasStable) {\n this.reconnectAttempts = 0;\n }\n\n // Start fallback polling while WS is down\n this.startFallbackPolling();\n\n this.scheduleReconnect();\n };\n\n this.ws.onerror = () => {\n this.log('WebSocket error');\n this.isConnecting = false;\n };\n } catch (e) {\n this.log(`Failed to connect: ${e}`);\n this.isConnecting = false;\n this.startFallbackPolling();\n this.scheduleReconnect();\n }\n }\n\n /**\n * Disconnect from the WebSocket server and clean up.\n */\n disconnect(): void {\n this.destroyed = true;\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.stopPingInterval();\n this.stopFallbackPolling();\n\n if (this.ws) {\n this.ws.onopen = null;\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close();\n this.ws = null;\n }\n this.isConnecting = false;\n this.reconnectAttempts = 0;\n }\n\n /**\n * Check if connected to the WebSocket server.\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocketReadyState.OPEN;\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Message Handling\n // ---------------------------------------------------------------------------\n\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data) as WebSocketMessage;\n\n switch (message.type) {\n case 'update':\n if (message.name && message.sequence !== undefined) {\n this.notifySubscribers({\n type: 'update',\n name: message.name,\n sequence: message.sequence,\n cid: message.cid ?? '',\n timestamp: message.timestamp || new Date().toISOString(),\n });\n }\n break;\n\n case 'subscribed':\n this.log(`Subscribed to ${message.names?.length || 0} names`);\n break;\n\n case 'unsubscribed':\n this.log(`Unsubscribed from ${message.names?.length || 0} names`);\n break;\n\n case 'pong':\n // Keepalive response received\n break;\n\n case 'error':\n this.log(`Server error: ${message.message}`);\n break;\n\n default:\n // Unknown message type — ignore\n break;\n }\n } catch {\n this.log('Failed to parse message');\n }\n }\n\n private notifySubscribers(update: IpnsUpdateEvent & { type: string }): void {\n // Notify name-specific subscribers\n const callbacks = this.subscriptions.get(update.name);\n if (callbacks) {\n this.log(`Update: ${update.name.slice(0, 16)}... seq=${update.sequence}`);\n for (const callback of callbacks) {\n try {\n callback(update);\n } catch {\n // Don't let callback errors break the client\n }\n }\n }\n\n // Notify global subscribers\n const globalCallbacks = this.subscriptions.get('*');\n if (globalCallbacks) {\n for (const callback of globalCallbacks) {\n try {\n callback(update);\n } catch {\n // Don't let callback errors break the client\n }\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: WebSocket Send\n // ---------------------------------------------------------------------------\n\n private sendSubscribe(names: string[]): void {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'subscribe', names }));\n }\n }\n\n private sendUnsubscribe(names: string[]): void {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'unsubscribe', names }));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Reconnection\n // ---------------------------------------------------------------------------\n\n /**\n * Schedule reconnection with exponential backoff.\n * Sequence: 5s, 10s, 20s, 40s, 60s (capped)\n */\n private scheduleReconnect(): void {\n if (this.destroyed || this.reconnectTimeout) return;\n\n // Don't reconnect if no subscriptions (excluding '*')\n const realSubscriptions = Array.from(this.subscriptions.keys()).filter(n => n !== '*');\n if (realSubscriptions.length === 0) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n this.initialReconnectDelayMs * Math.pow(2, this.reconnectAttempts - 1),\n this.maxReconnectDelayMs,\n );\n\n this.log(`Reconnecting in ${(delay / 1000).toFixed(1)}s (attempt ${this.reconnectAttempts})...`);\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n this.connect();\n }, delay);\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Keepalive\n // ---------------------------------------------------------------------------\n\n private startPingInterval(): void {\n this.stopPingInterval();\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocketReadyState.OPEN) {\n this.ws.send(JSON.stringify({ action: 'ping' }));\n }\n }, this.pingIntervalMs);\n }\n\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Fallback Polling\n // ---------------------------------------------------------------------------\n\n private startFallbackPolling(): void {\n if (this.fallbackPollInterval || !this.fallbackPollFn || this.destroyed) return;\n\n this.log(`Starting fallback polling (${this.fallbackPollIntervalMs / 1000}s interval)`);\n\n // Run poll immediately once\n this.fallbackPollFn().catch((err) => { logger.warn('IPNS-WS', 'Fallback poll error:', err); });\n\n this.fallbackPollInterval = setInterval(() => {\n this.fallbackPollFn?.().catch((err) => { logger.warn('IPNS-WS', 'Fallback poll error:', err); });\n }, this.fallbackPollIntervalMs);\n }\n\n private stopFallbackPolling(): void {\n if (this.fallbackPollInterval) {\n clearInterval(this.fallbackPollInterval);\n this.fallbackPollInterval = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Logging\n // ---------------------------------------------------------------------------\n\n private log(message: string): void {\n logger.debug('IPNS-WS', message);\n }\n}\n","/**\n * Write-Behind Buffer for IpfsStorageProvider\n *\n * Provides non-blocking writes via double-buffering and a promise-chain\n * serial queue. Writes are accepted immediately into a pending buffer\n * and flushed to IPFS asynchronously in the background.\n */\n\nimport type { TxfStorageDataBase } from '../../../storage';\n\n// =============================================================================\n// AsyncSerialQueue\n// =============================================================================\n\n/**\n * Promise-chain-based async mutex. Serializes async operations\n * without external dependencies. Each enqueued operation waits for\n * the previous one to complete before starting.\n */\nexport class AsyncSerialQueue {\n private tail: Promise<void> = Promise.resolve();\n\n /** Enqueue an async operation. Returns when it completes. */\n enqueue<T>(fn: () => Promise<T>): Promise<T> {\n let resolve: (value: T) => void;\n let reject: (reason: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n this.tail = this.tail.then(\n () => fn().then(resolve!, reject!),\n () => fn().then(resolve!, reject!),\n );\n\n return promise;\n }\n}\n\n// =============================================================================\n// WriteBuffer\n// =============================================================================\n\n/**\n * Collects mutations (token saves/deletes + full TXF data)\n * between flush cycles. Acts as a staging area for changes\n * that haven't been persisted to IPFS yet.\n */\nexport class WriteBuffer {\n /** Full TXF data from save() calls — latest wins */\n txfData: TxfStorageDataBase | null = null;\n\n get isEmpty(): boolean {\n return this.txfData === null;\n }\n\n clear(): void {\n this.txfData = null;\n }\n\n /**\n * Merge another buffer's contents into this one (for rollback).\n * Existing (newer) mutations in `this` take precedence over `other`.\n */\n mergeFrom(other: WriteBuffer): void {\n if (other.txfData && !this.txfData) {\n this.txfData = other.txfData;\n }\n }\n}\n","/**\n * IPFS Storage Provider\n * Main TokenStorageProvider implementation using IPFS/IPNS.\n * Shared cross-platform module (browser + Node.js via native fetch).\n *\n * Uses a write-behind buffer for non-blocking save() operations.\n * Writes are accepted immediately and flushed to IPFS asynchronously.\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity } from '../../../types';\nimport type {\n TokenStorageProvider,\n TxfStorageDataBase,\n SaveResult,\n LoadResult,\n SyncResult,\n StorageEventCallback,\n StorageEvent,\n} from '../../../storage';\nimport type {\n IpfsStorageConfig,\n IpfsStatePersistence,\n} from './ipfs-types';\nimport type { WebSocketFactory } from '../../../transport/websocket';\nimport { getIpfsGatewayUrls } from '../../../constants';\nimport { IpfsCache } from './ipfs-cache';\nimport { IpfsHttpClient } from './ipfs-http-client';\nimport { IpnsSubscriptionClient } from './ipns-subscription-client';\nimport { deriveIpnsIdentity } from './ipns-key-derivation';\nimport { createSignedRecord } from './ipns-record-manager';\nimport { mergeTxfData } from './txf-merge';\nimport { InMemoryIpfsStatePersistence } from './ipfs-state-persistence';\nimport { AsyncSerialQueue, WriteBuffer } from './write-behind-buffer';\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class IpfsStorageProvider<TData extends TxfStorageDataBase = TxfStorageDataBase>\n implements TokenStorageProvider<TData>\n{\n readonly id = 'ipfs';\n readonly name = 'IPFS Storage';\n readonly type = 'p2p' as const;\n\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n private ipnsKeyPair: unknown = null;\n private ipnsName: string | null = null;\n private ipnsSequenceNumber: bigint = 0n;\n private lastCid: string | null = null;\n private lastKnownRemoteSequence: bigint = 0n;\n private dataVersion = 0;\n\n /**\n * The CID currently stored on the sidecar for this IPNS name.\n * Used as `_meta.lastCid` in the next save to satisfy chain validation.\n * - null for bootstrap (first-ever save)\n * - set after every successful save() or load()\n */\n private remoteCid: string | null = null;\n\n private readonly cache: IpfsCache;\n private readonly httpClient: IpfsHttpClient;\n private readonly statePersistence: IpfsStatePersistence;\n private readonly eventCallbacks: Set<StorageEventCallback> = new Set();\n private readonly debug: boolean;\n private readonly ipnsLifetimeMs: number;\n\n /** WebSocket factory for push subscriptions */\n private readonly createWebSocket: WebSocketFactory | undefined;\n /** Override WS URL */\n private readonly wsUrl: string | undefined;\n /** Fallback poll interval (default: 90000) */\n private readonly fallbackPollIntervalMs: number;\n /** IPNS subscription client for push notifications */\n private subscriptionClient: IpnsSubscriptionClient | null = null;\n /** Unsubscribe function from subscription client */\n private subscriptionUnsubscribe: (() => void) | null = null;\n\n /** Write-behind buffer: serializes flush / sync / shutdown */\n private readonly flushQueue = new AsyncSerialQueue();\n /** Pending mutations not yet flushed to IPFS */\n private pendingBuffer = new WriteBuffer();\n /** Debounce timer for background flush */\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n /** Debounce interval in ms */\n private readonly flushDebounceMs: number;\n /** Set to true during shutdown to prevent new flushes */\n private isShuttingDown = false;\n\n constructor(\n config?: IpfsStorageConfig,\n statePersistence?: IpfsStatePersistence,\n ) {\n const gateways = config?.gateways ?? getIpfsGatewayUrls();\n this.debug = config?.debug ?? false;\n this.ipnsLifetimeMs = config?.ipnsLifetimeMs ?? (99 * 365 * 24 * 60 * 60 * 1000);\n this.flushDebounceMs = config?.flushDebounceMs ?? 2000;\n\n this.cache = new IpfsCache({\n ipnsTtlMs: config?.ipnsCacheTtlMs,\n failureCooldownMs: config?.circuitBreakerCooldownMs,\n failureThreshold: config?.circuitBreakerThreshold,\n knownFreshWindowMs: config?.knownFreshWindowMs,\n });\n\n this.httpClient = new IpfsHttpClient({\n gateways,\n fetchTimeoutMs: config?.fetchTimeoutMs,\n resolveTimeoutMs: config?.resolveTimeoutMs,\n publishTimeoutMs: config?.publishTimeoutMs,\n connectivityTimeoutMs: config?.connectivityTimeoutMs,\n debug: this.debug,\n }, this.cache);\n\n this.statePersistence = statePersistence ?? new InMemoryIpfsStatePersistence();\n this.createWebSocket = config?.createWebSocket;\n this.wsUrl = config?.wsUrl;\n this.fallbackPollIntervalMs = config?.fallbackPollIntervalMs ?? 90000;\n }\n\n // ---------------------------------------------------------------------------\n // BaseProvider interface\n // ---------------------------------------------------------------------------\n\n async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n await this.shutdown();\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ---------------------------------------------------------------------------\n // Identity & Initialization\n // ---------------------------------------------------------------------------\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n }\n\n async initialize(): Promise<boolean> {\n if (!this.identity) {\n this.log('Cannot initialize: no identity set');\n return false;\n }\n\n this.status = 'connecting';\n this.emitEvent({ type: 'storage:loading', timestamp: Date.now() });\n\n try {\n // Derive IPNS key pair and name from wallet private key\n const { keyPair, ipnsName } = await deriveIpnsIdentity(this.identity.privateKey);\n this.ipnsKeyPair = keyPair;\n this.ipnsName = ipnsName;\n this.log(`IPNS name derived: ${ipnsName}`);\n\n // Load persisted state\n const persisted = await this.statePersistence.load(ipnsName);\n if (persisted) {\n this.ipnsSequenceNumber = BigInt(persisted.sequenceNumber);\n this.lastCid = persisted.lastCid;\n this.remoteCid = persisted.lastCid; // chain link for next save\n this.dataVersion = persisted.version;\n this.log(`Loaded persisted state: seq=${this.ipnsSequenceNumber}, cid=${this.lastCid}`);\n }\n\n // Set up IPNS push subscription if WebSocket factory is available\n if (this.createWebSocket) {\n try {\n const wsUrlFinal = this.wsUrl ?? this.deriveWsUrl();\n if (wsUrlFinal) {\n this.subscriptionClient = new IpnsSubscriptionClient({\n wsUrl: wsUrlFinal,\n createWebSocket: this.createWebSocket,\n debug: this.debug,\n });\n\n // Subscribe to own IPNS name\n this.subscriptionUnsubscribe = this.subscriptionClient.subscribe(\n ipnsName,\n (update) => {\n this.log(`Push update: seq=${update.sequence}, cid=${update.cid}`);\n this.emitEvent({\n type: 'storage:remote-updated',\n timestamp: Date.now(),\n data: { name: update.name, sequence: update.sequence, cid: update.cid },\n });\n },\n );\n\n // Set fallback poll for when WS is disconnected\n this.subscriptionClient.setFallbackPoll(\n () => this.pollForRemoteChanges(),\n this.fallbackPollIntervalMs,\n );\n\n // Connect (non-blocking)\n this.subscriptionClient.connect();\n }\n } catch (wsError) {\n this.log(`Failed to set up IPNS subscription: ${wsError}`);\n // Non-fatal — provider works without push notifications\n }\n }\n\n // Test gateway connectivity (non-blocking, don't fail on it)\n this.httpClient.findHealthyGateways().then((healthy) => {\n if (healthy.length > 0) {\n this.log(`${healthy.length} healthy gateway(s) found`);\n } else {\n this.log('Warning: no healthy gateways found');\n }\n }).catch((err) => {\n logger.warn('IPFS-Storage', 'Gateway health check failed (non-fatal):', err);\n });\n\n this.isShuttingDown = false;\n this.status = 'connected';\n this.emitEvent({ type: 'storage:loaded', timestamp: Date.now() });\n return true;\n } catch (error) {\n this.status = 'error';\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n }\n\n async shutdown(): Promise<void> {\n this.isShuttingDown = true;\n\n // Cancel any pending debounced flush\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Final flush — drain any pending writes\n await this.flushQueue.enqueue(async () => {\n if (!this.pendingBuffer.isEmpty) {\n try {\n await this.executeFlush();\n } catch {\n this.log('Final flush on shutdown failed (data may be lost)');\n }\n }\n });\n\n // Disconnect subscription client\n if (this.subscriptionUnsubscribe) {\n this.subscriptionUnsubscribe();\n this.subscriptionUnsubscribe = null;\n }\n if (this.subscriptionClient) {\n this.subscriptionClient.disconnect();\n this.subscriptionClient = null;\n }\n\n this.cache.clear();\n this.status = 'disconnected';\n }\n\n // ---------------------------------------------------------------------------\n // Save (non-blocking — buffers data for async flush)\n // ---------------------------------------------------------------------------\n\n async save(data: TData): Promise<SaveResult> {\n if (!this.ipnsKeyPair || !this.ipnsName) {\n return { success: false, error: 'Not initialized', timestamp: Date.now() };\n }\n\n // Buffer the data for async flush\n this.pendingBuffer.txfData = data;\n this.scheduleFlush();\n\n // Return immediately — flush happens in background\n return { success: true, timestamp: Date.now() };\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Blocking save (used by sync and executeFlush)\n // ---------------------------------------------------------------------------\n\n /**\n * Perform the actual upload + IPNS publish synchronously.\n * Called by executeFlush() and sync() — never by public save().\n */\n private async _doSave(data: TData): Promise<SaveResult> {\n if (!this.ipnsKeyPair || !this.ipnsName) {\n return { success: false, error: 'Not initialized', timestamp: Date.now() };\n }\n\n this.emitEvent({ type: 'storage:saving', timestamp: Date.now() });\n\n try {\n // Update meta with chain-validation fields required by sidecar:\n // - lastCid: must equal the CID currently stored on sidecar (null for bootstrap)\n // - version: must be exactly current_version + 1 (for normal updates)\n this.dataVersion++;\n const metaUpdate: Record<string, unknown> = {\n ...data._meta,\n version: this.dataVersion,\n ipnsName: this.ipnsName,\n updatedAt: Date.now(),\n };\n if (this.remoteCid) {\n // Normal update: chain to previous CID\n metaUpdate.lastCid = this.remoteCid;\n }\n // Bootstrap (remoteCid is null): do NOT include lastCid field at all\n const updatedData = { ...data, _meta: metaUpdate } as unknown as Record<string, unknown>;\n\n // Upload to IPFS\n const { cid } = await this.httpClient.upload(updatedData);\n this.log(`Content uploaded: CID=${cid}`);\n\n // Compute new sequence: max(local, remote) + 1\n const baseSeq = this.ipnsSequenceNumber > this.lastKnownRemoteSequence\n ? this.ipnsSequenceNumber\n : this.lastKnownRemoteSequence;\n const newSeq = baseSeq + 1n;\n\n // Create signed IPNS record\n const marshalledRecord = await createSignedRecord(\n this.ipnsKeyPair,\n cid,\n newSeq,\n this.ipnsLifetimeMs,\n );\n\n // Publish to all gateways\n const publishResult = await this.httpClient.publishIpns(\n this.ipnsName,\n marshalledRecord,\n );\n\n if (!publishResult.success) {\n // Rollback version (sequence was not yet updated)\n this.dataVersion--;\n this.log(`IPNS publish failed: ${publishResult.error}`);\n return {\n success: false,\n error: publishResult.error ?? 'IPNS publish failed',\n timestamp: Date.now(),\n };\n }\n\n // Update local state\n this.ipnsSequenceNumber = newSeq;\n this.lastCid = cid;\n this.remoteCid = cid; // next save chains to this CID\n\n // Update cache\n this.cache.setIpnsRecord(this.ipnsName, {\n cid,\n sequence: newSeq,\n gateway: 'local',\n });\n this.cache.setContent(cid, updatedData as unknown as TxfStorageDataBase);\n this.cache.markIpnsFresh(this.ipnsName);\n\n // Persist state\n await this.statePersistence.save(this.ipnsName, {\n sequenceNumber: newSeq.toString(),\n lastCid: cid,\n version: this.dataVersion,\n });\n\n this.emitEvent({\n type: 'storage:saved',\n timestamp: Date.now(),\n data: { cid, sequence: newSeq.toString() },\n });\n\n this.log(`Saved: CID=${cid}, seq=${newSeq}`);\n return { success: true, cid, timestamp: Date.now() };\n } catch (error) {\n // Rollback version on any error\n this.dataVersion--;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return { success: false, error: errorMessage, timestamp: Date.now() };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Write-behind buffer: scheduling and flushing\n // ---------------------------------------------------------------------------\n\n /**\n * Schedule a debounced background flush.\n * Resets the timer on each call so rapid mutations coalesce.\n */\n private scheduleFlush(): void {\n if (this.isShuttingDown) return;\n if (this.flushTimer) clearTimeout(this.flushTimer);\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n this.log(`Background flush failed: ${err}`);\n });\n }, this.flushDebounceMs);\n }\n\n /**\n * Execute a flush of the pending buffer to IPFS.\n * Runs inside AsyncSerialQueue for concurrency safety.\n */\n private async executeFlush(): Promise<void> {\n if (this.pendingBuffer.isEmpty) return;\n\n // 1. Swap: take pending → active, create new empty pending\n const active = this.pendingBuffer;\n this.pendingBuffer = new WriteBuffer();\n\n try {\n // 2. Build the data to save\n // Use buffered TXF data if available, otherwise build minimal payload\n const baseData = (active.txfData ?? {\n _meta: { version: 0, address: this.identity?.directAddress ?? '', formatVersion: '2.0', updatedAt: 0 },\n }) as TData;\n\n // 3. Perform the actual blocking save\n const result = await this._doSave(baseData);\n\n if (!result.success) {\n throw new SphereError(result.error ?? 'Save failed', 'STORAGE_ERROR');\n }\n\n this.log(`Flushed successfully: CID=${result.cid}`);\n } catch (error) {\n // 4. Rollback: merge active back into pending\n this.pendingBuffer.mergeFrom(active);\n\n const msg = error instanceof Error ? error.message : String(error);\n this.log(`Flush failed (will retry): ${msg}`);\n\n // Schedule retry\n this.scheduleFlush();\n\n throw error; // re-throw so callers (e.g. shutdown) know it failed\n }\n }\n\n // ---------------------------------------------------------------------------\n // Load\n // ---------------------------------------------------------------------------\n\n async load(identifier?: string): Promise<LoadResult<TData>> {\n if (!this.ipnsName && !identifier) {\n return { success: false, error: 'Not initialized', source: 'local', timestamp: Date.now() };\n }\n\n this.emitEvent({ type: 'storage:loading', timestamp: Date.now() });\n\n try {\n // If a specific CID is given, fetch directly\n if (identifier) {\n const data = await this.httpClient.fetchContent<TData>(identifier);\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n }\n\n const ipnsName = this.ipnsName!;\n\n // Check known-fresh cache\n if (this.cache.isIpnsKnownFresh(ipnsName)) {\n const cached = this.cache.getIpnsRecordIgnoreTtl(ipnsName);\n if (cached) {\n const content = this.cache.getContent(cached.cid);\n if (content) {\n this.log('Using known-fresh cached data');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n }\n }\n\n // Check IPNS cache (60s TTL)\n const cachedRecord = this.cache.getIpnsRecord(ipnsName);\n if (cachedRecord) {\n const content = this.cache.getContent(cachedRecord.cid);\n if (content) {\n this.log('IPNS cache hit');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n // Have CID but not content — fetch content\n try {\n const data = await this.httpClient.fetchContent<TData>(cachedRecord.cid);\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n } catch {\n // Fall through to full resolution\n }\n }\n\n // Resolve IPNS from network\n const { best } = await this.httpClient.resolveIpns(ipnsName);\n\n if (!best) {\n // Not found — could be a new wallet\n this.log('IPNS record not found (new wallet?)');\n return { success: false, error: 'IPNS record not found', source: 'remote', timestamp: Date.now() };\n }\n\n // Track remote sequence and CID for chain validation\n if (best.sequence > this.lastKnownRemoteSequence) {\n this.lastKnownRemoteSequence = best.sequence;\n }\n this.remoteCid = best.cid;\n\n // Fetch content\n const data = await this.httpClient.fetchContent<TData>(best.cid);\n\n // Track remote version for correct version chaining\n const remoteVersion = (data as TxfStorageDataBase)?._meta?.version;\n if (typeof remoteVersion === 'number' && remoteVersion > this.dataVersion) {\n this.dataVersion = remoteVersion;\n }\n\n this.emitEvent({\n type: 'storage:loaded',\n timestamp: Date.now(),\n data: { cid: best.cid, sequence: best.sequence.toString() },\n });\n\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n } catch (error) {\n // On network error, try to return cached data\n if (this.ipnsName) {\n const cached = this.cache.getIpnsRecordIgnoreTtl(this.ipnsName);\n if (cached) {\n const content = this.cache.getContent(cached.cid);\n if (content) {\n this.log('Network error, returning stale cache');\n return { success: true, data: content as TData, source: 'cache', timestamp: Date.now() };\n }\n }\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'storage:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return { success: false, error: errorMessage, source: 'remote', timestamp: Date.now() };\n }\n }\n\n // ---------------------------------------------------------------------------\n // Sync (enters serial queue to avoid concurrent IPNS conflicts)\n // ---------------------------------------------------------------------------\n\n async sync(localData: TData): Promise<SyncResult<TData>> {\n return this.flushQueue.enqueue(async () => {\n // Cancel any pending debounced flush (we'll save as part of sync)\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n this.emitEvent({ type: 'sync:started', timestamp: Date.now() });\n\n try {\n // Drain pending buffer — its data will be included via the sync save\n this.pendingBuffer.clear();\n\n // Load remote data\n const remoteResult = await this.load();\n\n if (!remoteResult.success || !remoteResult.data) {\n // No remote data — save local as initial\n this.log('No remote data found, uploading local data');\n const saveResult = await this._doSave(localData);\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n return {\n success: saveResult.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: saveResult.error,\n };\n }\n\n const remoteData = remoteResult.data;\n\n // Check if merge is needed\n const localVersion = localData._meta?.version ?? 0;\n const remoteVersion = remoteData._meta?.version ?? 0;\n\n if (localVersion === remoteVersion && this.lastCid) {\n // Same version — no merge needed\n this.log('Data is in sync (same version)');\n this.emitEvent({ type: 'sync:completed', timestamp: Date.now() });\n return {\n success: true,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n };\n }\n\n // Merge\n this.log(`Merging: local v${localVersion} <-> remote v${remoteVersion}`);\n const { merged, added, removed, conflicts } = mergeTxfData(localData, remoteData);\n\n if (conflicts > 0) {\n this.emitEvent({\n type: 'sync:conflict',\n timestamp: Date.now(),\n data: { conflicts },\n });\n }\n\n // Save merged result\n const saveResult = await this._doSave(merged);\n\n this.emitEvent({\n type: 'sync:completed',\n timestamp: Date.now(),\n data: { added, removed, conflicts },\n });\n\n return {\n success: saveResult.success,\n merged: merged,\n added,\n removed,\n conflicts,\n error: saveResult.error,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emitEvent({\n type: 'sync:error',\n timestamp: Date.now(),\n error: errorMessage,\n });\n return {\n success: false,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: errorMessage,\n };\n }\n });\n }\n\n // ---------------------------------------------------------------------------\n // Private Helpers\n // ---------------------------------------------------------------------------\n\n // ---------------------------------------------------------------------------\n // Optional Methods\n // ---------------------------------------------------------------------------\n\n async exists(): Promise<boolean> {\n if (!this.ipnsName) return false;\n\n // Check cache first\n const cached = this.cache.getIpnsRecord(this.ipnsName);\n if (cached) return true;\n\n // Resolve from network\n const { best } = await this.httpClient.resolveIpns(this.ipnsName);\n return best !== null;\n }\n\n async clear(): Promise<boolean> {\n if (!this.ipnsKeyPair || !this.ipnsName) return false;\n\n // Clear pending buffer\n this.pendingBuffer.clear();\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n const emptyData = {\n _meta: {\n version: 0,\n address: this.identity?.directAddress ?? '',\n ipnsName: this.ipnsName,\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n } as TData;\n\n const result = await this._doSave(emptyData);\n if (result.success) {\n this.cache.clear();\n await this.statePersistence.clear(this.ipnsName);\n }\n return result.success;\n }\n\n onEvent(callback: StorageEventCallback): () => void {\n this.eventCallbacks.add(callback);\n return () => {\n this.eventCallbacks.delete(callback);\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public Accessors\n // ---------------------------------------------------------------------------\n\n getIpnsName(): string | null {\n return this.ipnsName;\n }\n\n getLastCid(): string | null {\n return this.lastCid;\n }\n\n getSequenceNumber(): bigint {\n return this.ipnsSequenceNumber;\n }\n\n getDataVersion(): number {\n return this.dataVersion;\n }\n\n getRemoteCid(): string | null {\n return this.remoteCid;\n }\n\n // ---------------------------------------------------------------------------\n // Testing helper: wait for pending flush to complete\n // ---------------------------------------------------------------------------\n\n /**\n * Wait for the pending flush timer to fire and the flush operation to\n * complete. Useful in tests to await background writes.\n * Returns immediately if no flush is pending.\n */\n async waitForFlush(): Promise<void> {\n if (this.flushTimer) {\n // Force the timer to fire now\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n await this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n logger.warn('IPFS-Storage', 'Flush on shutdown failed:', err);\n });\n } else if (!this.pendingBuffer.isEmpty) {\n // No timer but pending data — flush now\n await this.flushQueue.enqueue(() => this.executeFlush()).catch((err) => {\n logger.warn('IPFS-Storage', 'Flush on shutdown failed:', err);\n });\n } else {\n // Ensure any in-flight flush completes\n await this.flushQueue.enqueue(async () => {});\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal: Push Subscription Helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Derive WebSocket URL from the first configured gateway.\n * Converts https://host → wss://host/ws/ipns\n */\n private deriveWsUrl(): string | null {\n const gateways = this.httpClient.getGateways();\n if (gateways.length === 0) return null;\n\n const gateway = gateways[0];\n const wsProtocol = gateway.startsWith('https://') ? 'wss://' : 'ws://';\n const host = gateway.replace(/^https?:\\/\\//, '');\n return `${wsProtocol}${host}/ws/ipns`;\n }\n\n /**\n * Poll for remote IPNS changes (fallback when WS is unavailable).\n * Compares remote sequence number with last known and emits event if changed.\n */\n private async pollForRemoteChanges(): Promise<void> {\n if (!this.ipnsName) return;\n\n try {\n const { best } = await this.httpClient.resolveIpns(this.ipnsName);\n if (best && best.sequence > this.lastKnownRemoteSequence) {\n this.log(`Poll detected remote change: seq=${best.sequence} (was ${this.lastKnownRemoteSequence})`);\n this.lastKnownRemoteSequence = best.sequence;\n this.emitEvent({\n type: 'storage:remote-updated',\n timestamp: Date.now(),\n data: { name: this.ipnsName, sequence: Number(best.sequence), cid: best.cid },\n });\n }\n } catch {\n // Non-fatal — poll will retry on next interval\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internal\n // ---------------------------------------------------------------------------\n\n private emitEvent(event: StorageEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch {\n // Don't let event handler errors break the provider\n }\n }\n }\n\n private log(message: string): void {\n logger.debug('IPFS-Storage', message);\n }\n\n}\n","/**\n * Browser IPFS State Persistence\n * Uses localStorage for persisting IPFS/IPNS state between sessions\n */\n\nimport type { IpfsStatePersistence, IpfsPersistedState } from '../../shared/ipfs';\n\nconst KEY_PREFIX = 'sphere_ipfs_';\n\nfunction seqKey(ipnsName: string): string {\n return `${KEY_PREFIX}seq_${ipnsName}`;\n}\n\nfunction cidKey(ipnsName: string): string {\n return `${KEY_PREFIX}cid_${ipnsName}`;\n}\n\nfunction verKey(ipnsName: string): string {\n return `${KEY_PREFIX}ver_${ipnsName}`;\n}\n\nexport class BrowserIpfsStatePersistence implements IpfsStatePersistence {\n async load(ipnsName: string): Promise<IpfsPersistedState | null> {\n try {\n const seq = localStorage.getItem(seqKey(ipnsName));\n if (!seq) return null;\n\n return {\n sequenceNumber: seq,\n lastCid: localStorage.getItem(cidKey(ipnsName)),\n version: parseInt(localStorage.getItem(verKey(ipnsName)) ?? '0', 10),\n };\n } catch {\n return null;\n }\n }\n\n async save(ipnsName: string, state: IpfsPersistedState): Promise<void> {\n try {\n localStorage.setItem(seqKey(ipnsName), state.sequenceNumber);\n if (state.lastCid) {\n localStorage.setItem(cidKey(ipnsName), state.lastCid);\n } else {\n localStorage.removeItem(cidKey(ipnsName));\n }\n localStorage.setItem(verKey(ipnsName), String(state.version));\n } catch {\n // localStorage might be full or unavailable\n }\n }\n\n async clear(ipnsName: string): Promise<void> {\n try {\n localStorage.removeItem(seqKey(ipnsName));\n localStorage.removeItem(cidKey(ipnsName));\n localStorage.removeItem(verKey(ipnsName));\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n","/**\n * Browser IPFS Storage Module\n * Factory function for browser-specific IPFS storage provider\n */\n\nimport { IpfsStorageProvider, type IpfsStorageConfig } from '../../shared/ipfs';\nimport { BrowserIpfsStatePersistence } from './browser-ipfs-state-persistence';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export for convenience\nexport { IpfsStorageProvider } from '../../shared/ipfs';\nexport { BrowserIpfsStatePersistence } from './browser-ipfs-state-persistence';\nexport type { IpfsStorageConfig as IpfsStorageProviderConfig } from '../../shared/ipfs';\n\n/**\n * Create a browser WebSocket that conforms to the IWebSocket interface.\n */\nfunction createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create a browser IPFS storage provider with localStorage-based state persistence.\n * Automatically injects the browser WebSocket factory for IPNS push subscriptions.\n */\nexport function createBrowserIpfsStorageProvider(config?: IpfsStorageConfig): IpfsStorageProvider {\n return new IpfsStorageProvider(\n { ...config, createWebSocket: config?.createWebSocket ?? createBrowserWebSocket },\n new BrowserIpfsStatePersistence(),\n );\n}\n\n/** @deprecated Use createBrowserIpfsStorageProvider instead */\nexport const createIpfsStorageProvider = createBrowserIpfsStorageProvider;\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 { 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: AbortSignal.timeout(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 * 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 [cached, cachedTs] = await Promise.all([\n this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE),\n this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS),\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 await Promise.all([\n this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE, JSON.stringify(definitions)),\n this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS, 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 * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, DEFAULT_AGGREGATOR_API_KEY, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n L1Config,\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 ?? DEFAULT_AGGREGATOR_API_KEY,\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// L1 Resolution\n// =============================================================================\n\n/**\n * Resolve L1 configuration with override pattern\n *\n * Only returns config if l1 is explicitly provided (L1 is optional)\n *\n * @example\n * ```ts\n * // No L1 config - returns undefined\n * resolveL1Config('testnet', undefined);\n *\n * // Enable L1 with defaults\n * resolveL1Config('testnet', {});\n *\n * // Override electrum URL\n * resolveL1Config('testnet', { electrumUrl: 'wss://custom.fulcrum:50004' });\n * ```\n */\nexport function resolveL1Config(\n network: NetworkType,\n config?: L1Config\n): L1Config | undefined {\n if (config === undefined) {\n return undefined;\n }\n\n const networkConfig = getNetworkConfig(network);\n\n return {\n electrumUrl: config.electrumUrl ?? networkConfig.electrumUrl,\n defaultFeeRate: config.defaultFeeRate,\n enableVesting: config.enableVesting,\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":["import_buffer","DB_NAME","DB_VERSION","connectionSeq","db","success","message","senderNametag","NostrEventClass","SdkToken","valid","elliptic","hexToBytes","hexToBytes","data","saveResult","createBrowserWebSocket","netConfig"]}
|