@unicitylabs/sphere-sdk 0.9.1-dev.8 → 0.9.1-dev.9

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.
@@ -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","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha2.ts","../../../token-engine/sdk.ts","../../../token-engine/token-blob.ts","../../../storage/whole-blob-inventory-adapter.ts","../../../impl/browser/storage/IndexedDBTokenStorageProvider.ts","../../../transport/NostrTransportProvider.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","../../../core/crypto.ts","../../../wallet-api/errors.ts","../../../wallet-api/challenge.ts","../../../wallet-api/codec.ts","../../../wallet-api/client.ts","../../../impl/shared/wallet-api/WalletApiTokenStorageProvider.ts","../../../transport/delivery-provider.ts","../../../node_modules/@noble/hashes/src/hmac.ts","../../../node_modules/@noble/hashes/src/hkdf.ts","../../../node_modules/@noble/ciphers/src/utils.ts","../../../node_modules/@noble/ciphers/src/_arx.ts","../../../node_modules/@noble/ciphers/src/_poly1305.ts","../../../node_modules/@noble/ciphers/src/chacha.ts","../../../core/field-encryption.ts","../../../impl/shared/wallet-api/WalletApiMailboxProvider.ts","../../../impl/shared/wallet-api/composition.ts","../../../impl/browser/download.ts","../../../impl/shared/network.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';\n// Wallet-api storage provider (platform-neutral, sdk-changes S2)\nexport * from '../shared/wallet-api';\n\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n L1Config,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport { assertNetworkConsistency } from '../shared/network';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig, 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 // Fail loud: a missing network would silently load the wrong-network providers.\n if (!config?.network) {\n throw new SphereError('createBrowserProviders: config.network is required.', 'INVALID_CONFIG');\n }\n const network = config.network;\n // Refuse provably-broken networks (e.g. a null/mismatched trust base would\n // silently accept unverified tokens) before building any provider.\n assertNetworkConsistency(network);\n\n // Configure global logger: top-level debug enables all, per-provider overrides are additive.\n // Only override global debug flag when explicitly provided — don't reset a previously-configured value.\n if (config?.debug !== undefined) {\n sdkLogger.configure({ debug: config.debug });\n }\n if (config?.transport?.debug) sdkLogger.setTagDebug('Nostr', true);\n if (config?.oracle?.debug) sdkLogger.setTagDebug('Aggregator', true);\n if (config?.price?.debug) sdkLogger.setTagDebug('Price', true);\n\n // Resolve configurations using shared utilities\n const transportConfig = resolveTransportConfig(network, config?.transport);\n const oracleConfig = resolveOracleConfig(network, config?.oracle);\n const l1Config = resolveL1Config(network, config?.l1);\n const tokenSyncConfig = resolveTokenSyncConfig(network, config?.tokenSync);\n\n const storage = createIndexedDBStorageProvider({ ...config?.storage, network });\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 network,\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({ network }),\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 // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n | '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 // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n\n 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 /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data (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 /**\n * FINISHED v2 token blobs awaiting transport delivery. Written the moment a\n * transfer/split output is certified on-chain (the source is already spent),\n * removed after successful delivery — survives transport failures + crashes\n * so the recipient's token is never lost with the process.\n */\n PENDING_V2_DELIVERIES: 'pending_v2_deliveries',\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 // Invoice / Accounting storage keys\n /** Set of cancelled invoice IDs (JSON string array) */\n CANCELLED_INVOICES: 'cancelled_invoices',\n /** Set of closed invoice IDs (JSON string array) */\n CLOSED_INVOICES: 'closed_invoices',\n /** Frozen balances for terminated invoices (JSON map: invoiceId → FrozenInvoiceBalances) */\n FROZEN_BALANCES: 'frozen_balances',\n /** Auto-return settings (JSON: AutoReturnSettings) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (JSON: AutoReturnLedger) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Invoice-transfer index metadata (JSON: Record<invoiceId, { terminated, frozenAt? }>) */\n INV_LEDGER_INDEX: 'inv_ledger_index',\n /** Token scan state watermarks (JSON: Record<tokenId, txCount>) */\n TOKEN_SCAN_STATE: 'token_scan_state',\n // Swap storage keys\n /** Per-swap key: swap:{swapId} */\n SWAP_RECORD_PREFIX: 'swap:',\n /** Lightweight index array for listing */\n SWAP_INDEX: 'swap_index',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe (e.g. a testnet2 auto-return ledger firing a real send on mainnet).\n * Chat/identity per-address keys (CONVERSATIONS/MESSAGES/GROUP_CHAT_*) are network-AGNOSTIC and\n * are deliberately NOT listed — they stay per-address only.\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS,\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,\n STORAGE_KEYS_ADDRESS.PENDING_V2_DELIVERIES,\n STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,\n STORAGE_KEYS_ADDRESS.PROCESSED_COMBINED_TRANSFER_IDS,\n STORAGE_KEYS_ADDRESS.CANCELLED_INVOICES,\n STORAGE_KEYS_ADDRESS.CLOSED_INVOICES,\n STORAGE_KEYS_ADDRESS.FROZEN_BALANCES,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n STORAGE_KEYS_ADDRESS.INV_LEDGER_INDEX,\n STORAGE_KEYS_ADDRESS.TOKEN_SCAN_STATE,\n STORAGE_KEYS_ADDRESS.SWAP_INDEX,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/** @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\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/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly ipfsGateways: readonly string[];\n readonly electrumUrl: string;\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n}\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 // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs/electrum)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs/electrum)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n // NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot\n // operate against them until their gateways are cut over to the v2 protocol —\n // wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n// =============================================================================\n// 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 INVOICE_TOKEN_TYPE_HEX = '14676a280bda4275baf865b67cd4c611bcd58c9bf8226d508acaa10a8fcaccc6' as const;\n\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * Browser LocalStorage Provider\n * Implements StorageProvider using browser localStorage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface LocalStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Custom storage instance (for testing/SSR) */\n storage?: Storage;\n /**\n * When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global/seed keys stay shared.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class LocalStorageProvider implements StorageProvider {\n readonly id = 'localStorage';\n readonly name = 'Local Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser localStorage for single-device persistence';\n\n private config: Required<Pick<LocalStorageProviderConfig, 'prefix' | 'debug'>> & {\n storage: Storage;\n };\n private network?: NetworkType;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n\n constructor(config?: LocalStorageProviderConfig) {\n // SSR fallback: use in-memory storage if localStorage unavailable\n const storage = config?.storage ?? this.getStorageSafe();\n\n this.config = {\n prefix: config?.prefix ?? 'sphere_',\n storage,\n debug: config?.debug ?? false,\n };\n this.network = config?.network;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Test storage availability\n const testKey = `${this.config.prefix}_test`;\n this.config.storage.setItem(testKey, 'test');\n this.config.storage.removeItem(testKey);\n\n this.status = 'connected';\n this.log('Connected to localStorage');\n } catch (error) {\n this.status = 'error';\n throw new SphereError(`LocalStorage not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.log('Disconnected from localStorage');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.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 // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n // Add address ID prefix for per-address data\n const id = this.identity.chainPubkey;\n return `${this.config.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.config.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('LocalStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n private getStorageSafe(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n\n // SSR fallback: in-memory storage\n return createInMemoryStorage();\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('LocalStorage', message, ...args);\n }\n}\n\n// =============================================================================\n// In-Memory Storage (SSR Fallback)\n// =============================================================================\n\nfunction createInMemoryStorage(): Storage {\n const data = new Map<string, string>();\n\n return {\n get length() {\n return data.size;\n },\n clear() {\n data.clear();\n },\n getItem(key: string) {\n return data.get(key) ?? null;\n },\n setItem(key: string, value: string) {\n data.set(key, value);\n },\n removeItem(key: string) {\n data.delete(key);\n },\n key(index: number) {\n return Array.from(data.keys())[index] ?? null;\n },\n };\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createLocalStorageProvider(\n config?: LocalStorageProviderConfig\n): LocalStorageProvider {\n return new LocalStorageProvider(config);\n}\n","/**\n * Browser IndexedDB Storage Provider\n * Implements StorageProvider using IndexedDB for large-capacity browser storage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-storage';\nconst DB_VERSION = 1;\nconst STORE_NAME = 'kv';\n\nexport interface IndexedDBStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Database name (default: 'sphere-storage') */\n dbName?: string;\n /**\n * Network. When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global (seed) keys stay shared across networks.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class IndexedDBStorageProvider implements StorageProvider {\n readonly id = 'indexeddb-storage';\n readonly name = 'IndexedDB Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser IndexedDB for large-capacity persistence';\n\n private prefix: string;\n private dbName: string;\n private network?: NetworkType;\n private debug: boolean;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n private db: IDBDatabase | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBStorageProviderConfig) {\n this.prefix = config?.prefix ?? 'sphere_';\n this.dbName = config?.dbName ?? DB_NAME;\n this.network = config?.network;\n this.debug = config?.debug ?? false;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected' && this.db) return;\n\n // Retry once — a pending deleteDatabase() from a prior clear() can\n // block open() until the deletion completes. A short delay is usually\n // enough for the browser to finish the deletion.\n for (let attempt = 0; attempt < 2; attempt++) {\n this.status = 'connecting';\n const t0 = Date.now();\n logger.debug('IndexedDB',` connect: opening db=${this.dbName}, attempt=${attempt + 1}/2`);\n\n try {\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('IndexedDB open timed out after 5s')), 5000),\n ),\n ]);\n this.status = 'connected';\n logger.debug('IndexedDB',` connect: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return;\n } catch (error) {\n logger.warn('IndexedDB',` connect: open failed db=${this.dbName} attempt=${attempt + 1} (${Date.now() - t0}ms):`, error);\n if (attempt === 0) {\n this.status = 'disconnected';\n await new Promise((r) => setTimeout(r, 1000));\n continue;\n }\n this.status = 'error';\n throw new SphereError(`IndexedDB not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n }\n\n async disconnect(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDB',` disconnect: db=${this.dbName} connId=${cid} wasConnected=${!!this.db}`);\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.db !== null;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.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 const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n const id = this.identity.chainPubkey;\n return `${this.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected' || !this.db) {\n throw new SphereError('IndexedDBStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n // ===========================================================================\n // Private: IndexedDB Operations\n // ===========================================================================\n\n private openDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n\n request.onsuccess = () => {\n const db = request.result;\n const cid = ++connectionSeq;\n this.connId = cid;\n\n // Auto-close when another context requests version change or deletion.\n // Prevents leaked connections (e.g. React StrictMode double-mount)\n // from blocking deleteDatabase() or version upgrades.\n db.onversionchange = () => {\n logger.debug('IndexedDB',` onversionchange: auto-closing db=${this.dbName} connId=${cid}`);\n db.close();\n if (this.db === db) {\n this.db = null;\n this.status = 'disconnected';\n }\n };\n resolve(db);\n };\n\n // onblocked fires when another connection (e.g. other tab) holds\n // the database at a lower version. Log it — onsuccess will follow\n // once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDB',` open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'k' });\n }\n };\n });\n }\n\n private idbGet(key: string): Promise<{ k: string; v: string } | undefined> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? undefined);\n });\n }\n\n private idbPut(entry: { k: string; v: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.put(entry);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbDelete(key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.delete(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbCount(key: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.count(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n });\n }\n\n private idbGetAll(): Promise<{ k: string; v: string }[]> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.getAll();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private idbClear(): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.clear();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('IndexedDB', message, ...args);\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createIndexedDBStorageProvider(\n config?: IndexedDBStorageProviderConfig\n): IndexedDBStorageProvider {\n return new IndexedDBStorageProvider(config);\n}\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 §5.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 §6.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n","/**\n * token-engine/sdk.ts — the SINGLE place that imports @unicitylabs/state-transition-sdk.\n *\n * The v1 cut-over is done: the canonical package name now resolves to the v2 SDK\n * (the migration-era `state-transition-sdk-v2` npm alias is gone, and v1 with it).\n *\n * Everything else in token-engine/ imports SDK symbols from `./sdk`, never from the package directly.\n * ESLint `no-restricted-imports` forbids importing the SDK anywhere except this file.\n *\n * Import path note: subpaths are `…/lib/<path>.js` (no `/src/`); no root barrel.\n */\n\n// ── client / aggregator / proof / network / trust base ──────────────────────\nexport { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient.js';\nexport { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/AggregatorClient.js';\nexport type { IAggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/IAggregatorClient.js';\nexport { NetworkId } from '@unicitylabs/state-transition-sdk/lib/api/NetworkId.js';\nexport { CertificationData } from '@unicitylabs/state-transition-sdk/lib/api/CertificationData.js';\nexport { CertificationResponse, CertificationStatus } from '@unicitylabs/state-transition-sdk/lib/api/CertificationResponse.js';\nexport { StateId } from '@unicitylabs/state-transition-sdk/lib/api/StateId.js';\nexport { InclusionProof } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProof.js';\nexport { InclusionProofResponse } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProofResponse.js';\nexport { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/api/bft/RootTrustBase.js';\nexport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js';\nexport { InclusionProofVerificationStatus } from '@unicitylabs/state-transition-sdk/lib/transaction/verification/rule/InclusionProofVerificationRule.js';\n\n// ── token / transactions ────────────────────────────────────────────────────\nexport { Token } from '@unicitylabs/state-transition-sdk/lib/transaction/Token.js';\nexport { MintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransaction.js';\nexport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction.js';\nexport { CertifiedMintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedMintTransaction.js';\nexport { CertifiedTransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedTransferTransaction.js';\nexport { TokenId } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenId.js';\nexport { TokenType } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenType.js';\nexport { TokenSalt } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenSalt.js';\nexport type { ITransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/ITransaction.js';\nexport { MintJustificationVerifierService } from '@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js';\n\n// ── predicates / unlock scripts ─────────────────────────────────────────────\nexport type { IPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/IPredicate.js';\nexport type { IUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/IUnlockScript.js';\nexport { EncodedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js';\nexport { SignaturePredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js';\nexport { SignaturePredicateUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicateUnlockScript.js';\nexport { BurnPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/BurnPredicate.js';\nexport { PredicateVerifierService } from '@unicitylabs/state-transition-sdk/lib/predicate/verification/PredicateVerifierService.js';\n\n// ── crypto / hashing ────────────────────────────────────────────────────────\nexport { SigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/SigningService.js';\nexport { Signature } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Signature.js';\nexport { MintSigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/MintSigningService.js';\nexport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/HashAlgorithm.js';\nexport { DataHash } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHash.js';\nexport { DataHasher } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasher.js';\nexport { DataHasherFactory } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasherFactory.js';\n\n// ── CBOR serialization ──────────────────────────────────────────────────────\nexport { CborSerializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborSerializer.js';\nexport { CborDeserializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborDeserializer.js';\nexport { CborError } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborError.js';\n\n// ── payment / value / split ─────────────────────────────────────────────────\nexport type { IPaymentData } from '@unicitylabs/state-transition-sdk/lib/payment/IPaymentData.js';\nexport { Asset } from '@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js';\nexport { AssetId } from '@unicitylabs/state-transition-sdk/lib/payment/asset/AssetId.js';\nexport { PaymentAssetCollection } from '@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js';\nexport { TokenSplit } from '@unicitylabs/state-transition-sdk/lib/payment/TokenSplit.js';\nexport { SplitTokenRequest } from '@unicitylabs/state-transition-sdk/lib/payment/SplitTokenRequest.js';\nexport { SplitToken } from '@unicitylabs/state-transition-sdk/lib/payment/SplitToken.js';\nexport { SplitAssetProof } from '@unicitylabs/state-transition-sdk/lib/payment/SplitAssetProof.js';\nexport { SplitMintJustification } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustification.js';\nexport { SplitMintJustificationVerifier } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustificationVerifier.js';\n\n// ── verification result types ───────────────────────────────────────────────\nexport { VerificationStatus } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationStatus.js';\nexport { VerificationResult } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationResult.js';\n\n// ── unicity-id (nametag) ────────────────────────────────────────────────────\nexport { UnicityId } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityId.js';\nexport { UnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdMintTransaction.js';\nexport { CertifiedUnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/CertifiedUnicityIdMintTransaction.js';\nexport { UnicityIdToken } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdToken.js';\n\n// ── util ────────────────────────────────────────────────────────────────────\nexport { HexConverter } from '@unicitylabs/state-transition-sdk/lib/util/HexConverter.js';\nexport { BigintConverter } from '@unicitylabs/state-transition-sdk/lib/util/BigintConverter.js';\nexport { BitString } from '@unicitylabs/state-transition-sdk/lib/util/BitString.js';\n","/**\n * token-engine/token-blob.ts — storage/transport codec for a wallet token.\n *\n * A {@link TokenBlob} is `{ v, network, tokenId, token }` where `token` is the v2\n * `Token.toCBOR()` bytes and `tokenId` is the genesis-stable id (stored so dedup /\n * listing / tombstone keys need no engine call). This codec wraps it in a\n * sphere-private CBOR envelope so the wallet's own storage format can version\n * independently of the SDK's token CBOR. The decoded value is re-derivable from\n * `token`, so it is NOT stored here.\n */\n\nimport { CborDeserializer, CborError, CborSerializer } from './sdk';\nimport type { TokenBlob } from './types';\n\n/** Sphere-private CBOR tag (distinct from SpherePaymentData's 39050). */\nconst TOKEN_BLOB_TAG = 39051n;\n/** Current blob envelope version. */\nexport const TOKEN_BLOB_VERSION = 1;\n\n/** Encode a blob as `tag(39051)[ v, network, tokenId, token ]`. */\nexport function encodeTokenBlob(blob: TokenBlob): Uint8Array {\n return CborSerializer.encodeTag(\n TOKEN_BLOB_TAG,\n CborSerializer.encodeArray(\n CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),\n CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),\n CborSerializer.encodeTextString(blob.tokenId),\n CborSerializer.encodeByteString(blob.token),\n ),\n );\n}\n\n/** Decode a blob produced by {@link encodeTokenBlob}. */\nexport function decodeTokenBlob(bytes: Uint8Array): TokenBlob {\n const tag = CborDeserializer.decodeTag(bytes);\n if (tag.tag !== TOKEN_BLOB_TAG) {\n throw new CborError(`Invalid TokenBlob tag: ${tag.tag}`);\n }\n const fields = CborDeserializer.decodeArray(tag.data, 4);\n const v = Number(CborDeserializer.decodeUnsignedInteger(fields[0]));\n if (v !== TOKEN_BLOB_VERSION) {\n throw new CborError(`Unsupported TokenBlob version: ${v}`);\n }\n return {\n v,\n network: Number(CborDeserializer.decodeUnsignedInteger(fields[1])),\n tokenId: CborDeserializer.decodeTextString(fields[2]),\n token: CborDeserializer.decodeByteString(fields[3]),\n };\n}\n\n/**\n * The wallet-api WIRE bytes for a blob (ARCHITECTURE §5.2/§8.2): the backend\n * stores, content-addresses and validates the INNER `token` bytes (the v2\n * `Token.toCBOR()` form — `Token.fromCBOR` at §8.2 step 3). The sphere-private\n * 39051 envelope never crosses the §16 API. Tolerant on input: envelope bytes\n * unwrap to their inner `token` bytes; anything else is already wire format\n * and passes through. (The phase-2 harness caught the envelope leaking onto\n * the wire — the real backend 422s it at §8.2 step 3 with \"Invalid CBOR tag\n * for Token: 39051\"; only the test fake's matching wrongness had masked it.)\n */\nexport function unwrapTokenBlobBytes(bytes: Uint8Array): Uint8Array {\n try {\n return decodeTokenBlob(bytes).token;\n } catch {\n return bytes;\n }\n}\n","/**\n * storage/whole-blob-inventory-adapter.ts — default lazy-port adapter for\n * whole-blob providers (sdk-changes S2).\n *\n * The lazy inventory port (`listInventory` / `getToken` / `applyDelta`) is the\n * wallet's hot path against wallet-api, but the port extension must keep every\n * EXISTING whole-blob provider conformant — swappability is a covenant\n * (sdk-changes S7), not an aspiration. This adapter derives the three methods\n * from the provider's own `load()`/`save()`:\n *\n * - `listInventory()` — the view computed from the loaded set: every stored\n * token is `'active'`, with synthetic `seq`/`cursor`, `more: false`, and\n * `syncEpoch: 0n` (a local store has no server restore to signal). There is\n * no change journal, so `since` is ignored and the full active snapshot is\n * returned — a degenerate but coherent cursor model.\n * - `getToken()` — decoded from the loaded set (v2 blob entries only).\n * - `applyDelta()` — tombstones `spent` entries (the provider's own tombstone\n * handling deletes the stored blobs) and asserts `added` outputs are already\n * present: a whole-blob provider cannot materialize bytes from a blob-store\n * key — the engine path `save()`s its outputs first. Naturally idempotent:\n * an already-tombstoned `spent` row and an already-present (or since-moved)\n * `added` row are success, mirroring ARCHITECTURE §5.3 step 4.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { decodeTokenBlob } from '../token-engine/token-blob';\nimport type { TokenBlob } from '../token-engine/types';\nimport { SphereError } from '../core/errors';\nimport type {\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n InventoryItem,\n InventoryView,\n LoadResult,\n SaveResult,\n TxfStorageDataBase,\n TxfTombstone,\n} from './storage-provider';\n\n/** The slice of a whole-blob provider the adapter needs. */\nexport interface WholeBlobStore<TData extends TxfStorageDataBase = TxfStorageDataBase> {\n load(identifier?: string): Promise<LoadResult<TData>>;\n save(data: TData): Promise<SaveResult>;\n}\n\n/** A stored token entry that carries a v2 blob (the UI token record). */\ninterface V2TokenEntry {\n sdkData: string;\n coinId?: string;\n amount?: string;\n}\n\nconst TOKEN_KEY_RE = /^_[0-9a-f]{4,}$/i;\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\n/** A v2 engine blob is even-length hex (CBOR); legacy v1 TXF is JSON. */\nfunction isV2BlobHex(sdkData: unknown): sdkData is string {\n return (\n typeof sdkData === 'string' &&\n sdkData.length >= 2 &&\n sdkData.length % 2 === 0 &&\n /^[0-9a-f]+$/i.test(sdkData)\n );\n}\n\nfunction isV2TokenEntry(entry: unknown): entry is V2TokenEntry {\n return (\n typeof entry === 'object' &&\n entry !== null &&\n !('genesis' in entry) &&\n isV2BlobHex((entry as V2TokenEntry).sdkData)\n );\n}\n\n/** Stored token entries keyed by genesis tokenId (the `_<id>` key minus `_`). */\nfunction collectTokenEntries(data: TxfStorageDataBase): Map<string, unknown> {\n const out = new Map<string, unknown>();\n for (const [key, value] of Object.entries(data)) {\n if (!TOKEN_KEY_RE.test(key)) continue;\n if (value === null || typeof value !== 'object') continue;\n out.set(key.slice(1), value);\n }\n return out;\n}\n\nfunction entryAssets(entry: unknown): InventoryItem['assets'] {\n if (!isV2TokenEntry(entry)) return undefined;\n const { coinId, amount } = entry;\n if (typeof coinId !== 'string' || typeof amount !== 'string' || !/^[0-9]+$/.test(amount)) {\n return undefined;\n }\n return [{ coinId, amount: BigInt(amount) }];\n}\n\n/**\n * Default-adapter implementation of the S2 lazy port over a whole-blob store.\n * Providers instantiate one lazily and delegate their `listInventory` /\n * `getToken` / `applyDelta` to it (minimal edits, shared semantics).\n */\nexport class WholeBlobInventoryAdapter<TData extends TxfStorageDataBase = TxfStorageDataBase> {\n constructor(private readonly store: WholeBlobStore<TData>) {}\n\n private async loadData(): Promise<TData> {\n const result = await this.store.load();\n if (!result.success || !result.data) {\n throw new SphereError(\n `Whole-blob inventory adapter: load() failed${result.error ? `: ${result.error}` : ''}`,\n 'STORAGE_ERROR'\n );\n }\n return result.data;\n }\n\n /** View computed from the loaded set: all `'active'`, synthetic seq/cursor, `more: false`. */\n async listInventory(_since?: bigint): Promise<InventoryView> {\n const data = await this.loadData();\n const entries = [...collectTokenEntries(data).entries()].sort(([a], [b]) => (a < b ? -1 : 1));\n const items: InventoryItem[] = entries.map(([tokenId, entry], index) => ({\n tokenId,\n status: 'active' as const,\n assets: entryAssets(entry),\n seq: BigInt(index + 1),\n }));\n return { cursor: BigInt(items.length), syncEpoch: 0n, more: false, items };\n }\n\n /** Serve a token's decoded blob from the loaded set (v2 blob entries only). */\n async getToken(tokenId: string): Promise<TokenBlob> {\n const data = await this.loadData();\n const entry = collectTokenEntries(data).get(tokenId);\n if (entry === undefined) {\n throw new SphereError(`Token ${tokenId} not found in storage`, 'STORAGE_ERROR');\n }\n if (!isV2TokenEntry(entry)) {\n throw new SphereError(`Token ${tokenId} has no v2 blob (legacy TXF entry)`, 'STORAGE_ERROR');\n }\n const blob = decodeTokenBlob(hexToBytes(entry.sdkData));\n if (blob.tokenId !== tokenId) {\n throw new SphereError(\n `Stored blob tokenId mismatch for ${tokenId} (blob carries ${blob.tokenId})`,\n 'STORAGE_ERROR'\n );\n }\n return blob;\n }\n\n /**\n * Tombstone `spent`, assert `added` present. Mirrors the §5.3 apply\n * semantics a local store can honor; idempotent by construction (replays\n * find spent already tombstoned / added already present → success).\n */\n async applyDelta(\n _transferId: string,\n spent: string[],\n added: ApplyDeltaAdded[],\n _opts?: ApplyDeltaOptions\n ): Promise<void> {\n const addedIds = new Set(added.map((a) => a.tokenId));\n for (const tokenId of spent) {\n if (addedIds.has(tokenId)) {\n // Mirrors the server's 422 (ARCHITECTURE §5.3): a self-send routes\n // through the mailbox, never through one apply.\n throw new SphereError(\n `applyDelta: token ${tokenId} appears in both spent and added`,\n 'VALIDATION_ERROR'\n );\n }\n }\n\n const data = await this.loadData();\n const entries = collectTokenEntries(data);\n const tombstoned = new Set((data._tombstones ?? []).map((t) => t.tokenId));\n\n for (const { tokenId } of added) {\n // A whole-blob store cannot fetch bytes by blob-store key; the engine\n // path save()s outputs before recording the spend. Present (or already\n // moved on to a tombstone) is success; never-seen is a caller bug.\n if (!entries.has(tokenId) && !tombstoned.has(tokenId)) {\n throw new SphereError(\n `applyDelta: added token ${tokenId} is not in storage — save() engine outputs before applyDelta`,\n 'STORAGE_ERROR'\n );\n }\n }\n\n const newTombstones: TxfTombstone[] = [];\n for (const tokenId of spent) {\n const entry = entries.get(tokenId);\n if (entry === undefined) continue; // already tombstoned/unknown → success (§5.3 step 4)\n let stateHash = '';\n if (isV2TokenEntry(entry)) {\n try {\n const blob = decodeTokenBlob(hexToBytes(entry.sdkData));\n stateHash = bytesToHex(sha256(blob.token));\n } catch {\n // Tombstone without a state hash is still a valid removal record.\n }\n }\n newTombstones.push({ tokenId, stateHash, timestamp: Date.now() });\n delete data[`_${tokenId}`];\n }\n\n if (newTombstones.length === 0) return; // pure replay — nothing to write\n\n data._tombstones = [...(data._tombstones ?? []), ...newTombstones];\n const result = await this.store.save(data);\n if (!result.success) {\n throw new SphereError(\n `Whole-blob inventory adapter: save() failed${result.error ? `: ${result.error}` : ''}`,\n 'STORAGE_ERROR'\n );\n }\n }\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, InventoryView, ApplyDeltaAdded, ApplyDeltaOptions } from '../../../storage';\nimport { WholeBlobInventoryAdapter } from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport type { NetworkType } 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 /**\n * Network this store belongs to. Baked into the DB name so testnet/testnet2/mainnet\n * (and v1/v2) never share a token DB for the same wallet identity.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class 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 const base = config?.dbNamePrefix ?? DB_NAME;\n // Bake the network into the prefix so the DB name AND clear()/findPrefixedDatabases\n // (which filter by prefix) are per-network — clear() never wipes another network.\n this.dbNamePrefix = config?.network ? `${base}-${config.network}` : base;\n this.dbName = this.dbNamePrefix;\n this.debug = config?.debug ?? false;\n }\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n // Scope the DB by chainPubkey: always present, and NOT truncated like getAddressId's\n // DIRECT_first6_last6 — so two different wallets can never collide into one DB.\n // Network is already in dbNamePrefix → the DB is per-network + per-identity.\n this.dbName = `${this.dbNamePrefix}-${identity.chainPubkey}`;\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 // ── Lazy inventory port (sdk-changes S2) — default whole-blob adapter ──────\n\n /** Default lazy-port adapter over this store (sdk-changes S2). */\n private readonly inventoryAdapter = new WholeBlobInventoryAdapter<TxfStorageDataBase>(this);\n\n listInventory(since?: bigint): Promise<InventoryView> {\n return this.inventoryAdapter.listInventory(since);\n }\n\n getToken(tokenId: string): Promise<TokenBlob> {\n return this.inventoryAdapter.getToken(tokenId);\n }\n\n applyDelta(transferId: string, spent: string[], added: ApplyDeltaAdded[], opts?: ApplyDeltaOptions): Promise<void> {\n return this.inventoryAdapter.applyDelta(transferId, spent, added, opts);\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 * Create an independent instance for a different address.\n * The new instance shares the same config but has its own IDB connection.\n */\n createForAddress(): IndexedDBTokenStorageProvider {\n return new IndexedDBTokenStorageProvider({\n dbNamePrefix: this.dbNamePrefix,\n debug: this.debug,\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 /** In-memory max DM (gift-wrap) event timestamp. */\n private lastDmEventTs: number = 0;\n /** Fallback 'since' timestamp for first-time address subscriptions (consumed once). */\n private fallbackSince: number | null = null;\n /** Fallback 'since' timestamp for DM (gift-wrap) subscriptions (consumed once). */\n private fallbackDmSince: number | null = null;\n private identity: FullIdentity | null = null;\n private keyManager: NostrKeyManager | null = null;\n private status: ProviderStatus = 'disconnected';\n\n // NostrClient from nostr-js-sdk handles all WebSocket management,\n // keepalive pings, reconnection, and NIP-42 authentication\n private nostrClient: NostrClient | null = null;\n private mainSubscriptionId: string | null = null;\n\n // Event handlers\n private processedEventIds = new Set<string>();\n private messageHandlers: Set<MessageHandler> = new Set();\n private 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 * Get the WebSocket factory (used by MultiAddressTransportMux to share the same factory).\n */\n getWebSocketFactory(): WebSocketFactory {\n return this.config.createWebSocket;\n }\n\n /**\n * Get the configured relay URLs.\n */\n getConfiguredRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get the storage adapter.\n */\n getStorageAdapter(): TransportStorageAdapter | null {\n return this.storage;\n }\n\n /**\n * Get the underlying NostrClient (or null if not yet connected).\n *\n * Exposed so {@link MultiAddressTransportMux} can share the same\n * client/socket pair instead of opening a duplicate WebSocket per\n * relay (#123). The transport owns the client's lifecycle — callers\n * MUST NOT call {@code disconnect()} on the returned instance.\n */\n getNostrClient(): NostrClient | null {\n return this.nostrClient;\n }\n\n /**\n * Suppress event subscriptions — unsubscribe wallet/chat filters\n * but keep the connection alive for resolve/identity-binding operations.\n * Used when MultiAddressTransportMux takes over event handling.\n */\n suppressSubscriptions(): void {\n if (!this.nostrClient) return;\n\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Prevent re-subscription on reconnect by marking subscriptions as suppressed\n this._subscriptionsSuppressed = true;\n logger.debug('Nostr', 'Subscriptions suppressed — mux handles event routing');\n }\n\n // Flag to prevent re-subscription after suppressSubscriptions()\n private _subscriptionsSuppressed = false;\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Ensure keyManager exists for NostrClient\n if (!this.keyManager) {\n // Create a temporary key manager - will be replaced when setIdentity is called\n const tempKey = Buffer.alloc(32);\n crypto.getRandomValues(tempKey);\n this.keyManager = NostrKeyManager.fromPrivateKey(tempKey);\n }\n\n // Create NostrClient with robust connection handling:\n // - autoReconnect: automatic reconnection with exponential backoff\n // - pingIntervalMs: keepalive pings to detect stale connections\n // - NIP-42 AUTH handling built-in\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16, // exponential backoff cap\n pingIntervalMs: 15000, // 15 second keepalive pings (more aggressive to prevent drops)\n // Bump query timeout from the SDK default of 5s to 20s.\n // Real-world testnet observation (2026-05-01): under transient\n // relay overload, kind:30078 (nametag binding) queries take 5-7s\n // to return EVENT messages, even though the binding is on the\n // relay. The default 5s timeout fires before the event arrives,\n // resolveNametag returns null, and downstream sendDM throws\n // INVALID_RECIPIENT — causing every np.propose_deal to fail. 20s\n // gives the slow path enough headroom to complete while still\n // bailing reasonably fast on a truly broken relay.\n queryTimeoutMs: 20000,\n });\n\n // Add connection event listener for logging\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n this.emitEvent({ type: 'transport:reconnecting', timestamp: Date.now() });\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n // Re-establish subscriptions — the relay drops them on disconnect.\n this.subscribeToEvents().catch((err) => {\n logger.error('Nostr', 'Failed to re-subscribe after reconnect:', err);\n });\n },\n });\n\n // Connect to all relays (with timeout to prevent indefinite hang)\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport connection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n\n // Need at least one successful connection\n if (!this.nostrClient.isConnected()) {\n throw new SphereError('Failed to connect to any relay', 'TRANSPORT_ERROR');\n }\n\n this.status = 'connected';\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n logger.debug('Nostr', 'Connected to', this.nostrClient.getConnectedRelays().size, 'relays');\n\n // Set up subscriptions\n if (this.identity) {\n await this.subscribeToEvents();\n }\n } catch (error) {\n this.status = 'error';\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (this.nostrClient) {\n this.nostrClient.disconnect();\n this.nostrClient = null;\n }\n this.mainSubscriptionId = null;\n this.walletSubscriptionId = null;\n this.chatSubscriptionId = null;\n this.chatEoseFired = false;\n this.status = 'disconnected';\n this.emitEvent({ type: 'transport:disconnected', timestamp: Date.now() });\n logger.debug('Nostr', 'Disconnected from all relays');\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.nostrClient?.isConnected() === true;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // Dynamic Relay Management\n // ===========================================================================\n\n /**\n * Get list of configured relay URLs\n */\n getRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get list of currently connected relay URLs\n */\n getConnectedRelays(): string[] {\n if (!this.nostrClient) return [];\n return Array.from(this.nostrClient.getConnectedRelays());\n }\n\n /**\n * Add a new relay dynamically\n * Will connect immediately if provider is already connected\n */\n async addRelay(relayUrl: string): Promise<boolean> {\n // Check if already configured\n if (this.config.relays.includes(relayUrl)) {\n logger.debug('Nostr', 'Relay already configured:', relayUrl);\n return false;\n }\n\n // Add to config\n this.config.relays.push(relayUrl);\n\n // Connect if provider is connected\n if (this.status === 'connected' && this.nostrClient) {\n try {\n await this.nostrClient.connect(relayUrl);\n logger.debug('Nostr', 'Added and connected to relay:', relayUrl);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: true },\n });\n return true;\n } catch (error) {\n logger.debug('Nostr', 'Failed to connect to new relay:', relayUrl, error);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false, error: String(error) },\n });\n return false;\n }\n }\n\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false },\n });\n return true;\n }\n\n /**\n * Remove a relay dynamically\n * Will disconnect from the relay if connected\n * NOTE: NostrClient doesn't support removing individual relays at runtime.\n * We remove from config so it won't be used on next connect().\n */\n async removeRelay(relayUrl: string): Promise<boolean> {\n const index = this.config.relays.indexOf(relayUrl);\n if (index === -1) {\n logger.debug('Nostr', 'Relay not found:', relayUrl);\n return false;\n }\n\n // Remove from config\n this.config.relays.splice(index, 1);\n logger.debug('Nostr', 'Removed relay from config:', relayUrl);\n\n this.emitEvent({\n type: 'transport:relay_removed',\n timestamp: Date.now(),\n data: { relay: relayUrl },\n });\n\n // Check if we still have connections\n if (this.nostrClient && !this.nostrClient.isConnected() && this.status === 'connected') {\n this.status = 'error';\n this.emitEvent({\n type: 'transport:error',\n timestamp: Date.now(),\n data: { error: 'No connected relays remaining' },\n });\n }\n\n return true;\n }\n\n /**\n * Check if a relay is configured\n */\n hasRelay(relayUrl: string): boolean {\n return this.config.relays.includes(relayUrl);\n }\n\n /**\n * Check if a relay is currently connected\n */\n isRelayConnected(relayUrl: string): boolean {\n if (!this.nostrClient) return false;\n return this.nostrClient.getConnectedRelays().has(relayUrl);\n }\n\n // ===========================================================================\n // TransportProvider Implementation\n // ===========================================================================\n\n async setIdentity(identity: FullIdentity): Promise<void> {\n this.identity = identity;\n\n // Clear per-address state so stale dedup entries from previous address\n // don't block legitimate events for the new address.\n this.processedEventIds.clear();\n this.lastEventTs = 0;\n this.lastDmEventTs = 0;\n this.fallbackDmSince = null;\n\n // Create NostrKeyManager from private key\n const secretKey = Buffer.from(identity.privateKey, 'hex');\n this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);\n\n // Use Nostr-format pubkey (32 bytes / 64 hex chars) from keyManager\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Identity set, Nostr pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // If we already have a NostrClient with a temp key, we need to reconnect with the real key\n // NostrClient doesn't support changing key at runtime\n if (this.nostrClient && this.status === 'connected') {\n logger.debug('Nostr', 'Identity changed while connected - recreating NostrClient');\n const oldClient = this.nostrClient;\n\n // Create new client with real identity\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16,\n pingIntervalMs: 15000, // 15 second keepalive pings\n queryTimeoutMs: 20000, // see same option above for rationale\n });\n\n // Add connection event listener\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n },\n });\n\n // Connect with new identity, set up subscriptions, then disconnect old client\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport reconnection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n await this.subscribeToEvents();\n oldClient.disconnect();\n } else if (this.isConnected()) {\n // Already connected with right key, just subscribe\n await this.subscribeToEvents();\n }\n }\n\n setFallbackSince(sinceSeconds: number): void {\n this.fallbackSince = sinceSeconds;\n }\n\n setFallbackDmSince(sinceSeconds: number): void {\n this.fallbackDmSince = sinceSeconds;\n }\n\n /**\n * Get the Nostr-format public key (32 bytes / 64 hex chars)\n * This is the x-coordinate only, without the 02/03 prefix.\n */\n getNostrPubkey(): string {\n if (!this.keyManager) {\n throw new SphereError('KeyManager not initialized - call setIdentity first', 'NOT_INITIALIZED');\n }\n return this.keyManager.getPublicKeyHex();\n }\n\n async sendMessage(recipientPubkey: string, content: string): Promise<string> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Wrap content with sender nametag for Sphere app compatibility\n const senderNametag = this.identity?.nametag;\n const wrappedContent = senderNametag\n ? JSON.stringify({ senderNametag, text: content })\n : content;\n\n // Create NIP-17 gift-wrapped message (kind 1059) for recipient\n const giftWrap = NIP17.createGiftWrap(this.keyManager!, nostrRecipient, wrappedContent);\n\n await this.publishWithVerification(giftWrap, 3, 'dm');\n\n // NIP-17 self-wrap: send a copy to ourselves so relay can replay sent messages.\n // Content includes recipientPubkey and originalId for dedup against the live-sent record.\n const selfWrapContent = JSON.stringify({\n selfWrap: true,\n originalId: giftWrap.id,\n recipientPubkey,\n senderNametag,\n text: content,\n });\n const selfPubkey = this.keyManager!.getPublicKeyHex();\n const selfGiftWrap = NIP17.createGiftWrap(this.keyManager!, selfPubkey, selfWrapContent);\n this.publishEvent(selfGiftWrap).catch(err => {\n logger.debug('Nostr', 'Self-wrap publish failed:', err);\n });\n\n this.emitEvent({\n type: 'message:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return giftWrap.id;\n }\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.add(handler);\n\n // Flush any messages that arrived before this handler was registered\n if (this.pendingMessages.length > 0) {\n const pending = this.pendingMessages;\n this.pendingMessages = [];\n logger.debug('Nostr', 'Flushing', pending.length, 'buffered messages to new handler');\n for (const message of pending) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error (buffered):', error);\n }\n }\n }\n\n return () => this.messageHandlers.delete(handler);\n }\n\n 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.publishWithVerification(event, 3, 'token_transfer');\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 onChatReady(handler: () => void): () => void {\n // If EOSE already fired, invoke immediately\n if (this.chatEoseFired) {\n try { handler(); } catch { /* ignore */ }\n return () => {};\n }\n this.chatEoseHandlers.push(handler);\n return () => {\n this.chatEoseHandlers = this.chatEoseHandlers.filter(h => h !== handler);\n };\n }\n\n // ===========================================================================\n // Composing Indicators (NIP-59 kind 25050)\n // ===========================================================================\n\n onComposing(handler: ComposingHandler): () => void {\n this.composingHandlers.add(handler);\n return () => this.composingHandlers.delete(handler);\n }\n\n async sendComposingIndicator(recipientPubkey: string, content: string): Promise<void> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Build NIP-17 gift wrap with kind 25050 rumor (instead of kind 14 for DMs).\n // We replicate the three-layer NIP-59 envelope because NIP17.createGiftWrap\n // hardcodes kind 14 for the inner rumor.\n const giftWrap = this.createCustomKindGiftWrap(nostrRecipient, content, COMPOSING_INDICATOR_KIND);\n await this.publishEvent(giftWrap);\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Routes to the appropriate specific resolve method based on identifier format.\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n // @nametag\n if (identifier.startsWith('@')) {\n return this.resolveNametagInfo(identifier.slice(1));\n }\n\n // DIRECT:// 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 await this.ensureConnectedForResolve();\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 await this.ensureConnectedForResolve();\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 await this.ensureConnectedForResolve();\n\n const binding = await this.nostrClient!.queryBindingByAddress(address);\n if (!binding) return null;\n\n return this.bindingInfoToPeerInfo(binding);\n }\n\n /**\n * Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).\n */\n private async bindingInfoToPeerInfo(binding: BindingInfo, nametag?: string): Promise<PeerInfo> {\n return {\n nametag: nametag || binding.nametag,\n transportPubkey: binding.transportPubkey,\n chainPubkey: binding.publicKey || '',\n l1Address: binding.l1Address || '',\n directAddress: binding.directAddress || '',\n timestamp: binding.timestamp,\n };\n }\n\n /**\n * Resolve transport pubkey (Nostr pubkey) to full peer info.\n * Queries binding events authored by the given pubkey.\n */\n async resolveTransportPubkeyInfo(transportPubkey: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [transportPubkey],\n limit: 5,\n });\n\n if (events.length === 0) return null;\n\n // Sort by timestamp descending and take the most recent\n events.sort((a, b) => b.created_at - a.created_at);\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n\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 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 await this.ensureConnectedForResolve();\n\n if (transportPubkeys.length === 0) return [];\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: transportPubkeys,\n limit: transportPubkeys.length * 2,\n });\n\n if (events.length === 0) return [];\n\n // Group by author, take most recent per author\n const byAuthor = new Map<string, NostrEvent>();\n for (const event of events) {\n const existing = byAuthor.get(event.pubkey);\n if (!existing || event.created_at > existing.created_at) {\n byAuthor.set(event.pubkey, event);\n }\n }\n\n const results: PeerInfo[] = [];\n for (const [pubkey, event] of byAuthor) {\n try {\n const content = JSON.parse(event.content);\n 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 timestamp: event.created_at * 1000,\n });\n } catch {\n // Skip unparseable events\n }\n }\n\n return results;\n }\n\n /**\n * Recover nametag for the current identity by searching for encrypted nametag events\n * Used after wallet import to recover associated nametag\n * @returns Decrypted nametag or null if none found\n */\n async recoverNametag(): Promise<string | null> {\n await this.ensureConnectedForResolve();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n if (!this.identity || !this.keyManager) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n logger.debug('Nostr', 'Searching for nametag events for pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // Query for nametag binding events authored by this pubkey\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [nostrPubkey],\n limit: 10, // Get recent events in case of updates\n });\n\n if (events.length === 0) {\n logger.debug('Nostr', 'No nametag events found for this pubkey');\n return null;\n }\n\n // Sort by timestamp descending to get most recent\n events.sort((a, b) => b.created_at - a.created_at);\n\n // Try to decrypt nametag from events\n for (const event of events) {\n try {\n const content = JSON.parse(event.content);\n if (content.encrypted_nametag) {\n const decrypted = await decryptNametag(\n content.encrypted_nametag,\n this.identity.privateKey\n );\n if (decrypted) {\n logger.debug('Nostr', 'Recovered Unicity ID:', decrypted);\n return decrypted;\n }\n }\n } catch {\n // Try next event\n continue;\n }\n }\n\n logger.debug('Nostr', 'Could not decrypt Unicity ID from any event');\n return null;\n }\n\n /**\n * Publish identity binding event on Nostr.\n * Without nametag: publishes base binding (chainPubkey, 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 try {\n const success = await this.nostrClient!.publishNametagBinding(\n nametag,\n nostrPubkey,\n {\n publicKey: chainPubkey,\n l1Address,\n directAddress,\n },\n );\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding with Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n } catch (error) {\n // publishNametagBinding throws if nametag is already claimed\n if (error instanceof Error && error.message.includes('already claimed')) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag);\n return false;\n }\n throw error;\n }\n }\n\n // No nametag — delegate to nostr-js-sdk for base identity binding\n const success = await this.nostrClient!.publishIdentityBinding({\n publicKey: chainPubkey,\n 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 /** Persist the max DM (gift-wrap) event timestamp for the since filter on next connect. */\n private updateLastDmEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastDmEventTs) return;\n\n this.lastDmEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last DM event timestamp:', err);\n });\n }\n\n private async handleDirectMessage(event: NostrEvent): Promise<void> {\n // NIP-04 (kind 4) is deprecated for DMs - only used for legacy token transfers\n // DMs should come through NIP-17 (kind 1059 gift wrap) via handleGiftWrap\n // This handler is kept for backwards compatibility but does NOT dispatch to messageHandlers\n logger.debug('Nostr', 'Ignoring NIP-04 kind 4 event (DMs use NIP-17):', event.id?.slice(0, 12));\n }\n\n private async handleGiftWrap(event: NostrEvent): Promise<void> {\n if (!this.identity || !this.keyManager) {\n logger.debug('Nostr', 'handleGiftWrap: no identity/keyManager');\n return;\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pm = NIP17.unwrap(event as any, this.keyManager);\n\n // Persist DM timestamp after successful unwrap so failed decryptions\n // do not advance the since filter and permanently skip events.\n // Use real wall-clock time, NOT event.created_at — NIP-17 gift wraps\n // randomize created_at by ±2 days for privacy, so it can be in the future.\n this.updateLastDmEventTimestamp(Math.floor(Date.now() / 1000));\n\n logger.debug('Nostr', 'Gift wrap unwrapped, sender:', pm.senderPubkey?.slice(0, 16), 'kind:', pm.kind);\n\n // Handle self-wrap (sent message copy for relay replay)\n if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.selfWrap && parsed.recipientPubkey) {\n logger.debug('Nostr', 'Self-wrap replay for recipient:', parsed.recipientPubkey?.slice(0, 16));\n const message: IncomingMessage = {\n id: parsed.originalId || pm.eventId,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n recipientTransportPubkey: parsed.recipientPubkey,\n content: parsed.text ?? '',\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n isSelfWrap: true,\n };\n for (const handler of this.messageHandlers) {\n try { handler(message); } catch (e) { logger.debug('Nostr', 'Self-wrap handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON self-wrap\n }\n logger.debug('Nostr', 'Skipping own non-self-wrap message');\n return;\n }\n\n // Handle read receipts (kind 15)\n if (isReadReceipt(pm)) {\n logger.debug('Nostr', 'Read receipt from:', pm.senderPubkey?.slice(0, 16), 'for:', pm.replyToEventId);\n if (pm.replyToEventId) {\n const receipt: IncomingReadReceipt = {\n senderTransportPubkey: pm.senderPubkey,\n messageEventId: pm.replyToEventId,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.readReceiptHandlers) {\n try { handler(receipt); } catch (e) { logger.debug('Nostr', 'Read receipt handler error:', e); }\n }\n }\n return;\n }\n\n // Handle composing indicators (kind 25050)\n if (pm.kind === COMPOSING_INDICATOR_KIND) {\n let senderNametag: string | undefined;\n let expiresIn = 30000;\n try {\n const parsed = JSON.parse(pm.content);\n senderNametag = parsed.senderNametag || undefined;\n expiresIn = parsed.expiresIn ?? 30000;\n } catch {\n // Payload parse failed — use defaults\n }\n const indicator = {\n senderPubkey: pm.senderPubkey,\n senderNametag,\n expiresIn,\n };\n logger.debug('Nostr', 'Composing indicator from:', indicator.senderNametag || pm.senderPubkey?.slice(0, 16));\n for (const handler of this.composingHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Composing handler error:', e); }\n }\n return;\n }\n\n // Handle typing indicators (JSON content with type: 'typing')\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.type === 'typing') {\n logger.debug('Nostr', 'Typing indicator from:', pm.senderPubkey?.slice(0, 16));\n const indicator: IncomingTypingIndicator = {\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.typingIndicatorHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Typing handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON — continue to chat message handling\n }\n\n if (!isChatMessage(pm)) {\n logger.debug('Nostr', 'Skipping unknown message kind:', pm.kind);\n return;\n }\n\n // Sphere app wraps DM content as JSON: {senderNametag, text}\n let content = pm.content;\n let senderNametag: string | undefined;\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed === 'object' && parsed.text !== undefined) {\n content = parsed.text;\n senderNametag = parsed.senderNametag || undefined;\n }\n } catch {\n // Plain text — use as-is\n }\n\n logger.debug('Nostr', 'DM received from:', senderNametag || pm.senderPubkey?.slice(0, 16), 'content:', content?.slice(0, 50));\n\n const message: IncomingMessage = {\n // Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.\n // This ensures read receipts reference an ID the sender recognizes.\n id: event.id,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag,\n content,\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n };\n\n this.emitEvent({ type: 'message:received', timestamp: Date.now() });\n\n if (this.messageHandlers.size === 0) {\n logger.debug('Nostr', 'No message handlers registered, buffering message for later delivery');\n this.pendingMessages.push(message);\n } else {\n logger.debug('Nostr', 'Dispatching to', this.messageHandlers.size, 'handlers');\n for (const handler of this.messageHandlers) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error:', error);\n }\n }\n }\n } catch (err) {\n // Expected for gift wraps meant for other recipients\n logger.debug('Nostr', 'Gift wrap decrypt failed (expected if not for us):', (err as Error)?.message?.slice(0, 50));\n }\n }\n\n private 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 const MAX_ATTEMPTS = 3;\n const RETRY_BASE_DELAY_MS = 500;\n const RETRY_JITTER_MS = 200;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n // Snapshot this.nostrClient at the top of each iteration. Using a local\n // reference (instead of `this.nostrClient.publishEvent(...)`) prevents a\n // concurrent disconnect() during the subsequent await from turning the\n // call into a TypeError on null.\n const client = this.nostrClient;\n if (!client) {\n throw new SphereError('Transport disconnected during retry', 'TRANSPORT_ERROR');\n }\n\n try {\n // Re-create SDK event each attempt to avoid reusing the same event ID\n // in nostr-js-sdk's pendingOks map (which would leak timers from prior attempts)\n const sdkEvent = NostrEventClass.fromJSON(event);\n await client.publishEvent(sdkEvent);\n return;\n } catch (err: unknown) {\n lastError = err;\n const rawMessage = err instanceof Error ? err.message : String(err);\n\n // Only retry relay-level rejections (nostr-js-sdk wraps these as \"Event rejected: <reason>\")\n // or relay broadcast failures (\"sent 0 of N\"). All other errors (disconnected, closed,\n // no connected relays) are non-transient and should fail immediately.\n // Locale-invariant match with `en-US` to avoid Turkish-locale surprises\n // where `'I'.toLowerCase()` yields `'ı'` (dotless i).\n const lowered = rawMessage.toLocaleLowerCase('en-US');\n const isRelayRejection =\n lowered.startsWith('event rejected:') ||\n lowered.startsWith('sent 0 of');\n\n if (!isRelayRejection || attempt === MAX_ATTEMPTS) {\n break;\n }\n\n // Add jitter to desynchronize retries across concurrent clients\n const delay = RETRY_BASE_DELAY_MS + Math.floor(Math.random() * RETRY_JITTER_MS);\n logger.debug(\n 'Nostr',\n `publishEvent attempt ${attempt}/${MAX_ATTEMPTS} failed (${rawMessage}); retrying in ${delay}ms`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n const reason = lastError instanceof Error ? lastError.message : String(lastError);\n logger.error('Nostr', `publishEvent failed after ${MAX_ATTEMPTS} attempts: ${reason}`);\n // Chain the original error as `cause` to preserve context for debugging\n throw new SphereError(\n `Failed to publish event: ${reason}`,\n 'TRANSPORT_ERROR',\n lastError\n );\n }\n\n /**\n * Publish an event with verification: after publishing, query the relay to\n * confirm the event was stored. Retries up to `maxAttempts` times on failure.\n *\n * This is critical for token transfers and DMs where silent loss means\n * funds or messages disappear. The nostr-js-sdk's publishEvent resolves on\n * a 5s timeout even without relay confirmation, so verification is needed.\n */\n private async publishWithVerification(\n event: NostrEvent,\n maxAttempts: number = 3,\n label: string = 'event',\n ): Promise<void> {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await this.publishEvent(event);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Permanent rejection — don't retry (e.g., \"Event rejected: invalid signature\")\n if (msg.includes('Event rejected') && !msg.includes('rate') && !msg.includes('limit')) {\n throw err;\n }\n if (attempt === maxAttempts) throw err;\n logger.debug('Nostr', `${label} publish attempt ${attempt} failed (${msg}), retrying in ${attempt}s...`);\n await new Promise(r => setTimeout(r, 1000 * attempt));\n continue;\n }\n\n // Verify: query the relay for this specific event by ID.\n // Jittered delay to let relay index + reduce temporal fingerprinting.\n await new Promise(r => setTimeout(r, 300 + Math.random() * 1200));\n try {\n const found = await this.queryEvents({\n ids: [event.id],\n limit: 1,\n });\n if (found.length > 0) {\n if (attempt > 1) {\n logger.debug('Nostr', `${label} verified on relay after ${attempt} attempt(s)`);\n }\n return; // confirmed on relay\n }\n } catch {\n // Query failed — can't verify. If this is the last attempt,\n // accept the publish as best-effort.\n if (attempt === maxAttempts) {\n logger.debug('Nostr', `${label} verification query failed — accepting publish as best-effort`);\n return;\n }\n }\n\n // Event not found on relay — retry with increasing backoff\n if (attempt < maxAttempts) {\n const delay = Math.min(2000 * attempt, 10000);\n logger.debug('Nostr', `${label} not found on relay, retrying in ${delay}ms (attempt ${attempt}/${maxAttempts})...`);\n await new Promise(r => setTimeout(r, delay));\n } else {\n throw new SphereError(\n `${label} not verified on relay after ${maxAttempts} attempts — delivery failed`,\n 'TRANSPORT_ERROR',\n );\n }\n }\n }\n\n async fetchPendingEvents(): Promise<void> {\n if (!this.nostrClient?.isConnected() || !this.keyManager) {\n throw new SphereError('Transport not connected', 'TRANSPORT_ERROR');\n }\n\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n EventKinds.GIFT_WRAP, // NIP-17 gift-wrapped DMs (swap proposals, invoice receipts, etc.)\n ];\n walletFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have randomized created_at (±172800 s). Extend the\n // catch-up window by the full randomization range so events sent recently\n // but timestamped far in the past are not missed.\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400 - 172800;\n\n // Capture client reference after the guard to avoid a null-deref race:\n // a concurrent disconnect() can set this.nostrClient = null between the\n // isConnected() check above and the subscribe() call below.\n const client = this.nostrClient;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n // Declared outside the Promise so it's available for cleanup after resolve.\n let subId: string | undefined;\n\n await new Promise<void>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n\n // settle() only resolves the Promise — unsubscribe happens AFTER the Promise\n // resolves (below), where subId is guaranteed to be the real subscription ID.\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n };\n\n try {\n subId = client.subscribe(walletFilter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n if (!settled) {\n timeout = setTimeout(() => settle(), 5000);\n }\n });\n\n // Unsubscribe AFTER the Promise resolves — subId is now the real subscription ID.\n // This also ensures onEvent cannot fire while we iterate events below.\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n\n // Process collected events sequentially (dedup skips already-processed ones)\n for (const event of events) {\n await this.handleEvent(event);\n }\n }\n\n private async queryEvents(filterObj: NostrFilter): Promise<NostrEvent[]> {\n if (!this.nostrClient || !this.nostrClient.isConnected()) {\n throw new SphereError('No connected relays', 'TRANSPORT_ERROR');\n }\n\n // Capture client reference after the guard — same disconnect() race as fetchPendingEvents.\n const client = this.nostrClient;\n\n const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n // Declared outside the Promise so unsubscription can happen after resolve(),\n // where subId is guaranteed to be the real subscription ID (not in TDZ).\n let subId: string | undefined;\n\n return new Promise((resolve) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n if (subId) {\n try { client.unsubscribe(subId); } catch { /* disconnected */ }\n }\n logger.warn('Nostr', `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 15000);\n\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n resolve(events);\n };\n\n try {\n subId = client.subscribe(filter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch {\n clearTimeout(timeout);\n resolve(events);\n return;\n }\n\n });\n }\n\n // ===========================================================================\n // Private: Subscriptions\n // ===========================================================================\n\n // Track subscription IDs for cleanup\n private walletSubscriptionId: string | null = null;\n private chatSubscriptionId: string | null = null;\n\n // Chat EOSE handlers — fired once when relay finishes delivering stored DMs\n private chatEoseHandlers: Array<() => void> = [];\n private chatEoseFired = false;\n\n private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\n if (this._subscriptionsSuppressed) {\n logger.debug('Nostr', 'subscribeToEvents: suppressed — mux handles event routing');\n return;\n }\n if (!this.identity || !this.keyManager || !this.nostrClient) {\n logger.debug('Nostr', 'subscribeToEvents: skipped - no identity, keyManager, or nostrClient');\n return;\n }\n\n // Unsubscribe from previous subscriptions if any\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Use 32-byte Nostr pubkey (x-coordinate only), not 33-byte compressed key\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Subscribing with Nostr pubkey:', nostrPubkey);\n\n // Determine 'since' filter from persisted last event timestamp.\n // - Existing wallet: resume from last processed event (inclusive >=, dedup handles replays)\n // - Fresh wallet / no storage: use current time (no historical replay)\n let since: number;\n if (this.storage) {\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(storageKey);\n if (stored) {\n since = parseInt(stored, 10);\n this.lastEventTs = since; // Seed in-memory tracker from storage\n this.fallbackSince = null; // Stored value takes priority\n logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else if (this.fallbackSince !== null) {\n // No stored timestamp but caller provided a fallback (e.g. address creation time).\n // This ensures events sent while the address was inactive are not missed.\n since = this.fallbackSince;\n this.lastEventTs = since;\n this.fallbackSince = null; // Consume once\n logger.debug('Nostr', 'Using fallback since timestamp:', since);\n } else {\n // No stored timestamp, no fallback = fresh wallet, start from now\n since = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored timestamp, starting from now:', since);\n }\n } catch (err) {\n logger.debug('Nostr', 'Failed to read last event timestamp, falling back to now:', err);\n since = Math.floor(Date.now() / 1000);\n this.fallbackSince = null;\n }\n } else {\n // No storage adapter — fallback to last 24h (legacy behavior)\n since = Math.floor(Date.now() / 1000) - 86400;\n logger.debug('Nostr', 'No storage adapter, using 24h fallback');\n }\n\n // Subscribe to wallet events (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 // Determine 'since' for DM (gift-wrap) subscription independently from wallet events.\n let dmSince: number;\n if (this.storage) {\n const dmStorageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(dmStorageKey);\n const parsed = stored ? parseInt(stored, 10) : NaN;\n if (Number.isFinite(parsed)) {\n dmSince = parsed;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Stored value takes priority\n logger.debug('Nostr', 'DM resuming from stored timestamp:', dmSince);\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Consume once\n logger.debug('Nostr', 'DM using fallback since timestamp:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored DM timestamp, starting from now:', dmSince);\n }\n } catch (err) {\n if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'Storage read failed, using DM fallback since:', dmSince, err);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'Failed to read last DM event timestamp, falling back to now:', err);\n }\n }\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'No storage adapter for DM, using fallback since:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No storage adapter for DM, starting from now:', dmSince);\n }\n\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have created_at randomized ±2 days for privacy.\n // Without this offset, ~50% of messages are silently dropped by the relay\n // because their randomized timestamp lands before the `since` filter.\n // Math.max(0, ...) prevents negative timestamps when dmSince is small.\n chatFilter.since = Math.max(0, dmSince - TIMESTAMP_RANDOMIZATION);\n\n this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received chat event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Chat subscription ready (EOSE)');\n if (!this.chatEoseFired) {\n this.chatEoseFired = true;\n for (const handler of this.chatEoseHandlers) {\n try { handler(); } catch { /* ignore */ }\n }\n this.chatEoseHandlers = [];\n }\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Chat subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Chat subscription created, subId:', this.chatSubscriptionId);\n }\n\n private subscribeToTags(tags: string[]): void {\n if (!this.nostrClient) return;\n\n const key = tags.sort().join(':');\n const filter = new Filter({\n kinds: [EVENT_KINDS.BROADCAST],\n '#t': tags,\n since: Math.floor(Date.now() / 1000) - 3600, // Last hour\n });\n\n const subId = this.nostrClient.subscribe(filter, {\n onEvent: (event) => {\n this.handleBroadcast({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n });\n\n this.broadcastSubscriptions.set(key, subId);\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private async decryptContent(content: string, senderPubkey: string): Promise<string> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Decrypt content using NIP-04 (using hex variant for string keys)\n const decrypted = await NIP04.decryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n senderPubkey\n );\n\n // Strip known prefixes for compatibility with nostr-js-sdk\n return this.stripContentPrefix(decrypted);\n }\n\n /**\n * Strip known content prefixes (nostr-js-sdk compatibility)\n * Handles: payment_request:, token_transfer:, etc.\n */\n private stripContentPrefix(content: string): string {\n const prefixes = [\n 'payment_request:',\n 'token_transfer:',\n 'payment_response:',\n ];\n\n for (const prefix of prefixes) {\n if (content.startsWith(prefix)) {\n return content.slice(prefix.length);\n }\n }\n\n return content;\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (!this.isConnected()) {\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n }\n\n /**\n * Async version of ensureConnected — reconnects if the original transport\n * lost its WebSocket while subscriptions are suppressed (mux handles events).\n * Used by resolve methods which are always async.\n */\n private async ensureConnectedForResolve(): Promise<void> {\n if (this.isConnected()) return;\n\n // When mux is active, the original transport can lose its idle WebSocket.\n // Reconnect transparently so resolve operations still work.\n if (this._subscriptionsSuppressed && this.nostrClient) {\n logger.debug('Nostr', 'Suppressed transport disconnected — reconnecting for resolve');\n try {\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('reconnect timeout')), 5000)\n ),\n ]);\n if (this.nostrClient.isConnected()) {\n this.status = 'connected';\n return;\n }\n } catch {\n // Fall through to throw\n }\n }\n\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n\n private ensureReady(): void {\n this.ensureConnected();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n }\n\n private emitEvent(event: TransportEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n logger.debug('Nostr', 'Event callback error:', error);\n }\n }\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom inner rumor kind.\n * Replicates the three-layer NIP-59 envelope (rumor → seal → gift wrap)\n * because NIP17.createGiftWrap hardcodes kind 14 for the inner rumor.\n */\n private createCustomKindGiftWrap(recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n return NostrTransportProvider.createCustomKindGiftWrap(this.keyManager!, recipientPubkeyHex, content, rumorKind);\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom rumor kind.\n * Shared between NostrTransportProvider and MultiAddressTransportMux.\n */\n static createCustomKindGiftWrap(keyManager: NostrKeyManager, recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n const senderPubkey = keyManager.getPublicKeyHex();\n const now = Math.floor(Date.now() / 1000);\n\n // 1. Create Rumor (unsigned inner event with custom kind)\n const rumorTags: string[][] = [['p', recipientPubkeyHex]];\n const rumorSerialized = JSON.stringify([0, senderPubkey, now, rumorKind, rumorTags, content]);\n const rumorId = bytesToHex(sha256Noble(new TextEncoder().encode(rumorSerialized)));\n const rumor = { id: rumorId, pubkey: senderPubkey, created_at: now, kind: rumorKind, tags: rumorTags, content };\n\n // 2. Create Seal (kind 13, signed by sender, encrypts rumor)\n const recipientPubkeyBytes = hexToBytes(recipientPubkeyHex);\n const encryptedRumor = NIP44.encrypt(JSON.stringify(rumor), keyManager.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(keyManager, {\n kind: EventKinds.SEAL,\n tags: [],\n content: encryptedRumor,\n created_at: sealTimestamp,\n });\n\n // 3. Create Gift Wrap (kind 1059, signed by ephemeral key, encrypts seal)\n const ephemeralKeys = NostrKeyManager.generate();\n const encryptedSeal = NIP44.encrypt(JSON.stringify(seal.toJSON()), ephemeralKeys.getPrivateKey(), recipientPubkeyBytes);\n const wrapTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const giftWrap = NostrEventClass.create(ephemeralKeys, {\n kind: EventKinds.GIFT_WRAP,\n tags: [['p', recipientPubkeyHex]],\n content: encryptedSeal,\n created_at: wrapTimestamp,\n });\n ephemeralKeys.clear();\n\n return giftWrap;\n }\n\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface NostrEvent {\n id: string;\n kind: number;\n content: string;\n tags: string[][];\n pubkey: string;\n created_at: number;\n sig: string;\n}\n\ninterface NostrFilter {\n ids?: string[];\n authors?: string[];\n kinds?: number[];\n '#p'?: string[];\n '#t'?: string[];\n '#d'?: string[];\n since?: number;\n until?: number;\n limit?: number;\n}\n","/**\n * WebSocket Abstraction\n * Platform-independent WebSocket interface for cross-platform support\n */\n\n// =============================================================================\n// WebSocket Interface\n// =============================================================================\n\n/**\n * Minimal WebSocket interface compatible with browser and Node.js\n */\nexport interface IWebSocket {\n readonly readyState: number;\n\n send(data: string): void;\n close(code?: number, reason?: string): void;\n\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: IMessageEvent) => void) | null;\n}\n\nexport interface IMessageEvent {\n data: string;\n}\n\n/**\n * WebSocket ready states (same as native WebSocket)\n */\nexport const WebSocketReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Factory function to create WebSocket instances\n * Different implementations for browser (native) vs Node.js (ws package)\n */\nexport type WebSocketFactory = (url: string) => IWebSocket;\n\n// =============================================================================\n// UUID Generator\n// =============================================================================\n\n/**\n * Generate a unique ID (platform-independent)\n * Browser: crypto.randomUUID()\n * Node: crypto.randomUUID() or uuid package\n */\nexport type UUIDGenerator = () => string;\n\n/**\n * Default UUID generator using crypto.randomUUID\n * Works in modern browsers and Node 19+\n */\nexport function defaultUUIDGenerator(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","/**\n * Browser Transport Exports\n * Re-exports shared transport with browser-specific WebSocket factory\n */\n\nimport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export shared types and classes\nexport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\n\nexport {\n type IWebSocket,\n type IMessageEvent,\n type WebSocketFactory,\n type UUIDGenerator,\n WebSocketReadyState,\n defaultUUIDGenerator,\n} from '../../../transport/websocket';\n\n// =============================================================================\n// Browser WebSocket Factory\n// =============================================================================\n\n/**\n * Browser WebSocket factory using native WebSocket\n * Cast to IWebSocket since native WebSocket is a superset\n */\nexport function createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create NostrTransportProvider with browser WebSocket\n * Convenience factory that injects browser-native WebSocket\n */\nexport function createNostrTransportProvider(\n config?: Omit<NostrTransportProviderConfig, 'createWebSocket'>\n): NostrTransportProvider {\n return new NostrTransportProvider({\n ...config,\n createWebSocket: createBrowserWebSocket,\n });\n}\n","/**\n * Unicity Aggregator Provider\n *\n * Post v1-cutover this is a thin NETWORK-CONFIG provider for the v2 token\n * engine: it loads the root trust base (JSON) via the injected platform loader\n * and exposes the gateway URL + API key. The engine (token-engine/) builds its\n * own SDK clients from these — no state-transition SDK objects live here.\n *\n * `validateToken` remains as a best-effort JSON-RPC check for LEGACY v1 TXF\n * tokens still present in storage (display-path only).\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 ValidationResult,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT } from '../constants';\nimport { SphereError } from '../core/errors';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator (gateway) URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base loading (dev only) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n /** Trust base loader (platform-specific) */\n trustBaseLoader?: TrustBaseLoader;\n}\n\n// =============================================================================\n// RPC Response Types\n// =============================================================================\n\ninterface RpcValidateResponse {\n valid?: boolean;\n spent?: boolean;\n stateHash?: string;\n error?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class UnicityAggregatorProvider implements OracleProvider {\n readonly id = 'unicity-aggregator';\n readonly name = 'Unicity Aggregator';\n readonly type = 'network' as const;\n readonly description = 'Unicity gateway network config (trust base + URL + API key) for the v2 token engine';\n\n private config: Required<Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'>> & {\n trustBaseLoader?: TrustBaseLoader;\n };\n private status: ProviderStatus = 'disconnected';\n private eventCallbacks: Set<OracleEventCallback> = new Set();\n\n /** Raw trust-base JSON as loaded (the v2 token engine parses it itself). */\n private trustBaseJson: unknown | null = null;\n\n // Cache for spent states reported by validateToken (immutable once spent)\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 // v2 token-engine config surface\n // ===========================================================================\n\n /** Raw trust-base JSON (for constructing the v2 token engine); null when unavailable. */\n getTrustBaseJson(): unknown | null {\n return this.trustBaseJson;\n }\n\n /** The gateway URL this provider is configured against. */\n getAggregatorUrl(): string {\n return this.config.url;\n }\n\n /** The gateway API key (for the v2 token engine); undefined when none is configured. */\n getApiKey(): string | undefined {\n return this.config.apiKey || undefined;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n // Mark as connected — actual connectivity is verified on first operation.\n this.status = 'connected';\n this.emitEvent({ type: 'oracle:connected', timestamp: Date.now() });\n this.log('Connected to oracle:', this.config.url);\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.emitEvent({ type: 'oracle:disconnected', timestamp: Date.now() });\n this.log('Disconnected from oracle');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // OracleProvider Implementation\n // ===========================================================================\n\n async initialize(trustBaseJson?: unknown): Promise<void> {\n if (trustBaseJson) {\n this.trustBaseJson = trustBaseJson;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n try {\n const loaded = await this.config.trustBaseLoader.load();\n if (loaded) {\n this.trustBaseJson = loaded;\n }\n } catch (error) {\n this.log('Failed to load trust base:', error);\n }\n }\n\n await this.connect();\n this.log('Initialized with trust base JSON:', !!this.trustBaseJson);\n }\n\n /**\n * Best-effort RPC validation for LEGACY v1 TXF tokens (display path only).\n * v2 blob tokens are verified via the token engine and never reach this.\n */\n async validateToken(tokenData: unknown): Promise<ValidationResult> {\n this.ensureConnected();\n\n try {\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 // 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// Testnet2 (networkId 4) — embedded copy of bft-trustbase.testnet2.json\nexport const TRUSTBASE_TESTNET2 = {\n version: 1,\n networkId: 4,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy',\n sigKey: '0x02b2b1daf4dee562ef5ed5b89e1e0e6cf0bf952ce3af12be6531e40f8bd1a7e682',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M',\n sigKey: '0x02822d7ce59d2ff4f2da4cef361d5a80a3a74c351fe61a8a2c908a54841930cee0',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC',\n sigKey: '0x0352ef027d60921e252839bea60e2f1810473fb0ad8cb89044b101aecdea5f356a',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF',\n sigKey: '0x02ef71c49c77ca9510ad84596522b887e4df0b3262fce80192ddcfb6276efdf901',\n stake: 1,\n },\n ],\n quorumThreshold: 3,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy':\n '0xe9289562b9faf283ae264ce42ef641133237bca6b7f232e1e293305f0cf994d3262cee1ca9c8de93fc5b7da1466486123b6219e5308843b8ead173472dad35e501',\n '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M':\n '0x45e26943305c264d5dc5c3069cd5dbd2d43feb35d56d580656b80b33f4126e2a735b0c8eace0ef638128d9dbf5eb854dd9ad3ba42f9eafded60251bf7edb454501',\n '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC':\n '0xe2263c1cb24b6d933ead249901c7fd83fa884d95b935a63f1489b07ea342281071cbf2117ad27be29cde9207d8d87e8b331630718d92d644faf2fccbc9dba4c000',\n '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF':\n '0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301',\n },\n};\n\n// Mainnet trust base (TODO: add when available)\nexport const TRUSTBASE_MAINNET = null;\n\n// Dev trust base (same as testnet for now)\nexport const TRUSTBASE_DEV = TRUSTBASE_TESTNET;\n","/**\n * Shared TrustBase Loader Logic\n * Common embedded trustbase data and base loader\n */\n\nimport { TRUSTBASE_TESTNET2, TRUSTBASE_MAINNET, TRUSTBASE_DEV } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\nimport { SphereError } from '../../core/errors';\n\nexport interface TrustBaseLoader {\n load(): Promise<unknown | null>;\n}\n\n/**\n * Get embedded trustbase data by network\n */\nexport function getEmbeddedTrustBase(network: NetworkType): unknown | null {\n switch (network) {\n case 'mainnet':\n return TRUSTBASE_MAINNET;\n // v1 cutover: 'testnet' is now an alias of testnet2 (NETWORKS.testnet points\n // at the testnet2 gateway), so it MUST resolve the testnet2 trust base —\n // a mismatched trust base would make the engine reject every proof.\n case 'testnet':\n case 'testnet2':\n return TRUSTBASE_TESTNET2;\n case 'dev':\n return TRUSTBASE_DEV;\n default:\n // Fail loud: an unknown network must not silently resolve to the testnet trust base.\n throw new SphereError(`getEmbeddedTrustBase: unknown network \"${network}\"`, 'INVALID_CONFIG');\n }\n}\n\n/**\n * Base TrustBase loader with embedded fallback\n */\nexport abstract class BaseTrustBaseLoader implements TrustBaseLoader {\n protected network: NetworkType;\n\n constructor(network: NetworkType = 'testnet') {\n this.network = network;\n }\n\n /**\n * Try to load from external source (file, URL, etc.)\n * Override in subclass\n */\n protected abstract loadFromExternal(): Promise<unknown | null>;\n\n async load(): Promise<unknown | null> {\n // Try external source first\n const external = await this.loadFromExternal();\n if (external) {\n return external;\n }\n\n // Fallback to embedded data\n return getEmbeddedTrustBase(this.network);\n }\n}\n","/**\n * Browser Oracle Exports\n * Re-exports shared oracle with browser-specific TrustBaseLoader\n */\n\nimport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\nimport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\nimport { BaseTrustBaseLoader } from '../../shared/trustbase-loader';\nimport type { NetworkType } from '../../../constants';\nimport { SphereError } from '../../../core/errors';\n\n// Re-export shared types and classes\nexport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n UnicityOracleProvider,\n type UnicityOracleProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\n\nexport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\n\n// =============================================================================\n// Browser TrustBase Loader\n// =============================================================================\n\n/**\n * Browser TrustBase loader - fetches from URL or uses embedded data\n */\nexport class BrowserTrustBaseLoader extends BaseTrustBaseLoader {\n private url?: string;\n\n /**\n * @param networkOrUrl - a NetworkType, or a URL to fetch the trust base from\n * @param fallbackNetwork - network used for the embedded fallback when a URL is\n * supplied (and the fetch fails). Required when networkOrUrl is a URL — we must\n * never silently fall back to testnet on a different network.\n */\n constructor(networkOrUrl?: NetworkType | string, fallbackNetwork?: NetworkType) {\n if (!networkOrUrl) {\n throw new SphereError('BrowserTrustBaseLoader: network or trustBaseUrl is required.', 'INVALID_CONFIG');\n }\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n if (!fallbackNetwork) {\n throw new SphereError(\n 'BrowserTrustBaseLoader: fallbackNetwork is required when a trustBaseUrl is supplied.',\n 'INVALID_CONFIG',\n );\n }\n super(fallbackNetwork);\n this.url = networkOrUrl;\n } else {\n super(networkOrUrl as NetworkType);\n }\n }\n\n protected async loadFromExternal(): Promise<unknown | null> {\n if (!this.url) return null;\n\n try {\n const response = await fetch(this.url);\n if (response.ok) {\n return await response.json();\n }\n } catch {\n // Fall through to embedded\n }\n return null;\n }\n}\n\n/**\n * Create browser TrustBase loader\n */\nexport function createBrowserTrustBaseLoader(\n networkOrUrl?: NetworkType | string,\n fallbackNetwork?: NetworkType,\n): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl, fallbackNetwork);\n}\n\n// =============================================================================\n// Browser Factory\n// =============================================================================\n\n/**\n * Create UnicityAggregatorProvider with browser TrustBase loader\n */\nexport function createUnicityAggregatorProvider(\n config: Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'> & {\n trustBaseUrl?: string;\n network?: NetworkType;\n }\n): UnicityAggregatorProvider {\n const { trustBaseUrl, network, ...restConfig } = config;\n // Fail loud: without a network we cannot pick the right embedded trust base.\n if (!trustBaseUrl && !network) {\n throw new SphereError(\n 'createUnicityAggregatorProvider: network or trustBaseUrl required.',\n 'INVALID_CONFIG',\n );\n }\n return new UnicityAggregatorProvider({\n ...restConfig,\n // When a URL is supplied, `network` (if any) is the embedded-fallback network.\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network, network),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\n","/**\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/**\n * Recover the compressed secp256k1 public key from a signed message.\n *\n * Use this when the server needs to identify the signer (not just verify\n * a signature against a known pubkey). Combined with `sphere.resolve(pubkey)`\n * it gives a fully cryptographically-attributable identity for backend auth,\n * without trusting any client-supplied identifier claim.\n *\n * @param message - The plaintext that was signed\n * @param signature - 130-char hex (v + r + s) as produced by `signMessage`\n * @returns - Compressed 66-char hex pubkey\n * @throws - On malformed signature length or out-of-range recovery byte\n */\nexport function recoverPubkeyFromSignature(message: string, signature: string): string {\n if (signature.length !== 130) {\n throw new SphereError(\n `Invalid signature length: expected 130 hex chars, got ${signature.length}`,\n 'SIGNING_ERROR',\n );\n }\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) {\n throw new SphereError(\n `Invalid recovery byte: v=${v} out of range [0..3]`,\n 'SIGNING_ERROR',\n );\n }\n\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n\n const recovered = ec.recoverPubKey(hashBytes, { r, s }, v);\n return recovered.encode('hex', true);\n}\n\n// Re-export elliptic instance for advanced use cases\nexport { ec };\n","/**\n * wallet-api/errors.ts — the client's typed error surface (S1).\n *\n * HTTP error mapping follows ARCHITECTURE §16: `{ error: { code, message } }`\n * with 401 auth · 403 ownership/addressee · 404 unknown · 409 conflict\n * (lineage/ownership) · 413 too large · 422 validation · 429 rate-limit/quota.\n */\n\nexport type WalletApiErrorCode =\n /** Construction/config problems (bad base URL, missing identity). */\n | 'CONFIG'\n /** The server challenge failed template verification — never signed. */\n | 'CHALLENGE_TEMPLATE'\n /** 401 — session invalid and re-auth also failed. */\n | 'UNAUTHORIZED'\n /** 403 — ownership/addressee check failed. */\n | 'FORBIDDEN'\n /** 404 — unknown resource. */\n | 'NOT_FOUND'\n /** 409 — lineage/ownership conflict (e.g. evidenced-tombstone re-add — §5.3). */\n | 'CONFLICT'\n /** 413 — blob too large. */\n | 'TOO_LARGE'\n /** 422 — validation failure (malformed body, failed §8.2 pipeline). */\n | 'VALIDATION'\n /** 429 — rate limit / quota. */\n | 'RATE_LIMITED'\n /** Transport-level failure (fetch threw, connection refused, …). */\n | 'NETWORK'\n /** The response did not match the §16 contract (parse/shape failure). */\n | 'PROTOCOL'\n /** Any other non-2xx status. */\n | 'SERVER';\n\nconst STATUS_TO_CODE: Record<number, WalletApiErrorCode> = {\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 409: 'CONFLICT',\n 413: 'TOO_LARGE',\n 422: 'VALIDATION',\n 429: 'RATE_LIMITED',\n};\n\nexport class WalletApiError extends Error {\n readonly code: WalletApiErrorCode;\n /** HTTP status when the error came from a response. */\n readonly status?: number;\n\n constructor(message: string, code: WalletApiErrorCode, status?: number, cause?: unknown) {\n super(message);\n this.name = 'WalletApiError';\n this.code = code;\n this.status = status;\n if (cause !== undefined) (this as { cause?: unknown }).cause = cause;\n }\n\n static fromStatus(status: number, message: string): WalletApiError {\n return new WalletApiError(message, STATUS_TO_CODE[status] ?? 'SERVER', status);\n }\n}\n\n/**\n * The auth challenge failed template verification (S1): wrong domain prefix,\n * foreign pubkey, or implausible timestamps. The spend key never signs\n * server-chosen text that fails this check.\n */\nexport class ChallengeTemplateError extends WalletApiError {\n constructor(message: string) {\n super(message, 'CHALLENGE_TEMPLATE');\n this.name = 'ChallengeTemplateError';\n }\n}\n","/**\n * wallet-api/challenge.ts — auth-challenge template verification (S1,\n * ARCHITECTURE §4).\n *\n * The spend key NEVER signs arbitrary server-chosen text. Before signing, the\n * client verifies the challenge:\n * 1. begins with the fixed domain-separation prefix\n * `unicity:wallet-api:auth:v1\\n`;\n * 2. embeds the client's OWN pubkey (and the nonce returned alongside it);\n * 3. carries plausible timestamps (`issuedAt` near now, `expiresAt` in the\n * future, a sane validity window).\n *\n * The accepted grammar matches the REAL backend (wallet-api M1,\n * src/auth/service.ts): the prefix followed by a single-line JSON object with\n * `network`, `pubkey`, `nonce`, `issuedAt`, `expiresAt` (ISO-8601). This\n * grammar is the client's acceptance contract: a server whose challenge does\n * not parse is refused, by design. The fake server\n * (tests/support/fake-wallet-api.ts) produces exactly this form.\n */\n\nimport { ChallengeTemplateError } from './errors';\n\n/** Fixed domain-separation prefix (ARCHITECTURE §4 step 1). */\nexport const AUTH_CHALLENGE_PREFIX = 'unicity:wallet-api:auth:v1\\n';\n\n/** Allowed clock skew between client and server (ms). */\nconst MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;\n\n/** Maximum plausible challenge validity window (server default: NONCE_TTL = 5 min). */\nconst MAX_VALIDITY_WINDOW_MS = 60 * 60 * 1000;\n\nexport interface ChallengeExpectation {\n /** The client's own compressed pubkey hex — the challenge MUST embed it. */\n pubkey: string;\n /** The nonce returned by `POST /v1/auth/challenge` alongside the challenge. */\n nonce: string;\n /** The client's network — refused if the challenge names a different one. */\n network: string;\n /** Current time, ms since epoch. */\n nowMs: number;\n}\n\nfunction parseFields(body: string): Map<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n throw new ChallengeTemplateError('Challenge body is not the single-line JSON object the backend emits');\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new ChallengeTemplateError('Challenge body must be a JSON object');\n }\n const fields = new Map<string, string>();\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value !== 'string') {\n throw new ChallengeTemplateError(`Challenge field \"${key}\" must be a string`);\n }\n fields.set(key, value);\n }\n return fields;\n}\n\nfunction requireField(fields: Map<string, string>, key: string): string {\n const value = fields.get(key);\n if (value === undefined || value === '') {\n throw new ChallengeTemplateError(`Challenge is missing the \"${key}\" field`);\n }\n return value;\n}\n\nfunction parseTimestamp(value: string, key: string): number {\n const ms = Date.parse(value);\n if (Number.isNaN(ms)) {\n throw new ChallengeTemplateError(`Challenge \"${key}\" is not a parseable timestamp: ${value}`);\n }\n return ms;\n}\n\n/**\n * Verify a server auth challenge before signing. Throws\n * {@link ChallengeTemplateError} on ANY violation — the caller MUST refuse to\n * sign in that case.\n */\nexport function verifyChallengeTemplate(challenge: string, expect: ChallengeExpectation): void {\n if (typeof challenge !== 'string' || !challenge.startsWith(AUTH_CHALLENGE_PREFIX)) {\n throw new ChallengeTemplateError('Challenge does not start with the unicity:wallet-api:auth:v1 domain prefix');\n }\n const fields = parseFields(challenge.slice(AUTH_CHALLENGE_PREFIX.length));\n\n const pubkey = requireField(fields, 'pubkey');\n if (pubkey.toLowerCase() !== expect.pubkey.toLowerCase()) {\n throw new ChallengeTemplateError('Challenge embeds a different pubkey than this wallet');\n }\n\n const nonce = requireField(fields, 'nonce');\n if (nonce !== expect.nonce) {\n throw new ChallengeTemplateError('Challenge nonce does not match the nonce issued with it');\n }\n\n const network = requireField(fields, 'network');\n if (network !== expect.network) {\n throw new ChallengeTemplateError(`Challenge is for network \"${network}\", expected \"${expect.network}\"`);\n }\n\n const issuedAt = parseTimestamp(requireField(fields, 'issuedAt'), 'issuedAt');\n const expiresAt = parseTimestamp(requireField(fields, 'expiresAt'), 'expiresAt');\n\n if (issuedAt > expect.nowMs + MAX_CLOCK_SKEW_MS) {\n throw new ChallengeTemplateError('Challenge issuedAt is in the future');\n }\n if (expiresAt <= expect.nowMs - MAX_CLOCK_SKEW_MS) {\n throw new ChallengeTemplateError('Challenge is already expired');\n }\n if (expiresAt <= issuedAt || expiresAt - issuedAt > MAX_VALIDITY_WINDOW_MS) {\n throw new ChallengeTemplateError('Challenge validity window is implausible');\n }\n}\n","/**\n * wallet-api/codec.ts — runtime wire ⇄ domain conversion for §16 responses.\n *\n * Hand-rolled validators (repo convention — no zod dependency). The hard rule\n * (ARCHITECTURE §11): asset amounts are decimal strings on the wire and\n * `bigint` in domain types — never a JS `number` (they exceed 2^53). Cursors,\n * seqs and the syncEpoch are parsed the same way; the codec tolerates either\n * a decimal string (the Postgres `bigint`-over-`pg` wire form) or a safe\n * integer number for those counters, but amounts MUST be strings.\n */\n\nimport type { InventoryItem } from '../storage/storage-provider';\nimport { WalletApiError } from './errors';\nimport type {\n BlobUrlEntry,\n CoinBalance,\n HistoryPage,\n HistoryWireRecord,\n IntentRecord,\n InventoryPage,\n MailboxClaimResult,\n MailboxEntry,\n MailboxPage,\n PaymentRequestRecord,\n PaymentRequestsPage,\n UploadUrlEntry,\n} from './types';\n\nfunction protocolError(message: string): WalletApiError {\n return new WalletApiError(message, 'PROTOCOL');\n}\n\nfunction asRecord(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw protocolError(`${what} is not an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction asArray(value: unknown, what: string): unknown[] {\n if (!Array.isArray(value)) throw protocolError(`${what} is not an array`);\n return value;\n}\n\nfunction asString(value: unknown, what: string): string {\n if (typeof value !== 'string') throw protocolError(`${what} is not a string`);\n return value;\n}\n\n/** Counter (cursor/seq/syncEpoch): decimal string or safe integer → bigint. */\nexport function parseCounter(value: unknown, what: string): bigint {\n if (typeof value === 'string' && /^[0-9]+$/.test(value)) return BigInt(value);\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return BigInt(value);\n throw protocolError(`${what} is not a non-negative integer counter`);\n}\n\n/** Amount: decimal string ONLY (§11) → bigint. */\nexport function parseAmount(value: unknown, what: string): bigint {\n if (typeof value === 'string' && /^[0-9]+$/.test(value)) return BigInt(value);\n throw protocolError(`${what} is not a decimal-string amount`);\n}\n\nfunction parseAssets(value: unknown, what: string): InventoryItem['assets'] {\n if (value === undefined || value === null) return undefined;\n return asArray(value, what).map((raw, i) => {\n const rec = asRecord(raw, `${what}[${i}]`);\n return {\n coinId: asString(rec.coinId, `${what}[${i}].coinId`),\n amount: parseAmount(rec.amount, `${what}[${i}].amount`),\n };\n });\n}\n\nfunction parseInventoryItem(raw: unknown, what: string): InventoryItem {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'active' && status !== 'removed') {\n throw protocolError(`${what}.status is neither 'active' nor 'removed'`);\n }\n return {\n tokenId: asString(rec.tokenId, `${what}.tokenId`),\n status,\n assets: parseAssets(rec.assets, `${what}.assets`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n };\n}\n\n/** `GET /v1/inventory` → {@link InventoryPage} (§16). */\nexport function parseInventoryPage(body: unknown): InventoryPage {\n const rec = asRecord(body, 'inventory response');\n if (typeof rec.more !== 'boolean') throw protocolError('inventory response .more is not a boolean');\n return {\n cursor: parseCounter(rec.cursor, 'inventory response .cursor'),\n syncEpoch: parseCounter(rec.syncEpoch, 'inventory response .syncEpoch'),\n more: rec.more,\n items: asArray(rec.items, 'inventory response .items').map((raw, i) =>\n parseInventoryItem(raw, `items[${i}]`)\n ),\n };\n}\n\n/** `GET /v1/balances` → {@link CoinBalance}[] (§16). */\nexport function parseBalances(body: unknown): CoinBalance[] {\n const rec = asRecord(body, 'balances response');\n return asArray(rec.balances, 'balances response .balances').map((raw, i) => {\n const b = asRecord(raw, `balances[${i}]`);\n const tokenCount = b.tokenCount;\n if (typeof tokenCount !== 'number' || !Number.isSafeInteger(tokenCount) || tokenCount < 0) {\n throw protocolError(`balances[${i}].tokenCount is not a non-negative integer`);\n }\n return {\n coinId: asString(b.coinId, `balances[${i}].coinId`),\n total: parseAmount(b.total, `balances[${i}].total`),\n tokenCount,\n };\n });\n}\n\n/** `POST /v1/tokens/blob-urls` → {@link BlobUrlEntry}[] (§16). */\nexport function parseBlobUrls(body: unknown): BlobUrlEntry[] {\n const rec = asRecord(body, 'blob-urls response');\n return asArray(rec.urls, 'blob-urls response .urls').map((raw, i) => {\n const u = asRecord(raw, `urls[${i}]`);\n return {\n tokenId: asString(u.tokenId, `urls[${i}].tokenId`),\n getUrl: asString(u.getUrl, `urls[${i}].getUrl`),\n };\n });\n}\n\n/** `POST /v1/tokens/upload-urls` → {@link UploadUrlEntry}[] (§16). */\nexport function parseUploadUrls(body: unknown): UploadUrlEntry[] {\n const rec = asRecord(body, 'upload-urls response');\n return asArray(rec.urls, 'upload-urls response .urls').map((raw, i) => {\n const u = asRecord(raw, `urls[${i}]`);\n return {\n sha256: asString(u.sha256, `urls[${i}].sha256`),\n key: asString(u.key, `urls[${i}].key`),\n putUrl: asString(u.putUrl, `urls[${i}].putUrl`),\n };\n });\n}\n\n/** `POST /v1/inventory/apply` → cursor (§16). */\nexport function parseApplyResult(body: unknown): bigint {\n const rec = asRecord(body, 'apply response');\n return parseCounter(rec.cursor, 'apply response .cursor');\n}\n\n/** `GET /v1/intents` → {@link IntentRecord}[] (§16). */\nexport function parseIntents(body: unknown): IntentRecord[] {\n const rec = asRecord(body, 'intents response');\n return asArray(rec.intents, 'intents response .intents').map((raw, i) => {\n const it = asRecord(raw, `intents[${i}]`);\n const status = asString(it.status, `intents[${i}].status`);\n if (status !== 'open' && status !== 'completed' && status !== 'aborted') {\n throw protocolError(`intents[${i}].status is not a known intent status`);\n }\n const createdAt = Date.parse(asString(it.createdAt, `intents[${i}].createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`intents[${i}].createdAt is not a timestamp`);\n return {\n transferId: asString(it.transferId, `intents[${i}].transferId`),\n payload: asString(it.payload, `intents[${i}].payload`),\n status,\n createdAt,\n };\n });\n}\n\n// ── mailbox (§6/§16) ──────────────────────────────────────────────────────────\n\n/** `POST /v1/mailbox` → entryId (§16). */\nexport function parseDepositResult(body: unknown): string {\n const rec = asRecord(body, 'mailbox deposit response');\n return asString(rec.entryId, 'mailbox deposit response .entryId');\n}\n\nfunction parseMailboxEntry(raw: unknown, what: string): MailboxEntry {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'unclaimed' && status !== 'claimed' && status !== 'rejected') {\n throw protocolError(`${what}.status is not a known mailbox entry status`);\n }\n const createdAt = Date.parse(asString(rec.createdAt, `${what}.createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`${what}.createdAt is not a timestamp`);\n return {\n entryId: asString(rec.entryId, `${what}.entryId`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n status,\n transferId: asString(rec.transferId, `${what}.transferId`),\n tokenId: asString(rec.tokenId, `${what}.tokenId`),\n assets: (parseAssets(rec.assets, `${what}.assets`) ?? []).map((a) => ({ ...a })),\n senderPubkey: asString(rec.senderPubkey, `${what}.senderPubkey`),\n ...(rec.memo !== undefined && rec.memo !== null\n ? { memo: asString(rec.memo, `${what}.memo`) }\n : {}),\n createdAt,\n ...(rec.getUrl !== undefined && rec.getUrl !== null\n ? { getUrl: asString(rec.getUrl, `${what}.getUrl`) }\n : {}),\n ...(rec.blobCollected === true ? { blobCollected: true } : {}),\n };\n}\n\n/** `GET /v1/mailbox?since=` → {@link MailboxPage} (§16). */\nexport function parseMailboxPage(body: unknown): MailboxPage {\n const rec = asRecord(body, 'mailbox response');\n if (typeof rec.more !== 'boolean') throw protocolError('mailbox response .more is not a boolean');\n return {\n readPointer: parseCounter(rec.readPointer, 'mailbox response .readPointer'),\n syncEpoch: parseCounter(rec.syncEpoch, 'mailbox response .syncEpoch'),\n more: rec.more,\n entries: asArray(rec.entries, 'mailbox response .entries').map((raw, i) =>\n parseMailboxEntry(raw, `entries[${i}]`)\n ),\n };\n}\n\n/** `POST /v1/mailbox/claim` → {@link MailboxClaimResult} (§16). */\nexport function parseClaimResult(body: unknown): MailboxClaimResult {\n const rec = asRecord(body, 'mailbox claim response');\n return {\n claimed: asArray(rec.claimed, 'claim response .claimed').map((raw, i) =>\n asString(raw, `claimed[${i}]`)\n ),\n alreadyClaimed: asArray(rec.alreadyClaimed, 'claim response .alreadyClaimed').map((raw, i) => {\n const a = asRecord(raw, `alreadyClaimed[${i}]`);\n if (typeof a.intoInventory !== 'boolean') {\n throw protocolError(`alreadyClaimed[${i}].intoInventory is not a boolean`);\n }\n return { entryId: asString(a.entryId, `alreadyClaimed[${i}].entryId`), intoInventory: a.intoInventory };\n }),\n failed: asArray(rec.failed, 'claim response .failed').map((raw, i) => {\n const f = asRecord(raw, `failed[${i}]`);\n return { entryId: asString(f.entryId, `failed[${i}].entryId`), code: asString(f.code, `failed[${i}].code`) };\n }),\n };\n}\n\n/** `POST /v1/mailbox/reject` → rejected entry ids (§16). */\nexport function parseRejectResult(body: unknown): string[] {\n const rec = asRecord(body, 'mailbox reject response');\n return asArray(rec.rejected, 'reject response .rejected').map((raw, i) => asString(raw, `rejected[${i}]`));\n}\n\n// ── payment requests (§10/§16) ────────────────────────────────────────────────\n\n/** One §16 payment request → {@link PaymentRequestRecord} (create/respond/list). */\nexport function parsePaymentRequestRecord(raw: unknown, what: string): PaymentRequestRecord {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'open' && status !== 'paid' && status !== 'declined' && status !== 'expired') {\n throw protocolError(`${what}.status is not a known payment-request status`);\n }\n const createdAt = Date.parse(asString(rec.createdAt, `${what}.createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`${what}.createdAt is not a timestamp`);\n let expiresAt: number | undefined;\n if (rec.expiresAt !== undefined && rec.expiresAt !== null) {\n expiresAt = Date.parse(asString(rec.expiresAt, `${what}.expiresAt`));\n if (Number.isNaN(expiresAt)) throw protocolError(`${what}.expiresAt is not a timestamp`);\n }\n if (rec.transferId !== null && typeof rec.transferId !== 'string') {\n throw protocolError(`${what}.transferId is neither a string nor null`);\n }\n return {\n id: asString(rec.id, `${what}.id`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n fromPubkey: asString(rec.fromPubkey, `${what}.fromPubkey`),\n toPubkey: asString(rec.toPubkey, `${what}.toPubkey`),\n assets: (parseAssets(rec.assets, `${what}.assets`) ?? []).map((a) => ({ ...a })),\n ...(rec.memo !== undefined && rec.memo !== null\n ? { memo: asString(rec.memo, `${what}.memo`) }\n : {}),\n status,\n transferId: rec.transferId,\n createdAt,\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n };\n}\n\n/**\n * `GET /v1/payment-requests?role=` → {@link PaymentRequestsPage} (§16). The\n * cursor family is role-bound: incoming pages carry the `?since=` seq counter,\n * outgoing pages an opaque keyset string (null once drained) — a page whose\n * cursor does not match its role is a protocol violation.\n */\nexport function parsePaymentRequestsPage(\n body: unknown,\n role: 'incoming' | 'outgoing'\n): PaymentRequestsPage {\n const rec = asRecord(body, 'payment-requests response');\n if (typeof rec.more !== 'boolean') throw protocolError('payment-requests response .more is not a boolean');\n const requests = asArray(rec.requests, 'payment-requests response .requests').map((raw, i) =>\n parsePaymentRequestRecord(raw, `requests[${i}]`)\n );\n const syncEpoch = parseCounter(rec.syncEpoch, 'payment-requests response .syncEpoch');\n if (role === 'incoming') {\n return {\n role,\n requests,\n more: rec.more,\n cursor: parseCounter(rec.cursor, 'payment-requests response .cursor'),\n syncEpoch,\n };\n }\n if (rec.cursor !== null && typeof rec.cursor !== 'string') {\n throw protocolError('payment-requests response .cursor is neither a keyset string nor null');\n }\n return { role, requests, more: rec.more, cursor: rec.cursor, syncEpoch };\n}\n\n// ── history (§10/§16) ─────────────────────────────────────────────────────────\n\nfunction parseHistoryRecord(raw: unknown, what: string): HistoryWireRecord {\n const rec = asRecord(raw, what);\n return {\n dedupKey: asString(rec.dedupKey, `${what}.dedupKey`),\n id: asString(rec.id, `${what}.id`),\n type: asString(rec.type, `${what}.type`),\n ts: asString(rec.ts, `${what}.ts`),\n assets: asArray(rec.assets, `${what}.assets`).map((a, i) => {\n const asset = asRecord(a, `${what}.assets[${i}]`);\n // Amounts stay decimal strings on this surface — display data (§10).\n parseAmount(asset.amount, `${what}.assets[${i}].amount`);\n return {\n coinId: asString(asset.coinId, `${what}.assets[${i}].coinId`),\n amount: asString(asset.amount, `${what}.assets[${i}].amount`),\n };\n }),\n ...(typeof rec.transferId === 'string' ? { transferId: rec.transferId } : {}),\n ...(typeof rec.tokenId === 'string' ? { tokenId: rec.tokenId } : {}),\n ...(typeof rec.counterpartyPubkey === 'string' ? { counterpartyPubkey: rec.counterpartyPubkey } : {}),\n ...(typeof rec.memo === 'string' ? { memo: rec.memo } : {}),\n ...(typeof rec.counterpartyNametag === 'string' ? { counterpartyNametag: rec.counterpartyNametag } : {}),\n };\n}\n\n/** `GET /v1/history` → {@link HistoryPage} (§16). */\nexport function parseHistoryPage(body: unknown): HistoryPage {\n const rec = asRecord(body, 'history response');\n if (typeof rec.more !== 'boolean') throw protocolError('history response .more is not a boolean');\n if (rec.cursor !== null && typeof rec.cursor !== 'string') {\n throw protocolError('history response .cursor is not a string or null');\n }\n return {\n records: asArray(rec.records, 'history response .records').map((raw, i) =>\n parseHistoryRecord(raw, `records[${i}]`)\n ),\n more: rec.more,\n cursor: rec.cursor,\n syncEpoch: parseCounter(rec.syncEpoch, 'history response .syncEpoch'),\n };\n}\n\n/** Auth responses (§4). */\nexport function parseAuthChallenge(body: unknown): { nonce: string; challenge: string } {\n const rec = asRecord(body, 'auth/challenge response');\n return {\n nonce: asString(rec.nonce, 'auth/challenge response .nonce'),\n challenge: asString(rec.challenge, 'auth/challenge response .challenge'),\n };\n}\n\nexport function parseAuthTokens(body: unknown): { jwt: string; refreshToken: string } {\n const rec = asRecord(body, 'auth token response');\n return {\n jwt: asString(rec.jwt, 'auth token response .jwt'),\n refreshToken: asString(rec.refreshToken, 'auth token response .refreshToken'),\n };\n}\n\n/** `POST /v1/ws-ticket` (§9). */\nexport function parseWsTicket(body: unknown): string {\n const rec = asRecord(body, 'ws-ticket response');\n return asString(rec.ticket, 'ws-ticket response .ticket');\n}\n\n/** WS wake frame (§9): `{ type:'wake', stream, syncEpoch }`. */\nexport function parseWakeFrame(raw: string): { stream: 'inventory' | 'mailbox' | 'payment_requests'; syncEpoch: bigint } | null {\n let body: unknown;\n try {\n body = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof body !== 'object' || body === null) return null;\n const rec = body as Record<string, unknown>;\n if (rec.type !== 'wake') return null;\n const stream = rec.stream;\n if (stream !== 'inventory' && stream !== 'mailbox' && stream !== 'payment_requests') return null;\n try {\n return { stream, syncEpoch: parseCounter(rec.syncEpoch, 'wake .syncEpoch') };\n } catch {\n return null;\n }\n}\n","/**\n * wallet-api/client.ts — `WalletApiClient` (sdk-changes S1).\n *\n * A small typed client for the wallet-api backend: challenge→sign→JWT with a\n * rotating refresh token, typed REST for the §16 endpoints, and the WS wake\n * channel (ticket flow — §9). Injected into providers (DI; no singletons).\n *\n * Cross-cutting rules (S1):\n * - **Challenge template verification** — the spend key never signs text that\n * fails `verifyChallengeTemplate` (prefix + own pubkey + plausible\n * timestamps). See ./challenge.ts.\n * - **Credential hygiene** — the refresh token lives only in the injected\n * {@link KeyValueStore} (never a URL, never logged). A rotation-reuse\n * revocation or refresh expiry falls back to a fresh challenge→sign cycle —\n * silent, since the wallet key is available at unlock. Non-loopback base\n * URLs MUST be `https:` (ARCHITECTURE §4 transport rule), enforced at\n * construction.\n * - **Amounts are decimal strings end-to-end**, parsed with `BigInt` (§11) —\n * see ./codec.ts.\n *\n * The client also keeps the NORMATIVE LOCAL COPY of open intents (E.3): the\n * server is the primary, but intents are the one server table not\n * re-derivable from blobs — after a server restore (`syncEpoch` change,\n * ARCHITECTURE §5.4) the client re-PUTs its locally-known open intents\n * (idempotent) before anything resumes. `putIntent` persists locally before\n * the server PUT; the local copy survives a failed PUT as the restore\n * backstop.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\n\nimport { signMessage } from '../core/crypto';\nimport { WalletApiError } from './errors';\nimport { verifyChallengeTemplate } from './challenge';\nimport {\n parseApplyResult,\n parseAuthChallenge,\n parseAuthTokens,\n parseBalances,\n parseBlobUrls,\n parseClaimResult,\n parseDepositResult,\n parseHistoryPage,\n parseIntents,\n parseInventoryPage,\n parseMailboxPage,\n parsePaymentRequestRecord,\n parsePaymentRequestsPage,\n parseRejectResult,\n parseUploadUrls,\n parseWakeFrame,\n parseWsTicket,\n} from './codec';\nimport type {\n ApplyDeltaRequest,\n BlobUrlEntry,\n CoinBalance,\n CreatePaymentRequestInput,\n FetchLike,\n FetchResponseLike,\n HistoryPage,\n HistoryWireRecord,\n IntentRecord,\n InventoryPage,\n KeyValueStore,\n ListPaymentRequestsParams,\n MailboxClaimResult,\n MailboxDepositRequest,\n MailboxPage,\n PaymentRequestRecord,\n PaymentRequestsPage,\n RespondPaymentRequestInput,\n UploadUrlEntry,\n UploadUrlRequest,\n WakeCallback,\n WakeSocketHandle,\n WalletApiClientConfig,\n WalletApiIdentity,\n WebSocketFactoryLike,\n} from './types';\n\ninterface LocalIntent {\n payload: string;\n status: 'open' | 'completed' | 'aborted';\n createdAt: number;\n /**\n * #516: set when the abort decision could NOT land on the server (dead\n * backend at send-failure cleanup). {@link WalletApiClient.resyncOpenIntents}\n * replays it (PUT + abort) so the server row converges to aborted instead of\n * resume re-executing a send the user watched fail.\n */\n abortPending?: boolean;\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname.endsWith('.localhost') ||\n /^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname) ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\n/** Enforce the §4 transport rule: non-loopback MUST be https. */\nfunction assertSecureBaseUrl(baseUrl: string): URL {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new WalletApiError(`Invalid wallet-api base URL: ${baseUrl}`, 'CONFIG');\n }\n if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopbackHost(url.hostname))) {\n throw new WalletApiError(\n 'wallet-api base URL must be https (plain http is allowed only on loopback — ARCHITECTURE §4)',\n 'CONFIG'\n );\n }\n return url;\n}\n\n/** Platform-neutral bytes→base64 (browser btoa + Node ≥16 global btoa). */\nfunction bytesToBase64(bytes: Uint8Array): string {\n let bin = '';\n for (const b of bytes) bin += String.fromCharCode(b);\n return btoa(bin);\n}\n\nfunction defaultWebSocketFactory(url: string): import('./types').WebSocketLike {\n const Ctor = (globalThis as { WebSocket?: new (url: string) => import('./types').WebSocketLike }).WebSocket;\n if (!Ctor) {\n throw new WalletApiError(\n 'No global WebSocket available — inject webSocketFactory (e.g. the \"ws\" package on Node < 22)',\n 'CONFIG'\n );\n }\n return new Ctor(url);\n}\n\nexport class WalletApiClient {\n /** Network name (also the blob-key prefix `<network>/t/<sha256>` — §5.2). */\n readonly network: string;\n\n private readonly baseUrl: string;\n private readonly deviceId: string;\n private readonly storage: KeyValueStore;\n private readonly fetchFn: FetchLike;\n private readonly wsFactory: WebSocketFactoryLike;\n private readonly now: () => number;\n\n private identity: WalletApiIdentity | null = null;\n private jwt: string | null = null;\n /** Serializes concurrent re-auth attempts. */\n private authInFlight: Promise<void> | null = null;\n\n constructor(config: WalletApiClientConfig) {\n const url = assertSecureBaseUrl(config.baseUrl);\n this.baseUrl = url.toString().replace(/\\/$/, '');\n if (!config.network) throw new WalletApiError('network is required', 'CONFIG');\n this.network = config.network;\n this.deviceId = config.deviceId;\n this.storage = config.storage;\n if (!config.fetchFn && !(globalThis as { fetch?: FetchLike }).fetch) {\n throw new WalletApiError('No fetch available — inject fetchFn', 'CONFIG');\n }\n // Resolve the global lazily THROUGH globalThis so the implementation keeps\n // its original receiver (browser fetch throws \"Illegal invocation\" when\n // called detached).\n this.fetchFn =\n config.fetchFn ?? ((u, init) => (globalThis as unknown as { fetch: FetchLike }).fetch(u, init));\n this.wsFactory = config.webSocketFactory ?? defaultWebSocketFactory;\n this.now = config.now ?? (() => Date.now());\n }\n\n /** Bind the wallet identity this client authenticates as. Resets the session. */\n setIdentity(identity: WalletApiIdentity): void {\n this.identity = identity;\n this.jwt = null;\n }\n\n // ── storage keys (scoped per network + pubkey + device) ────────────────────\n\n private requireIdentity(): WalletApiIdentity {\n if (!this.identity) throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n return this.identity;\n }\n\n private scopedKey(kind: string): string {\n const { chainPubkey } = this.requireIdentity();\n return `wallet-api:${kind}:${this.network}:${chainPubkey}`;\n }\n\n private refreshTokenKey(): string {\n return `${this.scopedKey('refresh')}:${this.deviceId}`;\n }\n\n // ── auth (ARCHITECTURE §4) ──────────────────────────────────────────────────\n\n /**\n * Establish a session: try the stored refresh token first (rotating), fall\n * back to a fresh challenge→sign→verify cycle. Safe to call repeatedly.\n */\n async signIn(): Promise<void> {\n if (this.authInFlight) return this.authInFlight;\n this.authInFlight = this.signInInner().finally(() => {\n this.authInFlight = null;\n });\n return this.authInFlight;\n }\n\n private async signInInner(): Promise<void> {\n if (await this.tryRefresh()) return;\n await this.challengeSignIn();\n }\n\n /**\n * `POST /v1/auth/refresh` with the stored token; rotates on success. Any\n * 4xx (expired, revoked, rotation-reuse revocation) clears the stored token\n * and reports `false` — the caller falls back to a challenge cycle.\n */\n private async tryRefresh(): Promise<boolean> {\n const stored = await this.storage.get(this.refreshTokenKey());\n if (!stored) return false;\n const res = await this.rawFetch('POST', '/v1/auth/refresh', { refreshToken: stored });\n if (res.status === 200) {\n const tokens = parseAuthTokens(await this.readJson(res, 'auth/refresh'));\n this.jwt = tokens.jwt;\n await this.storage.set(this.refreshTokenKey(), tokens.refreshToken);\n return true;\n }\n // Stale/revoked/reused token — discard it; a fresh challenge cycle follows.\n await this.storage.remove(this.refreshTokenKey());\n return false;\n }\n\n /** The challenge→verify cycle (§4 steps 1–3) with template verification (S1). */\n private async challengeSignIn(): Promise<void> {\n const identity = this.requireIdentity();\n const challengeRes = await this.rawFetch('POST', '/v1/auth/challenge', {\n pubkey: identity.chainPubkey,\n });\n if (challengeRes.status !== 200) throw await this.toError(challengeRes, 'auth/challenge');\n const { nonce, challenge } = parseAuthChallenge(await this.readJson(challengeRes, 'auth/challenge'));\n\n // The spend key never signs unverified server-chosen text (S1).\n verifyChallengeTemplate(challenge, {\n pubkey: identity.chainPubkey,\n nonce,\n network: this.network,\n nowMs: this.now(),\n });\n\n const signature = signMessage(identity.privateKey, challenge);\n const verifyRes = await this.rawFetch('POST', '/v1/auth/verify', {\n nonce,\n signature,\n deviceId: this.deviceId,\n });\n if (verifyRes.status !== 200) throw await this.toError(verifyRes, 'auth/verify');\n const tokens = parseAuthTokens(await this.readJson(verifyRes, 'auth/verify'));\n this.jwt = tokens.jwt;\n await this.storage.set(this.refreshTokenKey(), tokens.refreshToken);\n }\n\n /** Revoke the session server-side and drop all local credentials. */\n async logout(): Promise<void> {\n if (this.jwt) {\n try {\n await this.rawFetch('POST', '/v1/auth/logout', {}, this.jwt);\n } finally {\n this.jwt = null;\n }\n }\n await this.storage.remove(this.refreshTokenKey());\n }\n\n // ── request core ────────────────────────────────────────────────────────────\n\n private async rawFetch(\n method: string,\n path: string,\n body?: unknown,\n jwt?: string\n ): Promise<FetchResponseLike> {\n const headers: Record<string, string> = {};\n if (body !== undefined) headers['content-type'] = 'application/json';\n if (jwt) headers['authorization'] = `Bearer ${jwt}`;\n try {\n return await this.fetchFn(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n } catch (err) {\n throw new WalletApiError(`wallet-api request failed: ${method} ${path}`, 'NETWORK', undefined, err);\n }\n }\n\n private async readJson(res: FetchResponseLike, what: string): Promise<unknown> {\n const text = await res.text();\n if (text === '') return null;\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new WalletApiError(`${what}: response is not JSON`, 'PROTOCOL', res.status, err);\n }\n }\n\n private async toError(res: FetchResponseLike, what: string): Promise<WalletApiError> {\n let message = `${what} failed with status ${res.status}`;\n try {\n const body = (await this.readJson(res, what)) as { error?: { code?: string; message?: string } } | null;\n if (body?.error?.message) message = `${what}: ${body.error.code ?? ''} ${body.error.message}`.trim();\n } catch {\n // keep the generic message\n }\n return WalletApiError.fromStatus(res.status, message);\n }\n\n /**\n * Authenticated JSON request. A 401 triggers one silent re-auth\n * (refresh → challenge fallback) and one retry.\n */\n private async requestJson(method: string, path: string, body?: unknown): Promise<unknown> {\n if (!this.jwt) await this.signIn();\n let res = await this.rawFetch(method, path, body, this.jwt!);\n if (res.status === 401) {\n this.jwt = null;\n await this.signIn();\n res = await this.rawFetch(method, path, body, this.jwt!);\n }\n if (res.status === 204) return null;\n if (!res.ok) throw await this.toError(res, `${method} ${path}`);\n return this.readJson(res, `${method} ${path}`);\n }\n\n // ── inventory / blobs (§16) ─────────────────────────────────────────────────\n\n /** `GET /v1/inventory?since=` — one page; the caller loops while `more`. */\n async listInventory(since?: bigint): Promise<InventoryPage> {\n const query = since !== undefined ? `?since=${since.toString()}` : '';\n const page = parseInventoryPage(await this.requestJson('GET', `/v1/inventory${query}`));\n await this.noteSyncEpoch(page.syncEpoch);\n return page;\n }\n\n /** `GET /v1/balances` — active rows only. */\n async getBalances(): Promise<CoinBalance[]> {\n return parseBalances(await this.requestJson('GET', '/v1/balances'));\n }\n\n /** `POST /v1/tokens/blob-urls` — owner's active *or tombstoned* rows (§5.3 recovery). */\n async getBlobUrls(tokenIds: string[]): Promise<BlobUrlEntry[]> {\n return parseBlobUrls(await this.requestJson('POST', '/v1/tokens/blob-urls', { tokenIds }));\n }\n\n /** `POST /v1/tokens/upload-urls` — checksum/length-bound presigned PUTs (§5.2). */\n async getUploadUrls(blobs: UploadUrlRequest[]): Promise<UploadUrlEntry[]> {\n return parseUploadUrls(await this.requestJson('POST', '/v1/tokens/upload-urls', { blobs }));\n }\n\n /**\n * `POST /v1/inventory/apply` (§5.3). Also marks the local intent copy\n * completed — the server completes the intent in the same transaction (§16).\n */\n async applyInventoryDelta(req: ApplyDeltaRequest): Promise<bigint> {\n const cursor = parseApplyResult(\n await this.requestJson('POST', '/v1/inventory/apply', {\n transferId: req.transferId,\n spent: req.spent,\n added: req.added,\n ...(req.externalDelivery !== undefined ? { externalDelivery: req.externalDelivery } : {}),\n })\n );\n await this.setLocalIntentStatus(req.transferId, 'completed');\n return cursor;\n }\n\n /** Download blob bytes from a signed GET URL (the URL itself is the credential). */\n async fetchBlob(getUrl: string): Promise<Uint8Array> {\n let res: FetchResponseLike;\n try {\n res = await this.fetchFn(getUrl, { method: 'GET' });\n } catch (err) {\n throw new WalletApiError('blob download failed', 'NETWORK', undefined, err);\n }\n if (!res.ok) throw WalletApiError.fromStatus(res.status, `blob download failed with status ${res.status}`);\n return new Uint8Array(await res.arrayBuffer());\n }\n\n /**\n * Upload blob bytes to a signed PUT URL. A `412 Precondition Failed` means\n * the identical blob already exists (content addressing, `If-None-Match: *`\n * — §5.2) and is treated as success.\n *\n * The §5.2 presign binds `x-amz-checksum-sha256` and `if-none-match` (plus\n * `content-length`) as SIGNED HEADERS — a real S3 endpoint rejects the\n * SigV4 signature unless the uploader sends them verbatim. (Caught by the\n * phase-2 harness on first contact with real MinIO — the in-process fake\n * had only validated the body, not the signed headers.)\n */\n async uploadBlob(putUrl: string, bytes: Uint8Array): Promise<void> {\n let res: FetchResponseLike;\n try {\n res = await this.fetchFn(putUrl, {\n method: 'PUT',\n headers: {\n 'content-type': 'application/octet-stream',\n 'x-amz-checksum-sha256': bytesToBase64(sha256(bytes)),\n 'if-none-match': '*',\n },\n body: bytes,\n });\n } catch (err) {\n throw new WalletApiError('blob upload failed', 'NETWORK', undefined, err);\n }\n if (res.ok || res.status === 412) return;\n throw WalletApiError.fromStatus(res.status, `blob upload failed with status ${res.status}`);\n }\n\n // ── mailbox (§6/§16) ────────────────────────────────────────────────────────\n\n /**\n * `POST /v1/mailbox` — deposit an already-uploaded finished blob to the\n * recipient's mailbox. Idempotent by content-derived `entry_id` (§6): an\n * existing entry in any status returns `200` with its id, provided the\n * request's recipient and key match the stored entry (a mismatch is `409`).\n */\n async depositMailbox(req: MailboxDepositRequest): Promise<string> {\n return parseDepositResult(\n await this.requestJson('POST', '/v1/mailbox', {\n recipientPubkey: req.recipientPubkey,\n key: req.key,\n transferId: req.transferId,\n stateHash: req.stateHash,\n tokenId: req.tokenId,\n ...(req.memo !== undefined ? { memo: req.memo } : {}),\n })\n );\n }\n\n /**\n * `GET /v1/mailbox?since=<seq>` — entries of EVERY status are listable for\n * any client-chosen `since`; a claimed entry carries a working `getUrl`\n * while its blob is within retention, `blobCollected: true` afterwards (§6).\n */\n async listMailbox(since?: bigint): Promise<MailboxPage> {\n const query = since !== undefined ? `?since=${since.toString()}` : '';\n const page = parseMailboxPage(await this.requestJson('GET', `/v1/mailbox${query}`));\n await this.noteSyncEpoch(page.syncEpoch);\n return page;\n }\n\n /**\n * `POST /v1/mailbox/claim` — addressee-only, idempotent ownership handoff\n * (§6). `intoInventory:false` is the delivery-only claim: the entry resolves\n * and the pointer advances with ZERO inventory writes.\n */\n async claimMailbox(entryIds: string[], intoInventory: boolean): Promise<MailboxClaimResult> {\n return parseClaimResult(\n await this.requestJson('POST', '/v1/mailbox/claim', { entryIds, intoInventory })\n );\n }\n\n /**\n * `POST /v1/mailbox/reject` — addressee-only; terminal for DISCOVERY only:\n * the entry counts toward read-pointer contiguity but remains claimable and\n * its blob is retained (§6 — reject is never a destruction path).\n */\n async rejectMailbox(entryIds: string[]): Promise<string[]> {\n return parseRejectResult(await this.requestJson('POST', '/v1/mailbox/reject', { entryIds }));\n }\n\n // ── history (§10/§16) ───────────────────────────────────────────────────────\n\n /**\n * `POST /v1/history` — client-asserted records, deduped by `dedupKey` (the\n * server never writes history rows — §10). `memo`/`counterpartyNametag`\n * MUST already be S6 envelopes.\n */\n async postHistoryRecords(records: HistoryWireRecord[]): Promise<void> {\n await this.requestJson('POST', '/v1/history', { records });\n }\n\n /** `GET /v1/history?before=&limit=` — newest-first keyset pages (§10). */\n async listHistory(options: { before?: string; limit?: number } = {}): Promise<HistoryPage> {\n const params = new URLSearchParams();\n if (options.before !== undefined) params.set('before', options.before);\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n const qs = params.toString();\n return parseHistoryPage(await this.requestJson('GET', `/v1/history${qs ? `?${qs}` : ''}`));\n }\n\n // ── payment requests (§10/§16) ──────────────────────────────────────────────\n\n /**\n * `POST /v1/payment-requests` — create a request addressed to a payer (the\n * payer's account is auto-provisioned even if they never authenticated —\n * §4/§9; the per-payer open cap → 429, §5.5). `memo` MUST already be an S6\n * `enc1.` envelope (§8.3) — only the requester's wallet key decrypts it.\n */\n async createPaymentRequest(input: CreatePaymentRequestInput): Promise<PaymentRequestRecord> {\n return parsePaymentRequestRecord(\n await this.requestJson('POST', '/v1/payment-requests', {\n toPubkey: input.toPubkey,\n // Amounts are decimal strings in every JSON body (§11).\n assets: input.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),\n ...(input.memo !== undefined ? { memo: input.memo } : {}),\n ...(input.expiresAt !== undefined ? { expiresAt: new Date(input.expiresAt).toISOString() } : {}),\n }),\n 'payment-request create response'\n );\n }\n\n /**\n * `GET /v1/payment-requests?role=…` (§16): `'incoming'` is the payer's\n * gap-free `?since=<seq>` stream (cursor = bigint, the standard §16 since\n * contract); `'outgoing'` is the requester's newest-first\n * `?before=<opaque keyset>` backfill (cursor = string | null). The two\n * cursor families never mix — the server 422s a mismatched parameter, and\n * the parameter types make it unrepresentable here.\n */\n async listPaymentRequests<R extends 'incoming' | 'outgoing'>(\n params: Extract<ListPaymentRequestsParams, { role: R }>\n ): Promise<Extract<PaymentRequestsPage, { role: R }>> {\n // Widened to the plain union — generic Extract<> members don't narrow.\n const p: ListPaymentRequestsParams = params;\n const query = new URLSearchParams();\n query.set('role', p.role);\n if (p.status !== undefined) query.set('status', p.status);\n if (p.role === 'incoming') {\n if (p.since !== undefined) query.set('since', p.since.toString());\n } else if (p.before !== undefined) {\n query.set('before', p.before);\n }\n const page = parsePaymentRequestsPage(\n await this.requestJson('GET', `/v1/payment-requests?${query.toString()}`),\n p.role\n );\n await this.noteSyncEpoch(page.syncEpoch);\n // The codec already discriminated the page by role; Extract<> re-states\n // the role-bound cursor family to the caller.\n return page as Extract<PaymentRequestsPage, { role: R }>;\n }\n\n /**\n * `POST /v1/payment-requests/{id}/respond` — addressee-only (§10, non-\n * addressee → 403); only an `open` request may be responded to (else 409).\n * `paid` REQUIRES and links the fulfilling send's transferId, `declined`\n * carries none — the pairing is enforced by {@link RespondPaymentRequestInput}.\n */\n async respondPaymentRequest(id: string, response: RespondPaymentRequestInput): Promise<PaymentRequestRecord> {\n return parsePaymentRequestRecord(\n await this.requestJson('POST', `/v1/payment-requests/${id}/respond`, response),\n 'payment-request respond response'\n );\n }\n\n // ── intents (E.3) ───────────────────────────────────────────────────────────\n\n private intentsKey(): string {\n return this.scopedKey('intents');\n }\n\n private async readLocalIntents(): Promise<Record<string, LocalIntent>> {\n const raw = await this.storage.get(this.intentsKey());\n if (!raw) return {};\n try {\n return JSON.parse(raw) as Record<string, LocalIntent>;\n } catch {\n return {};\n }\n }\n\n private async writeLocalIntents(intents: Record<string, LocalIntent>): Promise<void> {\n await this.storage.set(this.intentsKey(), JSON.stringify(intents));\n }\n\n private async setLocalIntentStatus(\n transferId: string,\n status: LocalIntent['status'],\n opts: { abortPending?: boolean } = {}\n ): Promise<void> {\n const intents = await this.readLocalIntents();\n const intent = intents[transferId];\n if (!intent) return;\n if (intent.status === 'completed') return; // completed never reverts (§16)\n const abortPending = opts.abortPending === true;\n if (intent.status === status && (intent.abortPending === true) === abortPending) return;\n intent.status = status;\n if (abortPending) intent.abortPending = true;\n else delete intent.abortPending;\n await this.writeLocalIntents(intents);\n }\n\n /**\n * Persist a (client-encrypted — S6) intent payload: LOCAL copy first, then\n * `PUT /v1/intents/{transferId}` and await the server ack (E.3 — the engine\n * MUST NOT be called before this resolves). A failed server PUT throws, but\n * the local copy stays as the restore backstop and is re-PUT by\n * {@link resyncOpenIntents}.\n */\n async putIntent(transferId: string, payloadEnvelope: string): Promise<void> {\n const intents = await this.readLocalIntents();\n if (!intents[transferId]) {\n intents[transferId] = { payload: payloadEnvelope, status: 'open', createdAt: this.now() };\n await this.writeLocalIntents(intents);\n }\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: payloadEnvelope });\n }\n\n /** `GET /v1/intents?status=` — server-side intent list. */\n async listIntents(status: 'open' | 'aborted'): Promise<IntentRecord[]> {\n return parseIntents(await this.requestJson('GET', `/v1/intents?status=${status}`));\n }\n\n /**\n * `POST /v1/intents/{id}/abort` — soft, recoverable (§16).\n *\n * #516: the LOCAL copy flips to 'aborted' even when the server abort cannot\n * land (dead backend) — leaving it 'open' would make {@link resyncOpenIntents}\n * re-PUT it and the next sign-in's `resumeOpenIntents` RE-EXECUTE a send the\n * user watched fail (and possibly retried manually): a double-pay hazard.\n * An unlanded abort is marked pending and replayed by\n * {@link resyncOpenIntents} (PUT + abort) so the server converges too.\n */\n async abortIntent(transferId: string): Promise<void> {\n try {\n await this.requestJson('POST', `/v1/intents/${transferId}/abort`);\n } catch (err) {\n await this.setLocalIntentStatus(transferId, 'aborted', { abortPending: true });\n throw err;\n }\n await this.setLocalIntentStatus(transferId, 'aborted');\n }\n\n /** `POST /v1/intents/{id}/complete` — the uniform client-side close (E.3). */\n async completeIntent(transferId: string): Promise<void> {\n await this.requestJson('POST', `/v1/intents/${transferId}/complete`);\n await this.setLocalIntentStatus(transferId, 'completed');\n }\n\n /** The locally-known open intents (the E.3 restore backstop). */\n async listLocalOpenIntents(): Promise<IntentRecord[]> {\n const intents = await this.readLocalIntents();\n return Object.entries(intents)\n .filter(([, i]) => i.status === 'open')\n .map(([transferId, i]) => ({ transferId, payload: i.payload, status: 'open' as const, createdAt: i.createdAt }));\n }\n\n /**\n * Re-PUT every locally-known open intent (idempotent — the server PUT is\n * write-once while open/completed). Called after a `syncEpoch` change\n * (server restore — §5.4): intents are the one server table not\n * re-derivable from blobs.\n *\n * #516: also replays locally-aborted intents whose abort never landed on\n * the server (`abortPending`) — PUT (no-op while open/completed; (re)creates\n * the row after a restore or a failed original PUT) then abort, so the\n * server row lands as 'aborted' instead of an 'open' intent that resume\n * would re-execute. Completed-wins is preserved server-side (§16).\n */\n async resyncOpenIntents(): Promise<void> {\n const intents = await this.readLocalIntents();\n for (const [transferId, intent] of Object.entries(intents)) {\n if (intent.status === 'open') {\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: intent.payload });\n } else if (intent.status === 'aborted' && intent.abortPending === true) {\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: intent.payload });\n await this.requestJson('POST', `/v1/intents/${transferId}/abort`);\n await this.setLocalIntentStatus(transferId, 'aborted'); // clears abortPending\n }\n }\n }\n\n // ── syncEpoch (§5.4/§9) ─────────────────────────────────────────────────────\n\n private syncEpochKey(): string {\n return this.scopedKey('syncEpoch');\n }\n\n /**\n * Track the server `syncEpoch` carried by cursor-bearing responses and\n * wakes. On a change (server restore), re-PUT locally-known open intents\n * before anything resumes (E.3). Storage-provider cursor invalidation is\n * the provider's own duty (S2) — it observes the same epoch values.\n */\n private async noteSyncEpoch(epoch: bigint): Promise<void> {\n const stored = await this.storage.get(this.syncEpochKey());\n if (stored === epoch.toString()) return;\n await this.storage.set(this.syncEpochKey(), epoch.toString());\n if (stored !== null) {\n await this.resyncOpenIntents();\n }\n }\n\n // ── realtime (§9) ───────────────────────────────────────────────────────────\n\n /**\n * Open the wake channel: `POST /v1/ws-ticket` (JWT-authed, single-use,\n * short TTL) then `GET /v1/ws?ticket=…` — the JWT never appears in a URL.\n * Wakes are nudges only; correctness comes from the `?since=` cursors.\n */\n async connectWakeSocket(onWake: WakeCallback): Promise<WakeSocketHandle> {\n const ticket = parseWsTicket(await this.requestJson('POST', '/v1/ws-ticket'));\n const wsBase = this.baseUrl.replace(/^http/, 'ws');\n const ws = this.wsFactory(`${wsBase}/v1/ws?ticket=${encodeURIComponent(ticket)}`);\n\n await new Promise<void>((resolve, reject) => {\n ws.onopen = () => resolve();\n ws.onerror = (err) => reject(new WalletApiError('wake socket failed to connect', 'NETWORK', undefined, err));\n ws.onclose = () => reject(new WalletApiError('wake socket closed before connecting', 'NETWORK'));\n });\n\n ws.onerror = null;\n ws.onclose = null;\n ws.onmessage = (ev) => {\n const frame = parseWakeFrame(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (!frame) return;\n void this.noteSyncEpoch(frame.syncEpoch);\n onWake(frame);\n };\n\n return { close: () => ws.close() };\n }\n}\n","/**\n * WalletApiTokenStorageProvider — the lazy, thin-wallet storage provider over\n * the wallet-api backend (sdk-changes S2; ARCHITECTURE §5/§16).\n *\n * Platform-neutral (browser + Node) over the injected {@link WalletApiClient}.\n * The wallet renders balances from the server's value-indexed inventory view;\n * blobs are fetched on demand only to spend (signed GET URLs). Key behaviors:\n *\n * - **Tombstone-aware delta sync** (§5.1): `?since=` deltas include\n * `status:'removed'` rows — the only way a stale device learns about\n * spends/handoffs; the provider applies them (drops the entries from its\n * active view) and loops while `more`.\n * - **Paginated full pull** is finished with an immediate\n * `?since=<page-1 cursor>` closing delta, whose tombstones repair any flips\n * that happened between pages (§5.1).\n * - **`syncEpoch` change** (server restore — §5.4): discard all persisted\n * cursors, full pull, then re-PUT locally-known open intents (E.3, via the\n * client) before anything resumes.\n * - **Write-behind with empty-import protection** (§5.1 client guards): a\n * removal is pushed only after a successful inventory load and only for a\n * confirmed on-chain spend (a `_tombstones` entry) — a fresh device or a\n * failed load can never appear to \"empty\" the wallet, and a merely-absent\n * token is never removed.\n * - **`recoverRemoved()`** (§5.3 recovery): tombstones the client cannot match\n * to a known spend are re-fetched via blob-urls (which work for own\n * tombstoned rows), re-verified locally, and re-added (reactivation). A\n * server `409` is an evidenced tombstone — \"actually spent\" — and is kept.\n * - **Blob upload** via upload-urls with client-side sha256; a `412` from the\n * content-addressed store means the blob already exists = success (§5.2).\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport type {\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n InventoryItem,\n InventoryView,\n LoadResult,\n RecoverRemovedResult,\n SaveResult,\n SyncResult,\n TokenStorageProvider,\n TxfStorageDataBase,\n} from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport {\n TOKEN_BLOB_VERSION,\n decodeTokenBlob,\n unwrapTokenBlobBytes,\n} from '../../../token-engine/token-blob';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport { WalletApiClient, WalletApiError } from '../../../wallet-api';\nimport type { KeyValueStore } from '../../../wallet-api';\n\nexport interface WalletApiTokenStorageConfig {\n /** The authenticated wallet-api client (S1). DI — never a singleton. */\n client: WalletApiClient;\n /** Persists the inventory cursor, syncEpoch and own-spend set per identity. */\n stateStore: KeyValueStore;\n /**\n * Optional local token verification used by `recoverRemoved()` before\n * re-adding a tombstoned blob (S2: \"re-verifies locally\"). Wire the engine's\n * `verify` here at composition; the default accepts any blob that decodes\n * and matches its tokenId.\n */\n verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nfunction newTransferId(): string {\n return (globalThis as { crypto: { randomUUID(): string } }).crypto.randomUUID();\n}\n\n/** A stored v2 token entry (the UI token record with a hex CBOR blob). */\nfunction isV2BlobEntry(entry: unknown): entry is { sdkData: string } {\n if (typeof entry !== 'object' || entry === null || 'genesis' in entry) return false;\n const sdkData = (entry as { sdkData?: unknown }).sdkData;\n return (\n typeof sdkData === 'string' &&\n sdkData.length >= 2 &&\n sdkData.length % 2 === 0 &&\n /^[0-9a-f]+$/i.test(sdkData)\n );\n}\n\nexport class WalletApiTokenStorageProvider implements TokenStorageProvider<TxfStorageDataBase> {\n readonly id = 'wallet-api-token-storage';\n readonly name = 'Wallet API Token Storage';\n readonly type = 'cloud' as const;\n /** Custody lives in the wallet-api backend — composing this provider without\n * the wallet-api client is illegal (fail-closed, S7/#515). */\n readonly requiresWalletApi = true;\n\n private readonly client: WalletApiClient;\n private readonly stateStore: KeyValueStore;\n private readonly verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n\n /** Local mirror of the inventory view — active rows and tombstones. */\n private readonly view = new Map<string, InventoryItem>();\n /**\n * Empty-import protection (§5.1): no removal is ever pushed before this\n * flips on the first successful inventory load.\n */\n private hadSuccessfulLoad = false;\n\n constructor(config: WalletApiTokenStorageConfig) {\n this.client = config.client;\n this.stateStore = config.stateStore;\n this.verifyToken = config.verifyToken;\n }\n\n // ── provider lifecycle ──────────────────────────────────────────────────────\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.client.setIdentity({ privateKey: identity.privateKey, chainPubkey: identity.chainPubkey });\n this.view.clear();\n this.hadSuccessfulLoad = false;\n }\n\n async initialize(): Promise<boolean> {\n this.status = 'connected';\n return true;\n }\n\n async shutdown(): Promise<void> {\n this.status = 'disconnected';\n }\n\n async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ── persisted sync state (per network + identity) ───────────────────────────\n\n private stateKey(kind: string): string {\n if (!this.identity) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n return `wallet-api-storage:${kind}:${this.client.network}:${this.identity.chainPubkey}`;\n }\n\n private async readCursor(): Promise<bigint | null> {\n const raw = await this.stateStore.get(this.stateKey('cursor'));\n return raw !== null && /^[0-9]+$/.test(raw) ? BigInt(raw) : null;\n }\n\n private async readSyncEpoch(): Promise<bigint | null> {\n const raw = await this.stateStore.get(this.stateKey('syncEpoch'));\n return raw !== null && /^[0-9]+$/.test(raw) ? BigInt(raw) : null;\n }\n\n private async persistSyncState(cursor: bigint, syncEpoch: bigint): Promise<void> {\n await this.stateStore.set(this.stateKey('cursor'), cursor.toString());\n await this.stateStore.set(this.stateKey('syncEpoch'), syncEpoch.toString());\n }\n\n /** TokenIds this provider itself spent — `recoverRemoved()` skips these. */\n private async readKnownSpends(): Promise<Set<string>> {\n const raw = await this.stateStore.get(this.stateKey('knownSpends'));\n if (!raw) return new Set();\n try {\n return new Set(JSON.parse(raw) as string[]);\n } catch {\n return new Set();\n }\n }\n\n private async addKnownSpends(tokenIds: string[]): Promise<void> {\n if (tokenIds.length === 0) return;\n const known = await this.readKnownSpends();\n for (const id of tokenIds) known.add(id);\n await this.stateStore.set(this.stateKey('knownSpends'), JSON.stringify([...known]));\n }\n\n // ── inventory sync (§5.1) ───────────────────────────────────────────────────\n\n private applyItems(items: InventoryItem[]): void {\n for (const item of items) {\n // Tombstones drop the entry from the active view but stay recorded —\n // recoverRemoved() needs them (§5.3 recovery).\n this.view.set(item.tokenId, item);\n }\n }\n\n /** Full pull + the §5.1 closing delta; replaces the whole local view. */\n private async fullPull(): Promise<void> {\n this.view.clear();\n let page = await this.client.listInventory(undefined);\n const firstPageCursor = page.cursor;\n this.applyItems(page.items);\n while (page.more) {\n page = await this.client.listInventory(page.cursor);\n this.applyItems(page.items);\n }\n // Closing delta from the page-1 cursor: its tombstones repair any flips\n // that happened between pages (§5.1 — a paginated full pull spans snapshots).\n let delta = await this.client.listInventory(firstPageCursor);\n this.applyItems(delta.items);\n while (delta.more) {\n delta = await this.client.listInventory(delta.cursor);\n this.applyItems(delta.items);\n }\n await this.persistSyncState(delta.cursor, delta.syncEpoch);\n }\n\n /**\n * Delta loop from the persisted cursor. Returns `true` when the server's\n * `syncEpoch` no longer matches the persisted one — the cursors are invalid\n * (server restore, §5.4) and the caller must resync from scratch.\n */\n private async deltaLoop(since: bigint): Promise<boolean> {\n const knownEpoch = await this.readSyncEpoch();\n let page = await this.client.listInventory(since);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) return true;\n this.applyItems(page.items);\n while (page.more) {\n page = await this.client.listInventory(page.cursor);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) return true;\n this.applyItems(page.items);\n }\n await this.persistSyncState(page.cursor, page.syncEpoch);\n return false;\n }\n\n /** §5.4: discard cursors → full pull → re-PUT locally-known open intents. */\n private async handleSyncEpochChange(): Promise<void> {\n await this.stateStore.remove(this.stateKey('cursor'));\n await this.stateStore.remove(this.stateKey('syncEpoch'));\n await this.fullPull();\n await this.client.resyncOpenIntents();\n }\n\n /** Converge the local view with the server (delta when possible). */\n private async syncInventory(): Promise<void> {\n const cursor = await this.readCursor();\n if (cursor === null) {\n await this.fullPull();\n } else if (await this.deltaLoop(cursor)) {\n await this.handleSyncEpochChange();\n }\n this.hadSuccessfulLoad = true;\n }\n\n // ── lazy inventory port (S2) ────────────────────────────────────────────────\n\n /**\n * Without `since`: converge with the server, then return the full active\n * view (`more:false`) — a fresh device renders balances with zero blob\n * downloads. With `since`: a true server delta page (tombstones included),\n * also applied to the local view.\n */\n async listInventory(since?: bigint): Promise<InventoryView> {\n if (since === undefined) {\n await this.syncInventory();\n return this.snapshotView();\n }\n const knownEpoch = await this.readSyncEpoch();\n const page = await this.client.listInventory(since);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) {\n // The caller's cursor predates a server restore — resync and hand back\n // the converged snapshot instead of a meaningless delta (§5.4).\n await this.handleSyncEpochChange();\n this.hadSuccessfulLoad = true;\n return this.snapshotView();\n }\n this.applyItems(page.items);\n if (!page.more) {\n const stored = await this.readCursor();\n if (stored === null || page.cursor > stored) {\n await this.persistSyncState(page.cursor, page.syncEpoch);\n }\n }\n return page;\n }\n\n private async snapshotView(): Promise<InventoryView> {\n const items = [...this.view.values()].filter((i) => i.status === 'active');\n const cursor = (await this.readCursor()) ?? 0n;\n const syncEpoch = (await this.readSyncEpoch()) ?? 0n;\n return { cursor, syncEpoch, more: false, items };\n }\n\n /** Fetch + decode one blob on demand via a short-lived signed GET (§5.1). */\n async getToken(tokenId: string): Promise<TokenBlob> {\n const urls = await this.client.getBlobUrls([tokenId]);\n const entry = urls.find((u) => u.tokenId === tokenId);\n if (!entry) {\n throw new WalletApiError(`No blob URL for token ${tokenId} (not owned here)`, 'NOT_FOUND');\n }\n const bytes = await this.client.fetchBlob(entry.getUrl);\n const blob = this.wrapWireBlob(tokenId, bytes);\n if (blob.tokenId !== tokenId) {\n throw new WalletApiError(\n `Blob tokenId mismatch: requested ${tokenId}, blob carries ${blob.tokenId}`,\n 'PROTOCOL'\n );\n }\n return blob;\n }\n\n /**\n * The §16 wire serves RAW token bytes (§5.2/§8.2 — the sphere 39051\n * envelope never crosses the API): re-wrap them for the engine-facing\n * {@link TokenBlob} surface. Envelope bytes (older rows, fake-world seeds)\n * still decode — their embedded tokenId is then checked by the caller. The\n * `network` of a raw wrap is not recoverable without the engine; consumers\n * derive it from the decoded token (`engine.decodeToken` re-wraps), so it\n * is never read from this value.\n */\n private wrapWireBlob(tokenId: string, bytes: Uint8Array): TokenBlob {\n try {\n return decodeTokenBlob(bytes);\n } catch {\n return { v: TOKEN_BLOB_VERSION, network: 0, tokenId, token: bytes };\n }\n }\n\n /**\n * Record a spend (§5.3) — idempotent by `transferId` server-side. MUST be\n * called after the mailbox deposit of the same `transferId` (the backend\n * evidence-checks removals against it). Refreshes the local view from the\n * resulting delta.\n */\n async applyDelta(\n transferId: string,\n spent: string[],\n added: ApplyDeltaAdded[],\n opts?: ApplyDeltaOptions\n ): Promise<void> {\n await this.client.applyInventoryDelta({\n transferId,\n spent,\n added,\n ...(opts?.externalDelivery !== undefined ? { externalDelivery: opts.externalDelivery } : {}),\n });\n await this.addKnownSpends(spent);\n await this.syncInventory();\n }\n\n // ── recovery (§5.3, sdk-changes S2) ─────────────────────────────────────────\n\n /**\n * Re-add tombstoned tokens this client cannot match to a known spend (a\n * wiped view — stolen JWT or a buggy client). Blobs are retained server-side\n * and blob-urls works for own tombstoned rows; each candidate is re-fetched,\n * locally verified, and re-added (reactivation). A `409` is the server's\n * verdict that the tombstone is an evidenced spend — kept as spent.\n */\n async recoverRemoved(): Promise<RecoverRemovedResult> {\n await this.syncInventory();\n const knownSpends = await this.readKnownSpends();\n const result: RecoverRemovedResult = { recovered: [], spent: [], skipped: [] };\n\n const candidates = [...this.view.values()].filter(\n (i) => i.status === 'removed' && !knownSpends.has(i.tokenId)\n );\n for (const item of candidates) {\n const blobBytes = await this.fetchRemovedBlob(item.tokenId);\n if (blobBytes === null || !(await this.locallyValid(item.tokenId, blobBytes))) {\n result.skipped.push(item.tokenId);\n continue;\n }\n // Content-addressed key of the exact bytes we verified (§5.2).\n const key = `${this.client.network}/t/${bytesToHex(sha256(blobBytes))}`;\n try {\n await this.client.applyInventoryDelta({\n transferId: newTransferId(),\n spent: [],\n added: [{ tokenId: item.tokenId, key }],\n });\n result.recovered.push(item.tokenId);\n } catch (err) {\n if (err instanceof WalletApiError && err.code === 'CONFLICT') {\n // Evidenced tombstone (or the token moved on) — actually spent (§5.3).\n result.spent.push(item.tokenId);\n await this.addKnownSpends([item.tokenId]);\n } else {\n throw err;\n }\n }\n }\n\n await this.syncInventory();\n return result;\n }\n\n private async fetchRemovedBlob(tokenId: string): Promise<Uint8Array | null> {\n const urls = await this.client.getBlobUrls([tokenId]);\n const entry = urls.find((u) => u.tokenId === tokenId);\n if (!entry) return null;\n try {\n return await this.client.fetchBlob(entry.getUrl);\n } catch {\n return null;\n }\n }\n\n private async locallyValid(tokenId: string, bytes: Uint8Array): Promise<boolean> {\n const blob = this.wrapWireBlob(tokenId, bytes);\n if (blob.tokenId !== tokenId) return false;\n if (this.verifyToken) return this.verifyToken(blob);\n return true;\n }\n\n // ── whole-blob surface (thin: the hot path is the lazy port above) ──────────\n\n /**\n * Thin view: converges with the server and reports tombstones, but carries\n * NO token entries — blobs are fetched on demand via `getToken()`\n * (sdk-changes S2: `load()` no longer eagerly pulls every blob).\n */\n async load(): Promise<LoadResult<TxfStorageDataBase>> {\n try {\n await this.syncInventory();\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'inventory sync failed',\n source: 'remote',\n timestamp: Date.now(),\n };\n }\n const data: TxfStorageDataBase = {\n _meta: {\n version: 1,\n address: this.identity?.l1Address ?? '',\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n _tombstones: [...this.view.values()]\n .filter((i) => i.status === 'removed')\n .map((i) => ({ tokenId: i.tokenId, stateHash: '', timestamp: Date.now() })),\n };\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n }\n\n /**\n * Write-behind push of a whole-blob snapshot:\n * - unknown tokens are uploaded (content-addressed, 412 = already present)\n * and added via one idempotent apply;\n * - removals are pushed ONLY for confirmed spends (`_tombstones` entries)\n * and ONLY after a successful inventory load (empty-import protection,\n * §5.1) — a merely-absent token is never removed.\n * A failed sync fails the save: pushing against an unknown server view\n * could only do harm.\n */\n async save(data: TxfStorageDataBase): Promise<SaveResult> {\n try {\n await this.syncInventory();\n const tombstoneIds = new Set((data._tombstones ?? []).map((t) => t.tokenId));\n await this.pushAdditions(data, tombstoneIds);\n await this.pushRemovals(tombstoneIds);\n await this.syncInventory();\n return { success: true, timestamp: Date.now() };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'wallet-api save failed',\n timestamp: Date.now(),\n };\n }\n }\n\n private async pushAdditions(data: TxfStorageDataBase, excluded: Set<string>): Promise<void> {\n const toAdd: { tokenId: string; bytes: Uint8Array }[] = [];\n for (const [key, value] of Object.entries(data)) {\n if (!/^_[0-9a-f]{4,}$/i.test(key) || !isV2BlobEntry(value)) continue;\n const tokenId = key.slice(1);\n // Skip tokens the server already knows: an equal-state re-push of an\n // active row is a lineage 409 (§5.3), and tombstoned tokens re-enter\n // via recoverRemoved(), never via write-behind.\n if (excluded.has(tokenId) || this.view.has(tokenId)) continue;\n // §5.2/§8.2: the wire carries the RAW token bytes — the stored sdkData\n // is the sphere envelope; unwrap it at the wallet-api boundary.\n toAdd.push({ tokenId, bytes: unwrapTokenBlobBytes(hexToBytes(value.sdkData)) });\n }\n if (toAdd.length === 0) return;\n\n const urls = await this.client.getUploadUrls(\n toAdd.map((t) => ({ sha256: bytesToHex(sha256(t.bytes)), size: t.bytes.length }))\n );\n const added: ApplyDeltaAdded[] = [];\n for (const token of toAdd) {\n const sha = bytesToHex(sha256(token.bytes));\n const url = urls.find((u) => u.sha256 === sha);\n if (!url) {\n throw new WalletApiError(`upload-urls response missing sha256 ${sha}`, 'PROTOCOL');\n }\n await this.client.uploadBlob(url.putUrl, token.bytes); // 412 = already present = success (§5.2)\n added.push({ tokenId: token.tokenId, key: url.key });\n }\n await this.client.applyInventoryDelta({ transferId: newTransferId(), spent: [], added });\n }\n\n private async pushRemovals(tombstoneIds: Set<string>): Promise<void> {\n // Empty-import protection (§5.1): never push a removal before the first\n // successful inventory load; a spent token is removed only against a\n // confirmed on-chain spend (a tombstone), never mere absence.\n if (!this.hadSuccessfulLoad || tombstoneIds.size === 0) return;\n const spent = [...tombstoneIds].filter((id) => this.view.get(id)?.status === 'active');\n if (spent.length === 0) return;\n await this.client.applyInventoryDelta({ transferId: newTransferId(), spent, added: [] });\n await this.addKnownSpends(spent);\n }\n\n async sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>> {\n const result = await this.save(localData);\n return {\n success: result.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: result.error,\n };\n }\n}\n\nexport function createWalletApiTokenStorageProvider(\n config: WalletApiTokenStorageConfig\n): WalletApiTokenStorageProvider {\n return new WalletApiTokenStorageProvider(config);\n}\n","/**\n * transport/delivery-provider.ts — the `DeliveryProvider` port (sdk-changes S7,\n * covenant §3.1-6).\n *\n * The seam that keeps the delivery rail swappable. In Unicity, a transfer —\n * after certification — is just a file handoff, so the port is deliberately\n * tiny: hand a finished token blob to a recipient, pull incoming deliveries,\n * acknowledge them. `WalletApiMailboxProvider`\n * (impl/shared/wallet-api/WalletApiMailboxProvider.ts) is the reference\n * implementation; anything that can move a file can implement it (the port\n * shape must not preclude the old Nostr transport or a future federated\n * transport — neither is a deliverable here).\n *\n * Normative shapes (sdk-changes S7):\n * - `DeliveryReceipt = { deliveryId }`\n * - `IncomingDelivery = { deliveryId, transferId?, senderPubkey?, memo?,\n * fetchBlob(), cursor }`\n * - `deliveryId` is the **content-derived** entry id —\n * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — NEVER a server-assigned\n * row id or seq (covenant §3.1-4; the contract suite asserts it). It is\n * computed client-side ({@link computeDeliveryId}) and must equal the\n * backend's `entry_id` (ARCHITECTURE §6).\n * - **Custody is a composition-time property, not a per-call flag**:\n * implementations take `custody: 'inventory' | 'external'` at construction\n * and every ack sends the corresponding `intoInventory` — delivery-only\n * safety must never depend on remembering an option at a call site.\n * - Implementations MUST keep a **persistent `(tokenId, stateHash)` seen-set**\n * for incoming deliveries: the recipient-side replay guard is part of the\n * port contract, not a server promise (the recipient never trusts the\n * backend — ARCHITECTURE §8.2). `deliveryId` is the canonical hash encoding\n * of exactly that pair, so a persistent deliveryId set satisfies this.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\n\n\n// =============================================================================\n// Normative shapes (sdk-changes S7)\n// =============================================================================\n\n/** Receipt for a delivered blob. `deliveryId` is content-derived — see module doc. */\nexport interface DeliveryReceipt {\n deliveryId: string;\n}\n\n/** Options for {@link DeliveryProvider.deliver}. */\nexport interface DeliverOptions {\n /**\n * The send's transferId (the E.3 intent id / realization seed). Recorded\n * with the delivery so the recipient can group multi-token payments and the\n * backend can evidence-check the sender's removals (ARCHITECTURE §5.3/§6).\n */\n transferId: string;\n /** Optional human memo. Implementations encrypt it client-side (S6). */\n memo?: string;\n}\n\n/** One incoming delivery pulled from the feed. */\nexport interface IncomingDelivery {\n /** Content-derived id — `hex(SHA-256(tokenId bytes ‖ stateHash bytes))`. */\n deliveryId: string;\n /** The sender's transferId, when the transport carries it. */\n transferId?: string;\n /** The sender's pubkey, when the transport carries it. */\n senderPubkey?: string;\n /** Decrypted memo (S6), when present and decryptable. */\n memo?: string;\n /** Fetch the finished token blob bytes (the encoded TokenBlob). */\n fetchBlob(): Promise<Uint8Array>;\n /** Transport-local resume cursor (opaque to callers). */\n cursor: string;\n}\n\nexport type DeliveryDisposition = 'claimed' | 'rejected';\n\n/**\n * Custody mode (composition-time): `'inventory'` — acknowledged deliveries\n * enter the wallet-api inventory (the full wallet-api preset); `'external'` —\n * the app's own storage keeps custody and acks perform ZERO inventory writes\n * (the delivery-only preset, ARCHITECTURE §6 \"delivery-only claim\").\n */\nexport type DeliveryCustody = 'inventory' | 'external';\n\n// =============================================================================\n// The port\n// =============================================================================\n\nexport interface DeliveryProvider {\n /** Composition-time custody property — never a per-call flag (S7). */\n readonly custody: DeliveryCustody;\n\n /**\n * Bind the wallet identity (optional — implementations that authenticate or\n * encrypt per-wallet need it; mirrors `TokenStorageProvider.setIdentity`).\n */\n setIdentity?(identity: { privateKey: string; chainPubkey: string }): void;\n\n /**\n * Hand a finished token blob to a recipient. `recipientPubkey` is the\n * recipient's CHAIN pubkey (33-byte compressed secp256k1, hex) — the\n * canonical Unicity identity (ARCHITECTURE §4); transports that address\n * recipients differently resolve it themselves.\n *\n * MUST be idempotent per (token, state): re-delivering the same finished\n * blob — including after the recipient claimed — succeeds and returns the\n * same content-derived `deliveryId` (ARCHITECTURE §6 deposit idempotency).\n */\n deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt>;\n\n /**\n * Pull-based feed of incoming deliveries since the given transport-local\n * cursor (or the provider's persisted cursor when omitted). Yields only\n * deliveries not yet in the persistent seen-set; completes when the feed is\n * drained — callers re-invoke on poll/wake. Feeds the existing\n * transport-agnostic `handleV2Transfer` (sdk-changes S3).\n */\n incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery>;\n\n /**\n * Acknowledge a delivery: `'claimed'` accepts it (with the provider's\n * composition-time custody), `'rejected'` marks it locally-unverifiable —\n * terminal for discovery only (the entry stays claimable server-side and\n * its blob is retained — ARCHITECTURE §6). Both record the delivery in the\n * persistent seen-set.\n */\n ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void>;\n\n /**\n * Optional wake hook: `callback` fires when new deliveries may be available\n * (e.g. a WS nudge — never a correctness dependency, ARCHITECTURE §9).\n * Returns an unsubscribe function.\n */\n onWake?(callback: () => void): () => void;\n\n /**\n * Late-bind the backend-true (tokenId, stateHash) derivation —\n * `ITokenEngine.deliveryKeys`. Compositions are engine-less (the engine is\n * built later); the module that owns both (PaymentsModule) binds this at\n * init. Implementations that derive ids (S7) MUST use it and fail loudly if\n * unbound; transports that don't derive may omit the method.\n */\n bindDeliveryKeys?(derive: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>): void;\n}\n\n// =============================================================================\n// deliveryId derivation (the backend's entry_id formula — ARCHITECTURE §6)\n// =============================================================================\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * The content-derived delivery id:\n * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — identical to the\n * backend's `entry_id` (ARCHITECTURE §6), computed client-side so no\n * implementation can substitute a server row id (covenant §3.1-4).\n *\n * @param tokenIdHex - genesis-stable 64-hex token id\n * @param stateHashHex - the SDK's per-state hash (DataHash imprint, hex) — the\n * backend's `state_hash` (wallet-api validation/chain.ts); NEVER a plain\n * sha256 over the token bytes (that variant 422s on deposit).\n */\nexport function computeDeliveryId(tokenIdHex: string, stateHashHex: string): string {\n const tokenId = hexToBytes(tokenIdHex);\n const stateHash = hexToBytes(stateHashHex);\n const joined = new Uint8Array(tokenId.length + stateHash.length);\n joined.set(tokenId, 0);\n joined.set(stateHash, tokenId.length);\n return bytesToHex(sha256(joined));\n}\n\n/** Full delivery keys: the engine-derived pair + the composed deliveryId. */\nexport interface DeliveryBlobKeys {\n /** Genesis-stable 64-hex token id (from the TokenBlob envelope). */\n tokenId: string;\n /** Per-state hash: `hex(SHA-256(inner token bytes))` — changes every transfer. */\n stateHash: string;\n /** {@link computeDeliveryId} of the two above. */\n deliveryId: string;\n}\n\n/**\n * Derive (tokenId, stateHash, deliveryId) from encoded TokenBlob bytes.\n * Throws when the bytes are not a decodable TokenBlob.\n */\n/**\n * Compose full delivery keys from an engine-derived (tokenId, stateHash) pair.\n * The pair MUST come from `ITokenEngine.deliveryKeys` (the backend-true SDK\n * derivation) — the port module deliberately cannot decode tokens itself.\n */\nexport function composeDeliveryKeys(keys: { tokenId: string; stateHash: string }): DeliveryBlobKeys {\n return { ...keys, deliveryId: computeDeliveryId(keys.tokenId, keys.stateHash) };\n}\n","/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport {\n abytes,\n aexists,\n ahash,\n aoutput,\n clean,\n type CHash,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Internal class for HMAC.\n * Accepts any byte key, although RFC 2104 §3 recommends keys at least\n * `HashLen` bytes long.\n */\nexport class _HMAC<T extends Hash<T>> implements Hash<_HMAC<T>> {\n oHash: T;\n iHash: T;\n blockLen: number;\n outputLen: number;\n canXOF = false;\n private finished = false;\n private destroyed = false;\n\n constructor(hash: TArg<CHash>, key: TArg<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 the first block) of the outer hash here,\n // 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: TArg<Uint8Array>): this {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n const buf = out.subarray(0, this.outputLen);\n // Reuse the first outputLen bytes for the inner digest; the outer hash consumes them before\n // overwriting that same prefix with the final tag, leaving any oversized tail untouched.\n this.iHash.digestInto(buf);\n this.oHash.update(buf);\n this.oHash.digestInto(buf);\n this.destroy();\n }\n digest(): TRet<Uint8Array> {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out as TRet<Uint8Array>;\n }\n _cloneInto(to?: _HMAC<T>): _HMAC<T> {\n // Create new instance without calling constructor since the key\n // is 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 - authentication key bytes\n * @param message - message bytes to authenticate\n * @returns Authentication tag bytes.\n * @example\n * Compute an RFC 2104 HMAC.\n * ```ts\n * import { hmac } from '@noble/hashes/hmac.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const mac = hmac(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]));\n * ```\n */\ntype HmacFn = {\n (hash: TArg<CHash>, key: TArg<Uint8Array>, message: TArg<Uint8Array>): TRet<Uint8Array>;\n create(hash: TArg<CHash>, key: TArg<Uint8Array>): TRet<_HMAC<any>>;\n};\nexport const hmac: TRet<HmacFn> = /* @__PURE__ */ (() => {\n const hmac_ = ((\n hash: TArg<CHash>,\n key: TArg<Uint8Array>,\n message: TArg<Uint8Array>\n ): TRet<Uint8Array> => new _HMAC<any>(hash, key).update(message).digest()) as TRet<HmacFn>;\n hmac_.create = (hash: TArg<CHash>, key: TArg<Uint8Array>): TRet<_HMAC<any>> =>\n new _HMAC<any>(hash, key) as TRet<_HMAC<any>>;\n return hmac_;\n})();\n","/**\n * HKDF (RFC 5869): extract + expand in one step.\n * See {@link https://soatok.blog/2021/11/17/understanding-hkdf/}.\n * @module\n */\nimport { hmac } from './hmac.ts';\nimport { abytes, ahash, anumber, type CHash, clean, type TArg, type TRet } 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 * Local validation only checks `hash`; `ikm` / `salt` byte validation is delegated to `hmac()`.\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 * @returns Pseudorandom key derived from input keying material.\n * @example\n * Run the HKDF extract step.\n * ```ts\n * import { extract } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * extract(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]));\n * ```\n */\nexport function extract(\n hash: TArg<CHash>,\n ikm: TArg<Uint8Array>,\n salt?: TArg<Uint8Array>\n): TRet<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\n// Shared mutable scratch byte for the RFC 5869 block counter `N`.\n// Safe to reuse because `expand()` is synchronous and resets it with `clean(...)` before returning.\nconst HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);\n// Shared RFC 5869 empty string for both `info === undefined` and the first-block `T(0)` input.\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\n * (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 * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM.\n * @returns Output keying material with the requested length.\n * @throws If the requested output length exceeds the HKDF limit\n * for the selected hash. {@link Error}\n * @example\n * Run the HKDF expand step.\n * ```ts\n * import { expand } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * expand(sha256, new Uint8Array(32), new Uint8Array([1, 2, 3]), 16);\n * ```\n */\nexport function expand(\n hash: TArg<CHash>,\n prk: TArg<Uint8Array>,\n info?: TArg<Uint8Array>,\n length: number = 32\n): TRet<Uint8Array> {\n ahash(hash);\n anumber(length, 'length');\n abytes(prk, undefined, 'prk');\n const olen = hash.outputLen;\n // RFC 5869 §2.3: PRK is \"a pseudorandom key of at least HashLen octets\".\n if (prk.length < olen) throw new Error('\"prk\" must be at least HashLen octets');\n // RFC 5869 §2.3 only bounds `L` by `<= 255*HashLen`; `L=0` is valid and yields empty OKM.\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) as TRet<Uint8Array>;\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 bytes\n * @param length - length of output keying material in bytes.\n * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM.\n * @returns Output keying material derived from the input key.\n * @throws If the requested output length exceeds the HKDF limit\n * for the selected hash. {@link Error}\n * @example\n * HKDF (RFC 5869): derive keys from an initial input.\n * ```ts\n * import { hkdf } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * import { randomBytes, utf8ToBytes } from '@noble/hashes/utils.js';\n * const inputKey = randomBytes(32);\n * const salt = randomBytes(32);\n * const info = utf8ToBytes('application-key');\n * const okm = hkdf(sha256, inputKey, salt, info, 32);\n * ```\n */\nexport const hkdf = (\n hash: TArg<CHash>,\n ikm: TArg<Uint8Array>,\n salt: TArg<Uint8Array | undefined>,\n info: TArg<Uint8Array | undefined>,\n length: number\n): TRet<Uint8Array> => expand(hash, extract(hash, ikm, salt), info, length);\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */\n\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - Value to inspect.\n * @returns `true` when the value is a Uint8Array view, including Node's `Buffer`.\n * @example\n * Guards a value before treating it as raw key material.\n *\n * ```ts\n * isBytes(new Uint8Array());\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy /\n // cross-realm cases. The fallback still requires a real ArrayBuffer view\n // so plain JSON-deserialized `{ constructor: ... }`\n // spoofing is rejected, and `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is boolean.\n * @param b - Value to validate.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validates a boolean option before branching on it.\n *\n * ```ts\n * abool(true);\n * ```\n */\nexport function abool(b: boolean): void {\n if (typeof b !== 'boolean') throw new TypeError(`boolean expected, not ${b}`);\n}\n\n/**\n * Asserts something is a non-negative safe integer.\n * @param n - Value to validate.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validates a non-negative length or counter.\n *\n * ```ts\n * anumber(1);\n * ```\n */\nexport function anumber(n: number): void {\n if (typeof n !== 'number') throw new TypeError('number expected, got ' + typeof n);\n if (!Number.isSafeInteger(n) || n < 0)\n throw new RangeError('positive integer expected, got ' + n);\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - Value to validate.\n * @param length - Expected byte length.\n * @param title - Optional label used in error messages.\n * @returns The validated byte array.\n * On Node, `Buffer` is accepted too because it is a Uint8Array view.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument lengths. {@link RangeError}\n * @example\n * Validates a fixed-length nonce or key buffer.\n *\n * ```ts\n * abytes(new Uint8Array([1, 2]), 2);\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Asserts a hash- or MAC-like instance has not been destroyed or finished.\n * @param instance - Stateful instance to validate.\n * @param checkFinished - Whether to reject finished instances.\n * When `false`, only `destroyed` is checked.\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Guards against calling `update()` or `digest()` on a finished hash.\n *\n * ```ts\n * aexists({ destroyed: false, finished: false });\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a properly-sized byte array.\n * @param out - Output buffer to validate.\n * @param instance - Hash-like instance providing `outputLen`.\n * This is the relaxed `digestInto()`-style contract: output must be at least `outputLen`,\n * unlike one-shot cipher helpers elsewhere in the repo that often require exact lengths.\n * @throws On wrong argument types. {@link TypeError}\n * @param onlyAligned - Whether `out` must be 4-byte aligned for zero-allocation word views.\n * @throws On wrong output buffer lengths. {@link RangeError}\n * @throws On wrong output buffer alignment. {@link Error}\n * @example\n * Verifies that a caller-provided output buffer is large enough.\n *\n * ```ts\n * aoutput(new Uint8Array(16), { outputLen: 16 });\n * ```\n */\nexport function aoutput(out: any, instance: any, onlyAligned = false): void {\n abytes(out, undefined, 'output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('digestInto() expects output buffer of length at least ' + min);\n }\n if (onlyAligned && !isAligned32(out)) throw new Error('invalid output, must be aligned');\n}\n\n/** One-shot hash helper with `.create()`. */\nexport type IHash = {\n (data: string | TArg<Uint8Array>): TRet<Uint8Array>;\n /** Input block size in bytes. */\n blockLen: number;\n /** Digest size in bytes. */\n outputLen: number;\n /** Creates a fresh incremental hash instance of the same algorithm. */\n create: any;\n};\n\n/** One-shot MAC helper with `.create()`. */\nexport type CMac<H extends IHash2 = IHash2, A extends any[] = []> = {\n (msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array>;\n /** Input block size in bytes. */\n blockLen: number;\n /** Digest size in bytes. */\n outputLen: number;\n /**\n * Creates a fresh incremental MAC instance of the same algorithm.\n * @param key - MAC key bytes.\n * @param args - Additional constructor arguments, when the MAC wrapper needs them.\n * @returns Fresh incremental MAC instance.\n */\n create(key: TArg<Uint8Array>, ...args: A): H;\n};\n\n/** Generic type encompassing 8/16/32-bit typed arrays, but not 64-bit. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed-array view to Uint8Array.\n * @param arr - Typed-array view to reinterpret.\n * @returns Uint8Array view over the same bytes.\n * @example\n * Views 32-bit words as raw bytes without copying.\n *\n * ```ts\n * u8(new Uint32Array([1]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed-array view to Uint32Array.\n * @param arr - Typed-array view to reinterpret.\n * @returns Uint32Array view over the same bytes. Callers are expected to provide a\n * 4-byte-aligned offset; trailing `1..3` bytes are silently dropped.\n * @example\n * Views a byte buffer as 32-bit words for block processing.\n *\n * ```ts\n * u32(new Uint8Array(4));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place.\n * Warning: JS provides no guarantees.\n * @param arrays - Arrays to wipe.\n * @example\n * Wipes a temporary key buffer after use.\n *\n * ```ts\n * const bytes = new Uint8Array([1]);\n * clean(bytes);\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - Typed-array view to wrap.\n * @returns DataView over the same bytes.\n * @example\n * Creates an endian-aware view for length encoding.\n *\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Whether the current platform is little-endian.\n * Most are; some IBM systems are not.\n */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Reverses byte order of one 32-bit word.\n * @param word - Unsigned 32-bit word to swap.\n * @returns The same word with bytes reversed.\n * @example\n * Swaps a big-endian word into little-endian byte order.\n *\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport const byteSwap = (word: number): number =>\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff);\n\n/**\n * Normalizes one 32-bit word to the little-endian representation expected by cipher cores.\n * @param n - Unsigned 32-bit word to normalize.\n * @returns Little-endian normalized word on big-endian hosts, else the input word unchanged.\n * @example\n * Normalizes a host-endian word before passing it into an ARX/AES core.\n *\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - Uint32Array whose words should be swapped.\n * @returns The same array after in-place byte swapping.\n * @example\n * Swaps every 32-bit word in a word-view buffer.\n *\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport const byteSwap32 = (arr: TArg<Uint32Array>): TRet<Uint32Array> => {\n for (let i = 0; i < arr.length; i++) arr[i] = byteSwap(arr[i]);\n return arr as TRet<Uint32Array>;\n};\n\n/**\n * Normalizes a Uint32Array view to the little-endian representation expected by cipher cores.\n * @param u - Word view to normalize in place.\n * @returns Little-endian normalized word view.\n * @example\n * Normalizes a word-view buffer before block processing.\n *\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion:\n// {@link https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex | caniuse entry}\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 * @param bytes - Bytes to encode.\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Formats ciphertext bytes for logs or test vectors.\n *\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - Hexadecimal string to decode.\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On malformed hexadecimal input. {@link RangeError}\n * @example\n * Parses a hex test vector into bytes.\n *\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array as TRet<Uint8Array>;\n}\n\n// Used in micro\n/**\n * Converts a big-endian hex string into bigint.\n * @param hex - Hexadecimal string without `0x`.\n * @returns Parsed bigint value. The empty string is treated as `0n`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Parses a big-endian field element or counter from hex.\n *\n * ```ts\n * hexToNumber('ff');\n * ```\n */\nexport function hexToNumber(hex: string): bigint {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n return BigInt(hex === '' ? '0' : '0x' + hex); // Big Endian\n}\n\n// Used in ff1\n// BE: Big Endian, LE: Little Endian\n/**\n * Converts big-endian bytes into bigint.\n * @param bytes - Big-endian bytes.\n * @returns Parsed bigint value. Empty input is treated as `0n`.\n * @throws On invalid byte input passed to the internal hex conversion. {@link TypeError}\n * @example\n * Reads a big-endian integer from serialized bytes.\n *\n * ```ts\n * bytesToNumberBE(new Uint8Array([1, 0]));\n * ```\n */\nexport function bytesToNumberBE(bytes: TArg<Uint8Array>): bigint {\n return hexToNumber(bytesToHex(bytes));\n}\n\n// Used in micro, ff1\n/**\n * Converts a number into big-endian bytes of fixed length.\n * @param n - Number to encode.\n * @param len - Output length in bytes.\n * @returns Big-endian bytes padded to `len`.\n * Validation is indirect through `hexToBytes(...)`, so negative values, `len = 0`,\n * and values that do not fit surface through the downstream hex parser instead of a\n * dedicated range guard here.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If the requested output length cannot represent the encoded value. {@link RangeError}\n * @example\n * Encodes a counter as fixed-width big-endian bytes.\n *\n * ```ts\n * numberToBytesBE(1, 2);\n * ```\n */\nexport function numberToBytesBE(n: number | bigint, len: number): TRet<Uint8Array> {\n // Reject coercible non-numeric inputs before string/hex conversion changes behavior.\n if (typeof n === 'number') anumber(n);\n else if (typeof n !== 'bigint') throw new TypeError(`number or bigint expected, got ${typeof n}`);\n anumber(len);\n return hexToBytes(n.toString(16).padStart(len * 2, '0'));\n}\n\n// Global symbols, but ts doesn't see them:\n// {@link https://github.com/microsoft/TypeScript/issues/31535 | TypeScript issue 31535}\ndeclare const TextEncoder: any;\ndeclare const TextDecoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * @param str - String to encode.\n * @returns UTF-8 bytes in a detached fresh Uint8Array copy.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encodes application text before encryption or MACing.\n *\n * ```ts\n * utf8ToBytes('abc'); // new Uint8Array([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)) as TRet<Uint8Array>; // {@link https://bugzil.la/1681809 | Firefox bug 1681809}\n}\n\n/**\n * Converts bytes to string using UTF8 encoding.\n * @param bytes - UTF-8 bytes.\n * @returns Decoded string. Input validation is delegated to `TextDecoder`, and malformed\n * UTF-8 is replacement-decoded instead of rejected.\n * @example\n * Decodes UTF-8 plaintext back into a string.\n *\n * ```ts\n * bytesToUtf8(new Uint8Array([97, 98, 99])); // 'abc'\n * ```\n */\nexport function bytesToUtf8(bytes: TArg<Uint8Array>): string {\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Checks if two U8A use same underlying buffer and overlaps.\n * This is invalid and can corrupt data.\n * @param a - First byte view.\n * @param b - Second byte view.\n * @returns `true` when the views overlap in memory.\n * @example\n * Detects whether two slices alias the same backing buffer.\n *\n * ```ts\n * overlapBytes(new Uint8Array(4), new Uint8Array(4));\n * ```\n */\nexport function overlapBytes(a: TArg<Uint8Array>, b: TArg<Uint8Array>): boolean {\n // Zero-length views cannot overwrite anything, even if their offset sits inside another range.\n if (!a.byteLength || !b.byteLength) return false;\n return (\n a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy\n a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end\n b.byteOffset < a.byteOffset + a.byteLength // b starts before a end\n );\n}\n\n/**\n * If input and output overlap and input starts before output, we will overwrite end of input before\n * we start processing it, so this is not supported for most ciphers\n * (except chacha/salsa, which were designed for this)\n * @param input - Input bytes.\n * @param output - Output bytes.\n * @throws If the output view would overwrite unread input bytes. {@link Error}\n * @example\n * Rejects an in-place layout that would overwrite unread input bytes.\n *\n * ```ts\n * complexOverlapBytes(new Uint8Array(4), new Uint8Array(4));\n * ```\n */\nexport function complexOverlapBytes(input: TArg<Uint8Array>, output: TArg<Uint8Array>): void {\n // This is very cursed. It works somehow, but I'm completely unsure,\n // reasoning about overlapping aligned windows is very hard.\n if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)\n throw new Error('complex overlap of input and output is not supported');\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - Byte arrays to concatenate.\n * @returns Combined byte array.\n * @throws On wrong argument types inside the byte-array list. {@link TypeError}\n * @example\n * Builds a `nonce || ciphertext` style buffer.\n *\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res as TRet<Uint8Array>;\n}\n\n// Used in ARX only\ntype EmptyObj = {};\n/**\n * Merges user options into defaults.\n * @param defaults - Default option values.\n * @param opts - User-provided overrides.\n * @returns Combined options object.\n * The merge mutates `defaults` in place and returns the same object.\n * @throws If options are missing or not an object. {@link Error}\n * @example\n * Applies user overrides to the default cipher options.\n *\n * ```ts\n * checkOpts({ rounds: 20 }, { rounds: 8 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts: T2\n): T1 & T2 {\n if (opts == null || typeof opts !== 'object') throw new Error('options must be defined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/**\n * Compares two byte arrays in kinda constant time once lengths already match.\n * @param a - First byte array.\n * @param b - Second byte array.\n * @returns `true` when the arrays contain the same bytes. Different lengths still return early.\n * @example\n * Compares an expected authentication tag with the received one.\n *\n * ```ts\n * equalBytes(new Uint8Array([1]), new Uint8Array([1]));\n * ```\n */\nexport function equalBytes(a: TArg<Uint8Array>, b: TArg<Uint8Array>): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];\n return diff === 0;\n}\n\n// TODO: remove\n/** Incremental hash interface used internally. */\nexport interface IHash2 {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by the final digest. */\n outputLen: number;\n /**\n * Absorbs one more chunk into the hash state.\n * @param buf - Data chunk to hash.\n * @returns The same hash instance for chaining.\n */\n update(buf: string | TArg<Uint8Array>): this;\n /**\n * Writes the final digest into a caller-provided buffer.\n * @param buf - Destination buffer for the digest bytes.\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a fresh digest buffer.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /**\n * Resets internal state. Makes Hash instance unusable.\n * Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed\n * by user, they will need to manually call `destroy()` when zeroing is necessary.\n */\n destroy(): void;\n}\n\n/**\n * Wraps a keyed MAC constructor into a one-shot helper with `.create()`.\n * @param keyLen - Valid probe-key length used to read static metadata once.\n * The probe key is only used for `outputLen` / `blockLen`, so callers with several valid key sizes\n * can pass any representative size as long as those values stay fixed.\n * @param macCons - Keyed MAC constructor or factory.\n * @param fromMsg - Optional adapter that derives extra constructor args from the one-shot message.\n * @returns Callable MAC helper with `.create()`.\n */\nexport function wrapMacConstructor<H extends IHash2, A extends any[] = []>(\n keyLen: number,\n macCons: TArg<(key: Uint8Array, ...args: A) => H>,\n fromMsg?: TArg<(msg: Uint8Array) => A>\n): TRet<CMac<H, A>> {\n const mac = macCons as (key: TArg<Uint8Array>, ...args: A) => H;\n const getArgs = (fromMsg || (() => [] as unknown as A)) as (msg: TArg<Uint8Array>) => A;\n const macC: any = (msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array> =>\n mac(key, ...getArgs(msg))\n .update(msg)\n .digest();\n const tmp = mac(new Uint8Array(keyLen), ...getArgs(new Uint8Array(0)));\n macC.outputLen = tmp.outputLen;\n macC.blockLen = tmp.blockLen;\n macC.create = (key: TArg<Uint8Array>, ...args: A) => mac(key, ...args);\n return macC as TRet<CMac<H, A>>;\n}\n\n// This will allow to re-use with composable things like packed & base encoders\n// Also, we probably can make tags composable\n\n/** Sync cipher: takes byte array and returns byte array. */\nexport type Cipher = {\n /**\n * Encrypts plaintext bytes.\n * @param plaintext - Data to encrypt.\n * @returns Ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>): TRet<Uint8Array>;\n /**\n * Decrypts ciphertext bytes.\n * @param ciphertext - Data to decrypt.\n * @returns Plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Async cipher e.g. from built-in WebCrypto. */\nexport type AsyncCipher = {\n /**\n * Encrypts plaintext bytes.\n * @param plaintext - Data to encrypt.\n * @returns Promise resolving to ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>): Promise<TRet<Uint8Array>>;\n /**\n * Decrypts ciphertext bytes.\n * @param ciphertext - Data to decrypt.\n * @returns Promise resolving to plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>): Promise<TRet<Uint8Array>>;\n};\n\n/** Cipher with `output` argument which can optimize by doing 1 less allocation. */\nexport type CipherWithOutput = Cipher & {\n /**\n * Encrypts plaintext bytes into an optional caller-provided buffer.\n * @param plaintext - Data to encrypt.\n * @param output - Optional destination buffer.\n * @returns Ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array>;\n /**\n * Decrypts ciphertext bytes into an optional caller-provided buffer.\n * @param ciphertext - Data to decrypt.\n * @param output - Optional destination buffer.\n * @returns Plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/**\n * Params are outside of return type, so it is accessible before calling constructor.\n * If function support multiple nonceLength's, we return the best one.\n */\nexport type CipherParams = {\n /** Cipher block size in bytes. */\n blockSize: number;\n /** Nonce length in bytes when the cipher uses a fixed nonce size. */\n nonceLength?: number;\n /** Authentication-tag length in bytes for AEAD modes. */\n tagLength?: number;\n /** Whether nonce length is variable at runtime. */\n varSizeNonce?: boolean;\n};\n/**\n * ARX AEAD cipher, like salsa or chacha.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param AAD - Optional associated data.\n * @returns Cipher instance with caller-managed output buffers.\n */\nexport type ARXCipher = ((\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n) => CipherWithOutput) & {\n blockSize: number;\n nonceLength: number;\n tagLength: number;\n};\n/**\n * Cipher constructor signature.\n * @param key - Secret key bytes.\n * @param args - Additional constructor arguments, such as nonce or IV.\n * @returns Cipher instance.\n */\nexport type CipherCons<T extends any[]> = (key: TArg<Uint8Array>, ...args: T) => Cipher;\n/**\n * Wraps a cipher: validates args, ensures encrypt() can only be called once.\n * Used internally by the exported cipher constructors.\n * Output-buffer support is inferred from the wrapped `encrypt` / `decrypt`\n * arity (`fn.length === 2`), and tag-bearing constructors are expected to use\n * `args[1]` for optional AAD.\n * @__NO_SIDE_EFFECTS__\n * @param params - Static cipher metadata. See {@link CipherParams}.\n * @param constructor - Cipher constructor.\n * @returns Wrapped constructor with validation.\n */\nexport const wrapCipher = <C extends CipherCons<any>, P extends CipherParams>(\n params: P,\n constructor: C\n): C & P => {\n function wrappedCipher(key: TArg<Uint8Array>, ...args: any[]): TRet<CipherWithOutput> {\n // Validate key\n abytes(key, undefined, 'key');\n\n // Validate nonce if nonceLength is present\n if (params.nonceLength !== undefined) {\n const nonce = args[0];\n abytes(nonce, params.varSizeNonce ? undefined : params.nonceLength, 'nonce');\n }\n\n // Validate AAD if tagLength present\n const tagl = params.tagLength;\n if (tagl && args[1] !== undefined) abytes(args[1], undefined, 'AAD');\n\n const cipher = constructor(key, ...args);\n const checkOutput = (fnLength: number, output?: TArg<Uint8Array>) => {\n if (output !== undefined) {\n if (fnLength !== 2) throw new Error('cipher output not supported');\n abytes(output, undefined, 'output');\n }\n };\n // Create wrapped cipher with validation and single-use encryption\n let called = false;\n const wrCipher = {\n encrypt(data: TArg<Uint8Array>, output?: TArg<Uint8Array>) {\n if (called) throw new Error('cannot encrypt() twice with same key + nonce');\n called = true;\n abytes(data);\n checkOutput(cipher.encrypt.length, output);\n return (cipher as CipherWithOutput).encrypt(data, output);\n },\n decrypt(data: TArg<Uint8Array>, output?: TArg<Uint8Array>) {\n abytes(data);\n if (tagl && data.length < tagl)\n throw new Error('\"ciphertext\" expected length bigger than tagLength=' + tagl);\n checkOutput(cipher.decrypt.length, output);\n return (cipher as CipherWithOutput).decrypt(data, output);\n },\n };\n\n return wrCipher as TRet<CipherWithOutput>;\n }\n\n Object.assign(wrappedCipher, params);\n return wrappedCipher as C & P;\n};\n\n/**\n * Represents a Salsa or ChaCha xor stream.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Optional starting block counter.\n * @returns Output bytes.\n */\nexport type XorStream = (\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n data: TArg<Uint8Array>,\n output?: TArg<Uint8Array>,\n counter?: number\n) => TRet<Uint8Array>;\n\n/**\n * By default, returns u8a of length.\n * When out is available, it checks it for validity and uses it.\n * @param expectedLength - Required output length.\n * @param out - Optional destination buffer.\n * @param onlyAligned - Whether `out` must be 4-byte aligned.\n * @returns Output buffer ready for writing.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If the provided output buffer has the wrong size or alignment. {@link Error}\n * @example\n * Reuses a caller-provided output buffer when lengths match.\n *\n * ```ts\n * getOutput(16, new Uint8Array(16));\n * ```\n */\nexport function getOutput(\n expectedLength: number,\n out?: TArg<Uint8Array>,\n onlyAligned = true\n): TRet<Uint8Array> {\n if (out === undefined) return new Uint8Array(expectedLength) as TRet<Uint8Array>;\n // Keep Buffer/cross-realm Uint8Array support here instead of trusting a shape-compatible object.\n abytes(out, undefined, 'output');\n if (out.length !== expectedLength)\n throw new Error(\n '\"output\" expected Uint8Array of length ' + expectedLength + ', got: ' + out.length\n );\n if (onlyAligned && !isAligned32(out)) throw new Error('invalid output, must be aligned');\n return out as TRet<Uint8Array>;\n}\n\n/**\n * Encodes data and AAD bit lengths into a 16-byte buffer.\n * @param dataLength - Data length in bits.\n * @param aadLength - AAD length in bits.\n * The serialized block is still `aadLength || dataLength`, matching GCM/Poly1305\n * conventions even though the helper parameter order is `(dataLength, aadLength)`.\n * @param isLE - Whether to encode lengths as little-endian.\n * @returns 16-byte length block.\n * @throws On wrong argument types passed to the endian validator. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Builds the length block appended by GCM and Poly1305.\n *\n * ```ts\n * u64Lengths(16, 8, true);\n * ```\n */\nexport function u64Lengths(dataLength: number, aadLength: number, isLE: boolean): TRet<Uint8Array> {\n // Reject coercible non-number lengths like '10' and true before BigInt(...) accepts them.\n anumber(dataLength);\n anumber(aadLength);\n abool(isLE);\n const num = new Uint8Array(16);\n const view = createView(num);\n view.setBigUint64(0, BigInt(aadLength), isLE);\n view.setBigUint64(8, BigInt(dataLength), isLE);\n return num as TRet<Uint8Array>;\n}\n\n/**\n * Checks whether a byte array is aligned to a 4-byte offset.\n * @param bytes - Byte array to inspect.\n * @returns `true` when the view is 4-byte aligned.\n * @example\n * Checks whether a buffer can be safely viewed as Uint32Array.\n *\n * ```ts\n * isAligned32(new Uint8Array(4));\n * ```\n */\nexport function isAligned32(bytes: TArg<Uint8Array>): boolean {\n return bytes.byteOffset % 4 === 0;\n}\n\n/**\n * Copies bytes into a new Uint8Array.\n * @param bytes - Bytes to copy.\n * @returns Copied byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Copies input into an aligned Uint8Array before block processing.\n *\n * ```ts\n * copyBytes(new Uint8Array([1, 2]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Cryptographically secure PRNG.\n * Uses internal OS-level `crypto.getRandomValues`.\n * @param bytesLength - Number of bytes to produce.\n * Validation is delegated to `Uint8Array(bytesLength)` and `getRandomValues`, so\n * non-integers, negative lengths, and oversize requests surface backend/runtime errors.\n * @returns Random byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the runtime does not expose `crypto.getRandomValues`. {@link Error}\n * @example\n * Generates a fresh nonce or key.\n *\n * ```ts\n * randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Validate upfront so fractional / coercible lengths do not silently\n // truncate through Uint8Array().\n anumber(bytesLength);\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n return cr.getRandomValues(new Uint8Array(bytesLength)) as TRet<Uint8Array>;\n}\n\n/**\n * The pseudorandom number generator doesn't wipe current state:\n * instead, it generates new one based on previous state + entropy.\n * Not reseed/rekey, since AES CTR DRBG does rekey on each randomBytes,\n * which is in fact `reseed`, since it changes counter too.\n */\nexport interface PRG {\n /**\n * Mixes fresh entropy into the current generator state.\n * @param seed - Entropy bytes to absorb.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Produces a requested number of pseudorandom bytes.\n * @param bytesLength - Number of bytes to generate.\n * @returns Random byte array.\n */\n randomBytes(bytesLength: number): TRet<Uint8Array>;\n /** Destroys the generator state. */\n clean(): void;\n}\n\n/** Removes the nonce argument from a cipher constructor type. */\nexport type RemoveNonce<T extends (...args: any) => any> = T extends (\n arg0: any,\n arg1: any,\n ...rest: infer R\n) => infer Ret\n ? (key: TArg<Uint8Array>, ...args: R) => Ret\n : never;\n/**\n * Cipher constructor that requires a nonce argument.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param args - Additional cipher-specific arguments.\n * @returns Cipher instance.\n */\nexport type CipherWithNonce = ((\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n ...args: any[]\n) => Cipher | AsyncCipher) & {\n nonceLength: number;\n};\n\n/**\n * Uses CSPRNG for nonce, nonce injected in ciphertext.\n * For `encrypt`, a `nonceBytes`-length buffer is fetched from CSPRNG and\n * prepended to encrypted ciphertext. For `decrypt`, first `nonceBytes` of ciphertext\n * are treated as nonce. The wrapper always allocates a fresh `nonce || ciphertext`\n * buffer on encrypt and intentionally does not support caller-provided destination buffers.\n * Too-short decrypt inputs are split into short/empty nonce views and then delegated\n * to the wrapped cipher instead of being rejected here first.\n *\n * NOTE: Under the same key, using random nonces (e.g. `managedNonce`) with AES-GCM and ChaCha\n * should be limited to `2**23` (8M) messages to get a collision chance of\n * `2**-50`. Stretching to `2**32` (4B) messages would raise that chance to\n * `2**-33`, still negligible but creeping up.\n * @param fn - Cipher constructor that expects a nonce.\n * @param randomBytes_ - Random-byte source used for nonce generation.\n * @returns Cipher constructor that prepends the nonce to ciphertext.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On invalid nonce lengths observed at wrapper construction or use. {@link RangeError}\n * @example\n * Prepends a fresh random nonce to every ciphertext.\n *\n * ```ts\n * import { gcm } from '@noble/ciphers/aes.js';\n * import { managedNonce, randomBytes } from '@noble/ciphers/utils.js';\n * const wrapped = managedNonce(gcm);\n * const key = randomBytes(16);\n * const ciphertext = wrapped(key).encrypt(new Uint8Array([1, 2, 3]));\n * wrapped(key).decrypt(ciphertext);\n * ```\n */\nexport function managedNonce<T extends CipherWithNonce>(\n fn: T,\n randomBytes_: typeof randomBytes = randomBytes\n): TRet<RemoveNonce<T>> {\n const { nonceLength } = fn;\n anumber(nonceLength);\n const addNonce = (\n nonce: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n plaintext: TArg<Uint8Array>\n ) => {\n const out = concatBytes(nonce, ciphertext);\n // Wrapped ciphers may alias caller plaintext on encrypt(); never zero\n // caller-owned buffers here.\n if (!overlapBytes(plaintext, ciphertext)) ciphertext.fill(0);\n return out;\n };\n // NOTE: we cannot support DST here, it would be mistake:\n // - we don't know how much dst length cipher requires\n // - nonce may unalign dst and break everything\n // - we create new u8a anyway (concatBytes)\n // - previously we passed all args to cipher, but that was mistake!\n const res = ((key: TArg<Uint8Array>, ...args: any[]): any => ({\n encrypt(plaintext: TArg<Uint8Array>) {\n abytes(plaintext);\n const nonce = randomBytes_(nonceLength);\n const encrypted = fn(key, nonce, ...args).encrypt(plaintext);\n // @ts-ignore\n if (encrypted instanceof Promise)\n return encrypted.then((ct) => addNonce(nonce, ct, plaintext));\n return addNonce(nonce, encrypted, plaintext);\n },\n decrypt(ciphertext: TArg<Uint8Array>) {\n abytes(ciphertext);\n const nonce = ciphertext.subarray(0, nonceLength);\n const decrypted = ciphertext.subarray(nonceLength);\n return fn(key, nonce, ...args).decrypt(decrypted);\n },\n })) as RemoveNonce<T> & { blockSize?: number; tagLength?: number };\n // Auto-nonce wrappers still preserve the wrapped payload geometry.\n if ('blockSize' in fn) res.blockSize = (fn as any).blockSize;\n if ('tagLength' in fn) res.tagLength = (fn as any).tagLength;\n return res as TRet<RemoveNonce<T>>;\n}\n\n/** `Uint8Array.of()` return type helper for TS 5.9. */\nexport type Uint8ArrayBuffer = TRet<Uint8Array>;\n","/**\n * Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.\n\nRFC8439 requires multi-step cipher stream, where\nauthKey starts with counter: 0, actual msg with counter: 1.\n\nFor this, we need a way to re-use nonce / counter:\n\n const counter = new Uint8Array(4);\n chacha(..., counter, ...); // counter is now 1\n chacha(..., counter, ...); // counter is now 2\n\nThis is complicated:\n\n- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB\n- Original papers don't allow mutating counters\n- Counter overflow is undefined [^1]\n- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it\n- Caveat: Cannot be re-used through all cases:\n- * chacha has (counter | nonce)\n- * xchacha has (nonce16 | counter | nonce16)\n- Idea B: separate nonce / counter and provide separate API for counter re-use\n- Caveat: there are different counter sizes depending on an algorithm.\n- salsa & chacha also differ in structures of key & sigma:\n salsa20: s[0] | k(4) | s[1] | nonce(2) | cnt(2) | s[2] | k(4) | s[3]\n chacha: s(4) | k(8) | cnt(1) | nonce(3)\n chacha20orig: s(4) | k(8) | cnt(2) | nonce(2)\n- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`\n- Caveat: we can't re-use counter array\n\nxchacha uses the subkey and remaining 8 byte nonce with ChaCha20 as normal\n(prefixed by 4 NUL bytes, since RFC8439 specifies a 12-byte nonce).\nCounter overflow is undefined; see {@link https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/ | the CFRG thread}.\nCurrent noble policy is strict non-wrap for the shared 32-bit counter path:\nexported ARX ciphers reject initial `0xffffffff` and stop before any implicit\nwrap back to zero.\nSee {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2 | the XChaCha appendix} for the extended-nonce construction.\n\n * @module\n */\nimport {\n type PRG,\n type TArg,\n type TRet,\n type XorStream,\n abool,\n abytes,\n anumber,\n checkOpts,\n clean,\n copyBytes,\n getOutput,\n isAligned32,\n isLE,\n randomBytes,\n swap32IfBE,\n u32,\n} from './utils.ts';\n\n// Replaces `TextEncoder` for ASCII literals, which is enough for sigma constants.\n// Non-ASCII input would not match UTF-8 `TextEncoder` output.\nconst encodeStr = (str: string) => Uint8Array.from(str.split(''), (c) => c.charCodeAt(0));\n// Raw `createCipher(...)` exports consume these native-endian `u32(...)` views directly.\n// Public `wrapCipher(...)` APIs reject non-little-endian platforms before reaching this path.\n// RFC 8439 §2.3 / RFC 7539 §2.3 only define the 256-bit-key constants; this 16-byte sigma is\n// kept for legacy allowShortKeys Salsa/ChaCha variants.\nconst sigma16_32 = /* @__PURE__ */ (() => swap32IfBE(u32(encodeStr('expand 16-byte k'))))();\n// RFC 8439 §2.3 / RFC 7539 §2.3 define words 0-3 as\n// `0x61707865 0x3320646e 0x79622d32 0x6b206574`, i.e. `expand 32-byte k`.\nconst sigma32_32 = /* @__PURE__ */ (() => swap32IfBE(u32(encodeStr('expand 32-byte k'))))();\n\n/**\n * Rotates a 32-bit word left.\n * @param a - Input word.\n * @param b - Rotation count in bits.\n * @returns Rotated 32-bit word.\n * @example\n * Moves the top byte of `0x12345678` into the low byte position.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(a: number, b: number): number {\n return (a << b) | (a >>> (32 - b));\n}\n\n/**\n * ARX core function operating on 32-bit words. Ciphers must use u32 for efficiency.\n * @param sigma - Sigma constants for the selected cipher layout.\n * @param key - Expanded key words.\n * @param nonce - Nonce and counter words prepared for the round function.\n * @param output - Output block written in place.\n * @param counter - Block counter value.\n * @param rounds - Optional round count override.\n */\nexport type CipherCoreFn = (\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n nonce: TArg<Uint32Array>,\n output: TArg<Uint32Array>,\n counter: number,\n rounds?: number\n) => void;\n\n/**\n * Nonce-extension function used by XChaCha and XSalsa.\n * @param sigma - Sigma constants for the selected cipher layout.\n * @param key - Expanded key words.\n * @param input - Input nonce words used for subkey derivation.\n * @param output - Output buffer written with the derived nonce words.\n */\nexport type ExtendNonceFn = (\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n input: TArg<Uint32Array>,\n output: TArg<Uint32Array>\n) => void;\n\n/** ARX cipher options.\n * * `allowShortKeys` for 16-byte keys\n * * `counterLength` in bytes\n * * `counterRight`: right: `nonce|counter`; left: `counter|nonce`\n * */\nexport type CipherOpts = {\n /** Whether 16-byte keys are accepted for legacy Salsa and ChaCha variants. */\n allowShortKeys?: boolean;\n /** Optional nonce-expansion hook used by extended-nonce variants. */\n extendNonceFn?: ExtendNonceFn;\n /** Counter length in bytes inside the nonce/counter layout. */\n counterLength?: number;\n /** Whether the layout is `nonce|counter` instead of `counter|nonce`. */\n counterRight?: boolean;\n /** Number of core rounds to execute. */\n rounds?: number;\n};\n\n// Salsa and Chacha block length is always 512-bit\nconst BLOCK_LEN = 64;\n// RFC 8439 §2.2 / RFC 7539 §2.2: the ChaCha state has 16 32-bit words.\nconst BLOCK_LEN32 = 16;\n\n// Counter policy for the shared public `counter` argument:\n// - RFC/IETF ChaCha20 uses a 32-bit counter.\n// - OpenSSL/Node `chacha20` instead treat the full 16-byte IV as a 128-bit\n// counter state and carry into the next word.\n// - Raw `chacha20orig`, `salsa20`, `xsalsa20`, and `xchacha20` use 64-bit counters in libsodium\n// and libtomcrypt, while some libs (for example libtomcrypt's RFC/IETF path) reject the max\n// boundary instead of carrying.\n// - AEAD wrappers diverge too: libsodium `xchacha20poly1305` uses the IETF payload counter from\n// block 1, while `secretstream_xchacha20poly1305` is a different protocol with rekey/reset.\n// Noble intentionally throws instead of silently picking one wrap model for users. In the default\n// path, even a 32-bit boundary would take 2^32 blocks * 64 bytes = 256 GiB, which is practically\n// unreachable for normal JS callers; advanced users who pass `counter` explicitly can implement\n// whatever wider carry / wrap policy they need on top.\nconst MAX_COUNTER = /* @__PURE__ */ (() => 2 ** 32 - 1)();\nconst U32_EMPTY = /* @__PURE__ */ Uint32Array.of();\nfunction runCipher(\n core: TArg<CipherCoreFn>,\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n nonce: TArg<Uint32Array>,\n data: TArg<Uint8Array>,\n output: TArg<Uint8Array>,\n counter: number,\n rounds: number\n): void {\n const len = data.length;\n const block = new Uint8Array(BLOCK_LEN);\n const b32 = u32(block);\n // Make sure that buffers aligned to 4 bytes\n const isAligned = isLE && isAligned32(data) && isAligned32(output);\n const d32 = isAligned ? u32(data) : U32_EMPTY;\n const o32 = isAligned ? u32(output) : U32_EMPTY;\n // RFC 8439 §2.4.1 / RFC 7539 §2.4.1 allow XORing one keystream block at a time and\n // truncating the final partial block instead of materializing the whole keystream.\n if (!isLE) {\n for (let pos = 0; pos < len; counter++) {\n core(\n sigma as TRet<Uint32Array>,\n key as TRet<Uint32Array>,\n nonce as TRet<Uint32Array>,\n b32,\n counter,\n rounds\n );\n // RFC 8439 §2.4 / RFC 7539 §2.4 serialize keystream words in little-endian order.\n swap32IfBE(b32);\n if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const take = Math.min(BLOCK_LEN, len - pos);\n for (let j = 0, posj; j < take; j++) {\n posj = pos + j;\n output[posj] = data[posj] ^ block[j];\n }\n pos += take;\n }\n return;\n }\n for (let pos = 0; pos < len; counter++) {\n core(\n sigma as TRet<Uint32Array>,\n key as TRet<Uint32Array>,\n nonce as TRet<Uint32Array>,\n b32,\n counter,\n rounds\n );\n // See MAX_COUNTER policy note above: never silently wrap the shared public counter.\n if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const take = Math.min(BLOCK_LEN, len - pos);\n // aligned to 4 bytes\n if (isAligned && take === BLOCK_LEN) {\n const pos32 = pos / 4;\n if (pos % 4 !== 0) throw new Error('arx: invalid block position');\n for (let j = 0, posj: number; j < BLOCK_LEN32; j++) {\n posj = pos32 + j;\n o32[posj] = d32[posj] ^ b32[j];\n }\n pos += BLOCK_LEN;\n continue;\n }\n for (let j = 0, posj; j < take; j++) {\n posj = pos + j;\n output[posj] = data[posj] ^ block[j];\n }\n pos += take;\n }\n}\n\n/**\n * Creates an ARX stream cipher from a 32-bit core permutation.\n * Used internally to build the exported Salsa and ChaCha stream ciphers.\n * @param core - Core function that fills one keystream block.\n * @param opts - Cipher layout and nonce-extension options. See {@link CipherOpts}.\n * @returns Stream cipher function over byte arrays.\n * @throws If the core callback, key size, counter, or output sizing is invalid. {@link Error}\n */\nexport function createCipher(core: TArg<CipherCoreFn>, opts: TArg<CipherOpts>): TRet<XorStream> {\n const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts(\n { allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 },\n opts\n );\n if (typeof core !== 'function') throw new Error('core must be a function');\n anumber(counterLength);\n anumber(rounds);\n abool(counterRight);\n abool(allowShortKeys);\n return (\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n data: TArg<Uint8Array>,\n output?: TArg<Uint8Array>,\n counter = 0\n ): TRet<Uint8Array> => {\n abytes(key, undefined, 'key');\n abytes(nonce, undefined, 'nonce');\n abytes(data, undefined, 'data');\n const len = data.length;\n // Raw XorStream APIs return ciphertext/plaintext bytes directly, so caller-provided outputs\n // must match the logical result length exactly instead of returning an oversized workspace.\n output = getOutput(len, output, false);\n anumber(counter);\n // See MAX_COUNTER policy note above: reject advanced explicit-counter requests before any wrap.\n if (counter < 0 || counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const toClean = [];\n\n // Key & sigma\n // key=16 -> sigma16, k=key|key\n // key=32 -> sigma32, k=key\n let l = key.length;\n let k: Uint8Array;\n let sigma: Uint32Array;\n if (l === 32) {\n // Copy caller keys too: big-endian normalization, extended-nonce subkey derivation, and\n // final clean(...) all mutate or wipe the temporary buffer in place.\n toClean.push((k = copyBytes(key)));\n sigma = sigma32_32;\n } else if (l === 16 && allowShortKeys) {\n k = new Uint8Array(32);\n k.set(key);\n k.set(key, 16);\n sigma = sigma16_32;\n toClean.push(k);\n } else {\n abytes(key, 32, 'arx key');\n throw new Error('invalid key size');\n // throw new Error(`\"arx key\" expected Uint8Array of length 32, got length=${l}`);\n }\n\n // Nonce\n // salsa20: 8 (8-byte counter)\n // chacha20orig: 8 (8-byte counter)\n // chacha20: 12 (4-byte counter)\n // xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)\n // xchacha20: 24 (16 -> hchacha, 8 -> old nonce)\n // Copy before taking u32(...) views on misaligned inputs, and on big-endian so later\n // swap32IfBE(...) never mutates caller nonce bytes in place.\n if (!isLE || !isAligned32(nonce)) toClean.push((nonce = copyBytes(nonce)));\n\n let k32 = u32(k);\n // hsalsa & hchacha: handle extended nonce\n if (extendNonceFn) {\n if (nonce.length !== 24) throw new Error(`arx: extended nonce must be 24 bytes`);\n const n16 = nonce.subarray(0, 16);\n if (isLE) extendNonceFn(sigma as TRet<Uint32Array>, k32, u32(n16), k32);\n else {\n const sigmaRaw = swap32IfBE(Uint32Array.from(sigma));\n extendNonceFn(sigmaRaw, k32, u32(n16), k32);\n clean(sigmaRaw);\n swap32IfBE(k32);\n }\n nonce = nonce.subarray(16);\n } else if (!isLE) swap32IfBE(k32);\n\n // Handle nonce counter\n const nonceNcLen = 16 - counterLength;\n if (nonceNcLen !== nonce.length)\n throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);\n\n // Normalize 64-bit-nonce layouts to the 12-byte core input: ChaCha/XChaCha prefix 4 zero\n // counter bytes, while Salsa/XSalsa append them after the nonce words.\n if (nonceNcLen !== 12) {\n const nc = new Uint8Array(12);\n nc.set(nonce, counterRight ? 0 : 12 - nonce.length);\n nonce = nc;\n toClean.push(nonce);\n }\n const n32 = swap32IfBE(u32(nonce));\n // Ensure temporary key/nonce copies are wiped even if the remaining\n // runtime guard in runCipher(...) throws on counter overflow.\n try {\n runCipher(core, sigma, k32, n32, data, output, counter, rounds);\n return output as TRet<Uint8Array>;\n } finally {\n clean(...toClean);\n }\n };\n}\n\n/** Internal class which wraps chacha20 or chacha8 to create CSPRNG. */\nexport class _XorStreamPRG implements PRG {\n readonly blockLen: number;\n readonly keyLen: number;\n readonly nonceLen: number;\n private state: TRet<Uint8Array>;\n private buf: TRet<Uint8Array>;\n private key: TRet<Uint8Array>;\n private nonce: TRet<Uint8Array>;\n private pos: number;\n private ctr: number;\n private cipher: TArg<XorStream>;\n constructor(\n cipher: TArg<XorStream>,\n blockLen: number,\n keyLen: number,\n nonceLen: number,\n seed: TArg<Uint8Array>\n ) {\n this.cipher = cipher;\n this.blockLen = blockLen;\n this.keyLen = keyLen;\n this.nonceLen = nonceLen;\n this.state = new Uint8Array(this.keyLen + this.nonceLen) as TRet<Uint8Array>;\n this.reseed(seed);\n this.ctr = 0;\n this.pos = this.blockLen;\n this.buf = new Uint8Array(this.blockLen) as TRet<Uint8Array>;\n // Keep a single key||nonce backing buffer so reseed/addEntropy/clean update the live cipher\n // inputs in place through these subarray views.\n this.key = this.state.subarray(0, this.keyLen) as TRet<Uint8Array>;\n this.nonce = this.state.subarray(this.keyLen) as TRet<Uint8Array>;\n }\n private reseed(seed: TArg<Uint8Array>) {\n abytes(seed);\n if (!seed || seed.length === 0) throw new Error('entropy required');\n // Mix variable-length entropy cyclically across the whole key||nonce state, then restart the\n // keystream so buffered leftovers from the previous state are never reused.\n for (let i = 0; i < seed.length; i++) this.state[i % this.state.length] ^= seed[i];\n this.ctr = 0;\n this.pos = this.blockLen;\n }\n addEntropy(seed: TArg<Uint8Array>): void {\n // Reject empty entropy before re-keying, otherwise a throwing call would still advance state.\n abytes(seed);\n if (seed.length === 0) throw new Error('entropy required');\n // Re-key from the current stream first, then mix external entropy into the fresh key||nonce\n // state through reseed() so stale buffered bytes are discarded.\n this.state.set(this.randomBytes(this.state.length));\n this.reseed(seed);\n }\n randomBytes(len: number): TRet<Uint8Array> {\n anumber(len);\n if (len === 0) return new Uint8Array(0) as TRet<Uint8Array>;\n const avail = this.pos < this.blockLen ? this.blockLen - this.pos : 0;\n const blocks = Math.ceil(Math.max(0, len - avail) / this.blockLen);\n // Preflight overflow so failed reads don't partially consume keystream\n // and leave the PRG repeating blocks.\n if (blocks > 0 && this.ctr > MAX_COUNTER - blocks) throw new Error('arx: counter overflow');\n const out = new Uint8Array(len);\n let outPos = 0;\n // `out` starts zero-filled, and `buf.fill(0)` below does the same for leftovers: XOR-stream\n // ciphers then emit raw keystream bytes directly into those buffers.\n // Serve buffered leftovers first so split reads stay identical to one larger read.\n if (this.pos < this.blockLen) {\n const take = Math.min(len, this.blockLen - this.pos);\n out.set(this.buf.subarray(this.pos, this.pos + take), 0);\n this.pos += take;\n outPos += take;\n if (outPos === len) return out as TRet<Uint8Array>; // fast path\n }\n // Full blocks directly to out\n const full = Math.floor((len - outPos) / this.blockLen);\n if (full > 0) {\n const blockBytes = full * this.blockLen;\n const b = out.subarray(outPos, outPos + blockBytes);\n this.cipher(this.key, this.nonce, b as TRet<Uint8Array>, b as TRet<Uint8Array>, this.ctr);\n this.ctr += full;\n outPos += blockBytes;\n }\n // Save leftovers\n const left = len - outPos;\n if (left > 0) {\n this.buf.fill(0);\n // NOTE: cipher will handle overflow\n this.cipher(\n this.key,\n this.nonce,\n this.buf as TRet<Uint8Array>,\n this.buf as TRet<Uint8Array>,\n this.ctr++\n );\n out.set(this.buf.subarray(0, left), outPos);\n this.pos = left;\n }\n return out as TRet<Uint8Array>;\n }\n // Clone seeds the new instance from this stream, so the source PRG advances too.\n clone(): _XorStreamPRG {\n return new _XorStreamPRG(\n this.cipher,\n this.blockLen,\n this.keyLen,\n this.nonceLen,\n this.randomBytes(this.state.length)\n );\n }\n // Zeroes the current state and leftover buffer, but does not make the instance unusable:\n // Later reads first drain zeros from the cleared buffer and then continue\n // from zero key||nonce state.\n clean(): void {\n this.pos = 0;\n this.ctr = 0;\n this.buf.fill(0);\n this.state.fill(0);\n }\n}\n\n/**\n * PRG constructor backed by an ARX stream cipher.\n * @param seed - Optional seed bytes mixed into the initial state. When omitted, exactly 32\n * random bytes are mixed in by default: larger states keep a zero tail, while smaller states\n * wrap those bytes through `reseed()`'s XOR schedule.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n */\nexport type XorPRG = (seed?: TArg<Uint8Array>) => TRet<_XorStreamPRG>;\n\n/**\n * Creates a PRG constructor from a stream cipher.\n * @param cipher - Stream cipher used to fill output blocks.\n * @param blockLen - Keystream block length in bytes.\n * @param keyLen - Internal key length in bytes.\n * @param nonceLen - Internal nonce length in bytes.\n * @returns PRG factory for seeded concrete `_XorStreamPRG` instances.\n * @example\n * Builds a PRG from XChaCha20 and reads bytes from a randomly seeded instance.\n * ```ts\n * import { xchacha20 } from '@noble/ciphers/chacha.js';\n * import { createPRG } from '@noble/ciphers/_arx.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const init = createPRG(xchacha20, 64, 32, 24);\n * const prg = init(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const createPRG = (\n cipher: TArg<XorStream>,\n blockLen: number,\n keyLen: number,\n nonceLen: number\n): TRet<XorPRG> => {\n return ((seed: TArg<Uint8Array> = randomBytes(32)): TRet<_XorStreamPRG> =>\n new _XorStreamPRG(\n cipher,\n blockLen,\n keyLen,\n nonceLen,\n seed\n ) as TRet<_XorStreamPRG>) as TRet<XorPRG>;\n};\n","/**\n * Poly1305 ({@link https://cr.yp.to/mac/poly1305-20050329.pdf | PDF},\n * {@link https://en.wikipedia.org/wiki/Poly1305 | wiki})\n * is a fast and parallel secret-key message-authentication code suitable for\n * a wide variety of applications. It was standardized in\n * {@link https://www.rfc-editor.org/rfc/rfc8439 | RFC 8439} and is now used in TLS 1.3.\n *\n * Polynomial MACs are not perfect for every situation:\n * they lack Random Key Robustness: the MAC can be forged, and can't be used in PAKE schemes.\n * See {@link https://keymaterial.net/2020/09/07/invisible-salamanders-in-aes-gcm-siv/ | the invisible salamanders attack writeup}.\n * To combat invisible salamanders, `hash(key)` can be included in ciphertext,\n * however, this would violate ciphertext indistinguishability:\n * an attacker would know which key was used - so `HKDF(key, i)`\n * could be used instead.\n *\n * Check out the {@link https://cr.yp.to/mac.html | original website}.\n * Based on public-domain {@link https://github.com/floodyberry/poly1305-donna | poly1305-donna}.\n * @module\n */\n// prettier-ignore\nimport {\n abytes, aexists, aoutput, bytesToHex,\n clean, concatBytes, copyBytes, hexToNumber, numberToBytesBE,\n wrapMacConstructor, type CMac, type IHash2, type TArg, type TRet\n} from './utils.ts';\n\n// Little-endian 2-byte load used by the Poly1305 limb decomposition.\nfunction u8to16(a: TArg<Uint8Array>, i: number) {\n return (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);\n}\n\nfunction bytesToNumberLE(bytes: TArg<Uint8Array>): bigint {\n return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));\n}\n\n/** Small version of `poly1305` without loop unrolling. Unused, provided for auditability. */\nfunction poly1305_small(msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array> {\n abytes(msg);\n abytes(key, 32, 'key');\n const POW_2_130_5 = BigInt(2) ** BigInt(130) - BigInt(5); // 2^130-5\n const POW_2_128_1 = BigInt(2) ** BigInt(128) - BigInt(1); // 2^128-1\n const CLAMP_R = BigInt('0x0ffffffc0ffffffc0ffffffc0fffffff');\n const r = bytesToNumberLE(key.subarray(0, 16)) & CLAMP_R;\n const s = bytesToNumberLE(key.subarray(16));\n // Process by 16 byte chunks\n let acc = BigInt(0);\n for (let i = 0; i < msg.length; i += 16) {\n const m = msg.subarray(i, i + 16);\n // RFC 8439 §2.5.1 / RFC 7539 §2.5.1 append [0x01] to each chunk before multiplying by r.\n const n = bytesToNumberLE(m) | (BigInt(1) << BigInt(8 * m.length));\n acc = ((acc + n) * r) % POW_2_130_5;\n }\n const res = (acc + s) & POW_2_128_1;\n // RFC 8439 §2.5 / RFC 7539 §2.5 serialize the low 128 bits in little-endian order.\n return numberToBytesBE(res, 16).reverse() as TRet<Uint8Array>; // LE\n}\n\n// Can be used to replace `computeTag` in chacha.ts. Unused, provided for auditability.\n// @ts-expect-error\nfunction poly1305_computeTag_small(\n authKey: TArg<Uint8Array>,\n // AEAD trailer must already be the 16-byte length block:\n // 8-byte little-endian AAD length || 8-byte little-endian ciphertext length.\n lengths: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n): TRet<Uint8Array> {\n // RFC 8439 §2.8.1 / RFC 7539 §2.8.1 MAC input is\n // AAD || pad16(AAD) || ciphertext || pad16(ciphertext) || lengths.\n const res = [];\n const updatePadded2 = (msg: TArg<Uint8Array>) => {\n res.push(msg);\n const leftover = msg.length % 16;\n // RFC 8439 §2.8.1 / RFC 7539 §2.8.1: pad16(x) is empty for aligned\n // inputs, else 16-(len%16) zero bytes.\n if (leftover) res.push(new Uint8Array(16).slice(leftover));\n };\n if (AAD) updatePadded2(AAD);\n updatePadded2(ciphertext);\n res.push(lengths);\n return poly1305_small(concatBytes(...res), authKey);\n}\n\n/**\n * Incremental Poly1305 MAC state.\n * Prefer `poly1305()` for one-shot use.\n * @param key - 32-byte Poly1305 one-time key.\n * @example\n * Feeds one chunk into an incremental Poly1305 state with a fresh one-time key.\n *\n * ```ts\n * import { Poly1305 } from '@noble/ciphers/_poly1305.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const mac = new Poly1305(key);\n * mac.update(new Uint8Array([1, 2, 3]));\n * mac.digest();\n * ```\n */\nexport class Poly1305 implements IHash2 {\n readonly blockLen = 16;\n readonly outputLen = 16;\n private buffer = new Uint8Array(16);\n private r = new Uint16Array(10); // Allocating 1 array with .subarray() here is slower than 3\n private h = new Uint16Array(10);\n private pad = new Uint16Array(8);\n private pos = 0;\n protected finished = false;\n protected destroyed = false;\n\n // Can be speed-up using BigUint64Array, at the cost of complexity\n constructor(key: TArg<Uint8Array>) {\n key = copyBytes(abytes(key, 32, 'key'));\n const t0 = u8to16(key, 0);\n const t1 = u8to16(key, 2);\n const t2 = u8to16(key, 4);\n const t3 = u8to16(key, 6);\n const t4 = u8to16(key, 8);\n const t5 = u8to16(key, 10);\n const t6 = u8to16(key, 12);\n const t7 = u8to16(key, 14);\n\n // RFC 8439 §2.5.1 / RFC 7539 §2.5.1 clamp r before multiplication.\n // These masks unpack that clamped value into 13-bit limbs, while pad\n // keeps the raw s half for finalize().\n // {@link https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47 | poly1305-donna reference}\n this.r[0] = t0 & 0x1fff;\n this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;\n this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;\n this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;\n this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;\n this.r[5] = (t4 >>> 1) & 0x1ffe;\n this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;\n this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;\n this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;\n this.r[9] = (t7 >>> 5) & 0x007f;\n for (let i = 0; i < 8; i++) this.pad[i] = u8to16(key, 16 + 2 * i);\n }\n\n private process(data: TArg<Uint8Array>, offset: number, isLast = false) {\n // RFC 8439 §2.5 / §2.5.1 and RFC 7539 §2.5 / §2.5.1 add an extra high\n // bit to every full 16-byte block. The final partial block gets its\n // explicit `1` byte during digestInto(), so `hibit` stays zero there.\n const hibit = isLast ? 0 : 1 << 11;\n const { h, r } = this;\n const r0 = r[0];\n const r1 = r[1];\n const r2 = r[2];\n const r3 = r[3];\n const r4 = r[4];\n const r5 = r[5];\n const r6 = r[6];\n const r7 = r[7];\n const r8 = r[8];\n const r9 = r[9];\n\n const t0 = u8to16(data, offset + 0);\n const t1 = u8to16(data, offset + 2);\n const t2 = u8to16(data, offset + 4);\n const t3 = u8to16(data, offset + 6);\n const t4 = u8to16(data, offset + 8);\n const t5 = u8to16(data, offset + 10);\n const t6 = u8to16(data, offset + 12);\n const t7 = u8to16(data, offset + 14);\n\n let h0 = h[0] + (t0 & 0x1fff);\n let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);\n let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);\n let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);\n let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);\n let h5 = h[5] + ((t4 >>> 1) & 0x1fff);\n let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);\n let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);\n let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);\n let h9 = h[9] + ((t7 >>> 5) | hibit);\n\n let c = 0;\n\n let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);\n c = d0 >>> 13;\n d0 &= 0x1fff;\n d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);\n c += d0 >>> 13;\n d0 &= 0x1fff;\n\n let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);\n c = d1 >>> 13;\n d1 &= 0x1fff;\n d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);\n c += d1 >>> 13;\n d1 &= 0x1fff;\n\n let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);\n c = d2 >>> 13;\n d2 &= 0x1fff;\n d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);\n c += d2 >>> 13;\n d2 &= 0x1fff;\n\n let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);\n c = d3 >>> 13;\n d3 &= 0x1fff;\n d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);\n c += d3 >>> 13;\n d3 &= 0x1fff;\n\n let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;\n c = d4 >>> 13;\n d4 &= 0x1fff;\n d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);\n c += d4 >>> 13;\n d4 &= 0x1fff;\n\n let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;\n c = d5 >>> 13;\n d5 &= 0x1fff;\n d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);\n c += d5 >>> 13;\n d5 &= 0x1fff;\n\n let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;\n c = d6 >>> 13;\n d6 &= 0x1fff;\n d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);\n c += d6 >>> 13;\n d6 &= 0x1fff;\n\n let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;\n c = d7 >>> 13;\n d7 &= 0x1fff;\n d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);\n c += d7 >>> 13;\n d7 &= 0x1fff;\n\n let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;\n c = d8 >>> 13;\n d8 &= 0x1fff;\n d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);\n c += d8 >>> 13;\n d8 &= 0x1fff;\n\n let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;\n c = d9 >>> 13;\n d9 &= 0x1fff;\n d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;\n c += d9 >>> 13;\n d9 &= 0x1fff;\n\n c = ((c << 2) + c) | 0;\n c = (c + d0) | 0;\n d0 = c & 0x1fff;\n c = c >>> 13;\n d1 += c;\n\n h[0] = d0;\n h[1] = d1;\n h[2] = d2;\n h[3] = d3;\n h[4] = d4;\n h[5] = d5;\n h[6] = d6;\n h[7] = d7;\n h[8] = d8;\n h[9] = d9;\n }\n\n private finalize() {\n const { h, pad } = this;\n const g = new Uint16Array(10);\n let c = h[1] >>> 13;\n h[1] &= 0x1fff;\n for (let i = 2; i < 10; i++) {\n h[i] += c;\n c = h[i] >>> 13;\n h[i] &= 0x1fff;\n }\n h[0] += c * 5;\n c = h[0] >>> 13;\n h[0] &= 0x1fff;\n h[1] += c;\n c = h[1] >>> 13;\n h[1] &= 0x1fff;\n h[2] += c;\n\n // RFC 8439 §2.5 / RFC 7539 §2.5 reduce modulo 2^130-5 before repacking\n // to 16-bit words and adding the raw s half.\n g[0] = h[0] + 5;\n c = g[0] >>> 13;\n g[0] &= 0x1fff;\n for (let i = 1; i < 10; i++) {\n g[i] = h[i] + c;\n c = g[i] >>> 13;\n g[i] &= 0x1fff;\n }\n g[9] -= 1 << 13;\n\n let mask = (c ^ 1) - 1;\n for (let i = 0; i < 10; i++) g[i] &= mask;\n mask = ~mask;\n for (let i = 0; i < 10; i++) h[i] = (h[i] & mask) | g[i];\n h[0] = (h[0] | (h[1] << 13)) & 0xffff;\n h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;\n h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;\n h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;\n h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;\n h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;\n h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;\n h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;\n\n let f = h[0] + pad[0];\n h[0] = f & 0xffff;\n for (let i = 1; i < 8; i++) {\n f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;\n h[i] = f & 0xffff;\n }\n clean(g);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n data = copyBytes(data);\n const { buffer, blockLen } = this;\n const len = data.length;\n\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\n if (take === blockLen) {\n for (; blockLen <= len - pos; pos += blockLen) this.process(data, 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(buffer, 0, false);\n this.pos = 0;\n }\n }\n return this;\n }\n destroy(): void {\n // `aexists(this)` guards update/digest paths, so destroy must mark the instance unusable too.\n this.destroyed = true;\n clean(this.h, this.r, this.buffer, this.pad);\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n const { buffer, h } = this;\n let { pos } = this;\n if (pos) {\n // RFC 8439 §2.5 / RFC 7539 §2.5: the final short block appends a\n // single `0x01` byte and zero-fills the remaining bytes before the\n // last multiplication step.\n buffer[pos++] = 1;\n for (; pos < 16; pos++) buffer[pos] = 0;\n this.process(buffer, 0, true);\n }\n this.finalize();\n let opos = 0;\n for (let i = 0; i < 8; i++) {\n out[opos++] = h[i] >>> 0;\n out[opos++] = h[i] >>> 8;\n }\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy out before destroy() zeroes the internal buffer.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n}\n\n/** One-shot keyed hash helper with `.create()`. */\nexport type CHash = CMac<Poly1305>;\n\n/**\n * Poly1305 MAC from RFC 8439.\n * @param msg - Message bytes to authenticate.\n * @param key - 32-byte Poly1305 one-time key.\n * @returns 16-byte authentication tag.\n * @example\n * Authenticates one message with a one-shot Poly1305 call and a fresh key.\n *\n * ```ts\n * import { poly1305 } from '@noble/ciphers/_poly1305.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * poly1305(new Uint8Array(), key);\n * ```\n */\nexport const poly1305: TRet<CHash> = /* @__PURE__ */ wrapMacConstructor(\n 32,\n (key: TArg<Uint8Array>) => new Poly1305(key)\n);\n","/**\n * ChaCha stream cipher, released\n * in 2008. Developed after Salsa20, ChaCha aims to increase diffusion per round.\n * It was standardized in\n * {@link https://www.rfc-editor.org/rfc/rfc8439 | RFC 8439} and\n * is now used in TLS 1.3.\n *\n * {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | XChaCha20}\n * extended-nonce variant is also provided. Similar to XSalsa, it's safe to use with\n * randomly-generated nonces.\n *\n * Check out\n * {@link http://cr.yp.to/chacha/chacha-20080128.pdf | PDF},\n * {@link https://en.wikipedia.org/wiki/Salsa20 | wiki}, and\n * {@link https://cr.yp.to/chacha.html | website}.\n *\n * @module\n */\nimport { type XorPRG, createCipher, createPRG, rotl } from './_arx.ts';\nimport { poly1305 } from './_poly1305.ts';\nimport {\n type ARXCipher,\n type CipherWithOutput,\n type TArg,\n type TRet,\n type XorStream,\n abytes,\n clean,\n equalBytes,\n getOutput,\n swap8IfBE,\n swap32IfBE,\n u64Lengths,\n wrapCipher,\n} from './utils.ts';\n\n/**\n * ChaCha core function. It is implemented twice:\n * 1. Simple loop (chachaCore_small, hchacha_small)\n * 2. Unrolled loop (chachaCore, hchacha) - 4x faster, but larger & harder to read\n * The specific implementation is selected in `createCipher` below.\n */\n\n/** RFC 8439 §2.1 quarter round on words a, b, c, d. */\n// prettier-ignore\nfunction chachaQR(x: TArg<Uint32Array>, a: number, b: number, c: number, d: number) {\n x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 16);\n x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 12);\n x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 8);\n x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 7);\n}\n\n/** Repeated ChaCha double rounds; callers are expected to pass an even round count. */\nfunction chachaRound(x: TArg<Uint32Array>, rounds = 20) {\n for (let r = 0; r < rounds; r += 2) {\n // RFC 8439 §2.3 / §2.3.1 inner_block: four column rounds, then four diagonal rounds.\n chachaQR(x, 0, 4, 8, 12);\n chachaQR(x, 1, 5, 9, 13);\n chachaQR(x, 2, 6, 10, 14);\n chachaQR(x, 3, 7, 11, 15);\n chachaQR(x, 0, 5, 10, 15);\n chachaQR(x, 1, 6, 11, 12);\n chachaQR(x, 2, 7, 8, 13);\n chachaQR(x, 3, 4, 9, 14);\n }\n}\n\n// Shared scratch for the auditability-only helper below; only the test-only\n// __TESTS.chachaCore_small hook reaches it, so production exports stay reentrant.\nconst ctmp = /* @__PURE__ */ new Uint32Array(16);\n\n/** Small version of chacha without loop unrolling. Unused, provided for auditability. */\n// prettier-ignore\nfunction chacha(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, i: TArg<Uint32Array>, out: TArg<Uint32Array>,\n isHChacha: boolean = true, rounds: number = 20\n): void {\n // `i` is either `[counter, nonce0, nonce1, nonce2]` for the ChaCha block\n // function or the full 128-bit nonce prefix for the HChaCha subkey path.\n // Create initial array using common pattern\n const y = Uint32Array.from([\n s[0], s[1], s[2], s[3], // \"expa\" \"nd 3\" \"2-by\" \"te k\"\n k[0], k[1], k[2], k[3], // Key Key Key Key\n k[4], k[5], k[6], k[7], // Key Key Key Key\n i[0], i[1], i[2], i[3], // Counter Counter Nonce Nonce\n ]);\n const x = ctmp;\n x.set(y);\n chachaRound(x, rounds);\n\n // HChaCha writes words 0..3 and 12..15 after the rounds; the ChaCha\n // block path adds the original state word-by-word.\n if (isHChacha) {\n const xindexes = [0, 1, 2, 3, 12, 13, 14, 15];\n for (let i = 0; i < 8; i++) out[i] = x[xindexes[i]];\n } else {\n for (let i = 0; i < 16; i++) out[i] = (y[i] + x[i]) | 0;\n }\n}\n\n/** Identical to `chachaCore`. Reached only through the test-only `__TESTS` export. */\n// @ts-ignore\nconst chachaCore_small: typeof chachaCore = (s, k, n, out, cnt, rounds) =>\n // Keep the reference wrapper on the same [counter, nonce0, nonce1, nonce2] layout as chacha().\n chacha(s, k, Uint32Array.from([cnt, n[0], n[1], n[2]]), out, false, rounds);\n/** Identical to `hchacha`. Unused. */\n// @ts-ignore\nconst hchacha_small: typeof hchacha = chacha;\n\n/** RFC 8439 §2.3 block core for `state = constants | key | counter | nonce`. */\n// prettier-ignore\nfunction chachaCore(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, n: TArg<Uint32Array>, out: TArg<Uint32Array>, cnt: number, rounds = 20\n): void {\n let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // \"expa\" \"nd 3\" \"2-by\" \"te k\"\n y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key\n y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key\n y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Nonce Nonce Nonce\n // Save state to temporary variables\n let x00 = y00, x01 = y01, x02 = y02, x03 = y03,\n x04 = y04, x05 = y05, x06 = y06, x07 = y07,\n x08 = y08, x09 = y09, x10 = y10, x11 = y11,\n x12 = y12, x13 = y13, x14 = y14, x15 = y15;\n for (let r = 0; r < rounds; r += 2) {\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);\n\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);\n\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 8);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);\n\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);\n\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);\n\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);\n\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);\n\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);\n }\n // RFC 8439 §2.3 / §2.3.1: add the original state words back in state order.\n let oi = 0;\n out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;\n out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;\n out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;\n out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;\n out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;\n out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;\n out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;\n out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;\n}\n/**\n * hchacha hashes key and nonce into key' and nonce' for xchacha20.\n * Algorithmically identical to `hchacha_small`, but this exported path\n * normalizes word order on big-endian hosts.\n * Need to find a way to merge it with `chachaCore` without 25% performance hit.\n * @param s - Sigma constants as 32-bit words.\n * @param k - Key words.\n * @param i - Nonce-prefix words.\n * @param out - Output buffer for the derived subkey.\n * @example\n * Derives the XChaCha subkey from sigma, key, and nonce-prefix words.\n *\n * ```ts\n * const sigma = new Uint32Array(4);\n * const key = new Uint32Array(8);\n * const nonce = new Uint32Array(4);\n * const out = new Uint32Array(8);\n * hchacha(sigma, key, nonce, out);\n * ```\n */\n// prettier-ignore\nexport function hchacha(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, i: TArg<Uint32Array>, out: TArg<Uint32Array>\n): void {\n let x00 = swap8IfBE(s[0]), x01 = swap8IfBE(s[1]), x02 = swap8IfBE(s[2]), x03 = swap8IfBE(s[3]),\n x04 = swap8IfBE(k[0]), x05 = swap8IfBE(k[1]), x06 = swap8IfBE(k[2]), x07 = swap8IfBE(k[3]),\n x08 = swap8IfBE(k[4]), x09 = swap8IfBE(k[5]), x10 = swap8IfBE(k[6]), x11 = swap8IfBE(k[7]),\n x12 = swap8IfBE(i[0]), x13 = swap8IfBE(i[1]), x14 = swap8IfBE(i[2]), x15 = swap8IfBE(i[3]);\n for (let r = 0; r < 20; r += 2) {\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);\n\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);\n\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 8);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);\n\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);\n\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);\n\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);\n\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);\n\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);\n }\n // HChaCha derives the subkey from state words 0..3 and 12..15 after 20 rounds.\n let oi = 0;\n out[oi++] = x00; out[oi++] = x01;\n out[oi++] = x02; out[oi++] = x03;\n out[oi++] = x12; out[oi++] = x13;\n out[oi++] = x14; out[oi++] = x15;\n swap32IfBE(out);\n}\n\n/**\n * Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.\n * The nonce/counter layout still reserves 8 counter bytes internally, but the shared public\n * `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`\n * near `MAX_COUNTER` for the full counter-policy rationale.\n * @param key - 16-byte or 32-byte key.\n * @param nonce - 8-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with the original 8-byte-nonce ChaCha variant and a fresh key/nonce.\n *\n * ```ts\n * import { chacha20orig } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(8);\n * chacha20orig(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha20orig: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n allowShortKeys: true,\n});\n/**\n * ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.\n * With smaller nonce, it's not safe to make it random (CSPRNG), due to collision chance.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with the RFC 8439 ChaCha20 stream cipher and a fresh key/nonce.\n *\n * ```ts\n * import { chacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha20(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha20: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n allowShortKeys: false,\n});\n\n/**\n * XChaCha eXtended-nonce ChaCha. With 24-byte nonce, it's safe to make it random (CSPRNG).\n * See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.\n * The nonce/counter layout still reserves 8 counter bytes internally, but the shared public\n * `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`\n * near `MAX_COUNTER` for the full counter-policy rationale.\n * @param key - 32-byte key.\n * @param nonce - 24-byte extended nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with XChaCha20 using a fresh key and random 24-byte nonce.\n *\n * ```ts\n * import { xchacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(24);\n * xchacha20(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const xchacha20: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n extendNonceFn: hchacha,\n allowShortKeys: false,\n});\n\n/**\n * Reduced 8-round chacha, described in original paper.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Uses the reduced 8-round variant for non-critical workloads with a fresh key/nonce.\n *\n * ```ts\n * import { chacha8 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha8(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha8: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 8,\n});\n\n/**\n * Reduced 12-round chacha, described in original paper.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Uses the reduced 12-round variant for non-critical workloads with a fresh key/nonce.\n *\n * ```ts\n * import { chacha12 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha12(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha12: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 12,\n});\n\n// Test-only hooks for keeping the simple/reference core aligned with the unrolled production core.\nexport const __TESTS: {\n chachaCore_small: typeof chachaCore_small;\n chachaCore: typeof chachaCore;\n} = /* @__PURE__ */ Object.freeze({ chachaCore_small, chachaCore });\n\n// RFC 8439 §2.8.1 pad16(x): shared zero block for AAD/ciphertext padding.\nconst ZEROS16 = /* @__PURE__ */ new Uint8Array(16);\n// RFC 8439 §2.8 / §2.8.1: aligned inputs add nothing, otherwise append 16-(len%16) zero bytes.\nconst updatePadded = (h: ReturnType<typeof poly1305.create>, msg: TArg<Uint8Array>) => {\n h.update(msg);\n const leftover = msg.length % 16;\n if (leftover) h.update(ZEROS16.subarray(leftover));\n};\n\n// RFC 8439 §2.6.1 poly1305_key_gen returns `block[0..31]`, so AEAD key\n// generation only needs 32 zero bytes.\nconst ZEROS32 = /* @__PURE__ */ new Uint8Array(32);\nfunction computeTag(\n fn: TArg<XorStream>,\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n): TRet<Uint8Array> {\n if (AAD !== undefined) abytes(AAD, undefined, 'AAD');\n // RFC 8439 §2.6 / §2.8: derive the Poly1305 one-time key from counter 0,\n // then MAC AAD || pad16(AAD) || ciphertext || pad16(ciphertext) || len(AAD) || len(ciphertext).\n const authKey = fn(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n ZEROS32 as TRet<Uint8Array>\n );\n const lengths = u64Lengths(ciphertext.length, AAD ? AAD.length : 0, true);\n\n // Methods below can be replaced with\n // return poly1305_computeTag_small(authKey, lengths, ciphertext, AAD)\n const h = poly1305.create(authKey);\n if (AAD) updatePadded(h, AAD);\n updatePadded(h, ciphertext);\n h.update(lengths);\n const res = h.digest();\n clean(authKey, lengths);\n return res;\n}\n\n/**\n * AEAD algorithm from RFC 8439.\n * Salsa20 and chacha (RFC 8439) use poly1305 differently.\n * We could have composed them, but it's hard because of authKey:\n * In salsa20, authKey changes position in salsa stream.\n * In chacha, authKey can't be computed inside computeTag, it modifies the counter.\n */\nexport const _poly1305_aead =\n (xorStream: TArg<XorStream>) =>\n (key: TArg<Uint8Array>, nonce: TArg<Uint8Array>, AAD?: TArg<Uint8Array>): CipherWithOutput => {\n // This borrows caller key/nonce/AAD buffers by reference; mutating them after construction\n // changes future encrypt/decrypt results.\n const tagLength = 16;\n return {\n encrypt(plaintext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array> {\n const plength = plaintext.length;\n output = getOutput(plength + tagLength, output, false);\n output.set(plaintext);\n const oPlain = output.subarray(0, -tagLength);\n // RFC 8439 §2.8: payload encryption starts at counter 1 because counter 0 produced the OTK.\n xorStream(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n oPlain as TRet<Uint8Array>,\n oPlain as TRet<Uint8Array>,\n 1\n );\n const tag = computeTag(xorStream, key, nonce, oPlain, AAD);\n output.set(tag, plength); // append tag\n clean(tag);\n return output as TRet<Uint8Array>;\n },\n decrypt(ciphertext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array> {\n output = getOutput(ciphertext.length - tagLength, output, false);\n const data = ciphertext.subarray(0, -tagLength);\n const passedTag = ciphertext.subarray(-tagLength);\n const tag = computeTag(xorStream, key, nonce, data, AAD);\n // RFC 8439 §2.8 / §4: authenticate ciphertext before decrypting it, and compare tags with\n // the constant-time equalBytes() helper rather than decrypting speculative plaintext first.\n if (!equalBytes(passedTag, tag)) {\n clean(tag);\n throw new Error('invalid tag');\n }\n output.set(ciphertext.subarray(0, -tagLength));\n // Actual decryption\n xorStream(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n output as TRet<Uint8Array>,\n output as TRet<Uint8Array>,\n 1\n ); // start stream with i=1\n clean(tag);\n return output as TRet<Uint8Array>;\n },\n };\n };\n\n/**\n * ChaCha20-Poly1305 from RFC 8439.\n *\n * Unsafe to use random nonces under the same key, due to collision chance.\n * Prefer XChaCha instead.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param AAD - Additional authenticated data.\n * @returns AEAD cipher instance.\n * @example\n * Encrypts and authenticates plaintext with a fresh key and nonce.\n *\n * ```ts\n * import { chacha20poly1305 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * const cipher = chacha20poly1305(key, nonce);\n * cipher.encrypt(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport const chacha20poly1305: TRet<ARXCipher> = /* @__PURE__ */ wrapCipher(\n { blockSize: 64, nonceLength: 12, tagLength: 16 },\n /* @__PURE__ */ _poly1305_aead(chacha20)\n);\n/**\n * XChaCha20-Poly1305 extended-nonce chacha.\n *\n * Can be safely used with random nonces (CSPRNG).\n * See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.\n * @param key - 32-byte key.\n * @param nonce - 24-byte nonce.\n * @param AAD - Additional authenticated data.\n * @returns AEAD cipher instance.\n * @example\n * Encrypts and authenticates plaintext with a fresh key and random 24-byte nonce.\n *\n * ```ts\n * import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(24);\n * const cipher = xchacha20poly1305(key, nonce);\n * cipher.encrypt(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport const xchacha20poly1305: TRet<ARXCipher> = /* @__PURE__ */ wrapCipher(\n { blockSize: 64, nonceLength: 24, tagLength: 16 },\n /* @__PURE__ */ _poly1305_aead(xchacha20)\n);\n\n/**\n * Chacha20 CSPRNG (cryptographically secure pseudorandom number generator).\n * It's best to limit usage to non-production, non-critical cases: for example, test-only.\n * Compatible with libtomcrypt. It does not have a specification, so unclear how secure it is.\n * @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,\n * only 32 random bytes are mixed into the 40-byte state.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n * @example\n * Seeds the test-only ChaCha20 DRBG from fresh entropy.\n *\n * ```ts\n * import { rngChacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const prg = rngChacha20(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const rngChacha20: TRet<XorPRG> = /* @__PURE__ */ createPRG(chacha20orig, 64, 32, 8);\n/**\n * Chacha20/8 CSPRNG (cryptographically secure pseudorandom number generator).\n * It's best to limit usage to non-production, non-critical cases: for example, test-only.\n * Faster than `rngChacha20`.\n * @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,\n * only 32 random bytes are mixed into the 44-byte state.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n * @example\n * Seeds the faster test-only ChaCha8 DRBG from fresh entropy.\n *\n * ```ts\n * import { rngChacha8 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const prg = rngChacha8(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const rngChacha8: TRet<XorPRG> = /* @__PURE__ */ createPRG(chacha8, 64, 32, 12);\n","/**\n * core/field-encryption.ts — client-side field encryption (sdk-changes S6,\n * ARCHITECTURE §8.3).\n *\n * Fields the backend stores but never needs to read (mailbox `memo`, history\n * `memo` + `counterparty_nametag`, payment-request `memo`, and the intent\n * payload — E.3) are encrypted client-side so any of the owner's devices can\n * decrypt but the operator cannot.\n *\n * - **Key**: `HKDF-SHA256(ikm = walletPrivKey bytes, info = \"sphere-fieldenc-v1\")`\n * → 32-byte symmetric key. Deterministic per wallet → multi-device by\n * construction (mirrors token-engine/realization.ts — same @noble/hashes\n * HKDF, no salt).\n * - **Cipher/envelope**: XChaCha20-Poly1305 with a random 24-byte nonce; wire\n * form is EXACTLY `\"enc1.\" + base64(nonce ‖ ciphertext)`.\n * - **One mapping rule everywhere** (ARCHITECTURE §8.3): JSON bodies and\n * responses carry the `enc1.` string verbatim; the server validates only\n * prefix + base64 + size cap, stores the exact UTF-8 bytes, and returns them\n * unchanged. {@link assertFieldEnvelopeShape} is that server-side check.\n */\n\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha.js';\nimport { randomBytes } from '@noble/ciphers/utils.js';\nimport { SphereError } from './errors';\n\n/** HKDF info label — versioned; the same label appears in sdk-changes S6. */\nexport const FIELD_ENCRYPTION_HKDF_INFO = 'sphere-fieldenc-v1';\n\n/** Envelope wire prefix. The version lives in the prefix (`enc1`). */\nexport const FIELD_ENVELOPE_PREFIX = 'enc1.';\n\n/** XChaCha20-Poly1305 nonce length (24 bytes). */\nexport const FIELD_ENVELOPE_NONCE_BYTES = 24;\n\n/** Poly1305 authentication tag length appended to the ciphertext. */\nconst POLY1305_TAG_BYTES = 16;\n\n/**\n * Default envelope size cap in bytes (UTF-8 length of the full `enc1.…`\n * string). Matches the intent-payload cap (≤ 4 KiB — ARCHITECTURE §7/§11).\n */\nexport const FIELD_ENVELOPE_MAX_BYTES = 4096;\n\n// =============================================================================\n// base64 (RFC 4648, with padding) — hand-rolled so the codec is identical in\n// Node and browsers (no Buffer / btoa dependency) and decode is strict.\n// =============================================================================\n\nconst B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst B64_LOOKUP: Record<string, number> = {};\nfor (let i = 0; i < B64_ALPHABET.length; i++) B64_LOOKUP[B64_ALPHABET[i]] = i;\n\nfunction base64Encode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i];\n const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;\n const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;\n out += B64_ALPHABET[b0 >> 2];\n out += B64_ALPHABET[((b0 & 0x03) << 4) | (b1 >> 4)];\n out += i + 1 < bytes.length ? B64_ALPHABET[((b1 & 0x0f) << 2) | (b2 >> 6)] : '=';\n out += i + 2 < bytes.length ? B64_ALPHABET[b2 & 0x3f] : '=';\n }\n return out;\n}\n\n/** Strict decode: throws on non-alphabet chars, bad length, or bad padding. */\nfunction base64Decode(s: string): Uint8Array {\n if (s.length % 4 !== 0) {\n throw new SphereError('Invalid field envelope: base64 length not a multiple of 4', 'DECRYPTION_ERROR');\n }\n let padding = 0;\n if (s.endsWith('==')) padding = 2;\n else if (s.endsWith('=')) padding = 1;\n const body = s.slice(0, s.length - padding);\n if (body.includes('=')) {\n throw new SphereError('Invalid field envelope: misplaced base64 padding', 'DECRYPTION_ERROR');\n }\n const out = new Uint8Array((s.length / 4) * 3 - padding);\n let o = 0;\n for (let i = 0; i < body.length; i += 4) {\n const n0 = B64_LOOKUP[body[i]];\n const n1 = B64_LOOKUP[body[i + 1]];\n const n2 = body[i + 2] !== undefined ? B64_LOOKUP[body[i + 2]] : 0;\n const n3 = body[i + 3] !== undefined ? B64_LOOKUP[body[i + 3]] : 0;\n if (n0 === undefined || n1 === undefined || n2 === undefined || n3 === undefined) {\n throw new SphereError('Invalid field envelope: non-base64 character', 'DECRYPTION_ERROR');\n }\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n if (o < out.length) out[o++] = (triple >> 16) & 0xff;\n if (o < out.length) out[o++] = (triple >> 8) & 0xff;\n if (o < out.length) out[o++] = triple & 0xff;\n }\n return out;\n}\n\n// =============================================================================\n// Key derivation\n// =============================================================================\n\nfunction hexToBytesStrict(hex: string): Uint8Array {\n if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) {\n throw new SphereError('Invalid private key hex for field-encryption key derivation', 'INVALID_CONFIG');\n }\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\n/**\n * Derive the wallet's 32-byte field-encryption key (sdk-changes S6):\n * `HKDF-SHA256(ikm = wallet privkey bytes, info = \"sphere-fieldenc-v1\")`.\n *\n * Deterministic per wallet key — every device holding the seed derives the\n * same key, so envelopes round-trip across devices with no key exchange.\n *\n * @param privKeyHex - wallet secp256k1 private key, hex (the HKDF ikm is its raw bytes)\n */\nexport function deriveFieldEncryptionKey(privKeyHex: string): Uint8Array {\n const ikm = hexToBytesStrict(privKeyHex);\n const info = new TextEncoder().encode(FIELD_ENCRYPTION_HKDF_INFO);\n return hkdf(sha256, ikm, undefined, info, 32);\n}\n\n// =============================================================================\n// Envelope encrypt / decrypt\n// =============================================================================\n\n/**\n * Encrypt a plaintext field into the S6 wire envelope:\n * `\"enc1.\" + base64(nonce ‖ ciphertext)` (XChaCha20-Poly1305, random 24-byte nonce).\n */\nexport function encryptField(key: Uint8Array, plaintext: string): string {\n const nonce = randomBytes(FIELD_ENVELOPE_NONCE_BYTES);\n const ciphertext = xchacha20poly1305(key, nonce).encrypt(new TextEncoder().encode(plaintext));\n const packed = new Uint8Array(nonce.length + ciphertext.length);\n packed.set(nonce, 0);\n packed.set(ciphertext, nonce.length);\n return FIELD_ENVELOPE_PREFIX + base64Encode(packed);\n}\n\n/**\n * Decrypt an S6 wire envelope. Validates the prefix and shape, then opens the\n * XChaCha20-Poly1305 box; any tampering (nonce, ciphertext, or tag) throws a\n * `SphereError('DECRYPTION_ERROR')`.\n */\nexport function decryptField(key: Uint8Array, envelope: string): string {\n if (typeof envelope !== 'string' || !envelope.startsWith(FIELD_ENVELOPE_PREFIX)) {\n throw new SphereError('Invalid field envelope: missing \"enc1.\" prefix', 'DECRYPTION_ERROR');\n }\n const packed = base64Decode(envelope.slice(FIELD_ENVELOPE_PREFIX.length));\n if (packed.length < FIELD_ENVELOPE_NONCE_BYTES + POLY1305_TAG_BYTES) {\n throw new SphereError('Invalid field envelope: too short for nonce + tag', 'DECRYPTION_ERROR');\n }\n const nonce = packed.slice(0, FIELD_ENVELOPE_NONCE_BYTES);\n const ciphertext = packed.slice(FIELD_ENVELOPE_NONCE_BYTES);\n try {\n const plain = xchacha20poly1305(key, nonce).decrypt(ciphertext);\n return new TextDecoder().decode(plain);\n } catch (err) {\n throw new SphereError('Field envelope authentication failed (tampered or wrong key)', 'DECRYPTION_ERROR', err);\n }\n}\n\n/**\n * The server-side shape check (ARCHITECTURE §8.3): the value must be a string,\n * carry the `enc1.` prefix, decode as base64, be long enough to hold a nonce +\n * tag, and fit the size cap. The server never sees plaintext — this is the\n * ONLY validation it performs before storing the exact UTF-8 bytes verbatim.\n *\n * Throws `SphereError('VALIDATION_ERROR')` on any violation.\n */\nexport function assertFieldEnvelopeShape(value: unknown, maxBytes: number = FIELD_ENVELOPE_MAX_BYTES): void {\n if (typeof value !== 'string') {\n throw new SphereError('Field envelope must be a string', 'VALIDATION_ERROR');\n }\n if (!value.startsWith(FIELD_ENVELOPE_PREFIX)) {\n throw new SphereError('Field envelope must start with \"enc1.\"', 'VALIDATION_ERROR');\n }\n if (new TextEncoder().encode(value).length > maxBytes) {\n throw new SphereError(`Field envelope exceeds size cap of ${maxBytes} bytes`, 'VALIDATION_ERROR');\n }\n let packed: Uint8Array;\n try {\n packed = base64Decode(value.slice(FIELD_ENVELOPE_PREFIX.length));\n } catch (err) {\n throw new SphereError('Field envelope payload is not valid base64', 'VALIDATION_ERROR', err);\n }\n if (packed.length < FIELD_ENVELOPE_NONCE_BYTES + POLY1305_TAG_BYTES) {\n throw new SphereError('Field envelope payload too short for nonce + tag', 'VALIDATION_ERROR');\n }\n}\n","/**\n * WalletApiMailboxProvider — the reference `DeliveryProvider` implementation\n * over the wallet-api mailbox (sdk-changes S3/S7; ARCHITECTURE §6/§16).\n *\n * Platform-neutral over the injected {@link WalletApiClient}. Key behaviors:\n *\n * - **deliver** = sha256 + upload via upload-urls (a `412` from the\n * content-addressed store means the blob is already present = success,\n * §5.2) → `POST /v1/mailbox` (idempotent by content-derived `entry_id`,\n * §6). The memo is encrypted client-side with the S6 field key before it\n * leaves the device; the deposit's `entryId` is verified to equal the\n * locally computed content-derived id — a backend substituting row ids is a\n * protocol violation (covenant §3.1-4).\n * - **incoming** = `GET /v1/mailbox?since=` paging (`more` loops), yielding\n * claimable entries (pending; claimed/rejected entries are recorded as seen\n * and skipped — they were resolved here or on another of the owner's\n * devices). `fetchBlob` uses the entry's `getUrl` and re-derives\n * (tokenId, stateHash, deliveryId) from the actual bytes — the recipient\n * never trusts the backend (§8.2). `blobCollected` entries throw a typed\n * error.\n * - **ack** → claim with the provider's **composition-time custody**\n * (`'inventory'` → `intoInventory: true` handoff; `'external'` →\n * `intoInventory: false`, ZERO inventory writes — §6 delivery-only claim),\n * or reject (terminal for discovery only — §6).\n * - **Persistent seen-set**: every acked delivery's content-derived id — the\n * canonical hash of its `(tokenId, stateHash)` pair — is persisted via the\n * injected {@link KeyValueStore}; a replayed delivery (server replay, cursor\n * reset, restore) is never yielded again (S7 port contract).\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport type {\n DeliverOptions,\n DeliveryCustody,\n DeliveryDisposition,\n DeliveryProvider,\n DeliveryReceipt,\n IncomingDelivery,\n} from '../../../transport/delivery-provider';\nimport { composeDeliveryKeys, computeDeliveryId } from '../../../transport/delivery-provider';\nimport { unwrapTokenBlobBytes } from '../../../token-engine/token-blob';\nimport { deriveFieldEncryptionKey, decryptField, encryptField } from '../../../core/field-encryption';\nimport { WalletApiClient, WalletApiError } from '../../../wallet-api';\nimport type { KeyValueStore, MailboxEntry } from '../../../wallet-api';\nimport { logger } from '../../../core/logger';\n\nexport interface WalletApiMailboxProviderConfig {\n /** The authenticated wallet-api client (S1). DI — never a singleton. */\n client: WalletApiClient;\n /**\n * Composition-time custody (S7 — NEVER a per-call flag): `'inventory'` for\n * the full wallet-api preset (acks hand tokens into the server inventory),\n * `'external'` for the own-storage preset (acks perform zero inventory\n * writes; the app's storage keeps custody).\n */\n custody: DeliveryCustody;\n /** Persists the per-identity seen-set + mailbox cursor. */\n stateStore: KeyValueStore;\n /**\n * The backend-true (tokenId, stateHash) derivation (`ITokenEngine.deliveryKeys`).\n * Optional at construction — compositions are engine-less; `PaymentsModule`\n * late-binds it via `bindDeliveryKeys` at init. The provider never derives\n * these locally (§8.2 / sdk-changes S7) and fails loudly if unbound.\n */\n deliveryKeys?: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nconst TAG = 'WalletApiMailbox';\n\nexport class WalletApiMailboxProvider implements DeliveryProvider {\n readonly custody: DeliveryCustody;\n\n private readonly client: WalletApiClient;\n private readonly stateStore: KeyValueStore;\n private deriveKeysFn: ((blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>) | null = null;\n\n private identity: { privateKey: string; chainPubkey: string } | null = null;\n private fieldKey: Uint8Array | null = null;\n\n constructor(config: WalletApiMailboxProviderConfig) {\n this.client = config.client;\n this.custody = config.custody;\n this.stateStore = config.stateStore;\n this.deriveKeysFn = config.deliveryKeys ?? null;\n }\n\n /** @inheritDoc — wired by PaymentsModule at init (engine-owning seam). */\n bindDeliveryKeys(derive: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>): void {\n this.deriveKeysFn = derive;\n }\n\n private deriveKeys(blobBytes: Uint8Array): Promise<{ tokenId: string; stateHash: string }> {\n if (this.deriveKeysFn === null) {\n throw new WalletApiError(\n 'deliveryKeys derivation not bound — PaymentsModule binds ITokenEngine.deliveryKeys at init (S7)',\n 'PROTOCOL'\n );\n }\n return this.deriveKeysFn(blobBytes);\n }\n\n /** Bind the wallet identity (derives the S6 field key; binds the client). */\n setIdentity(identity: { privateKey: string; chainPubkey: string }): void {\n this.identity = identity;\n this.fieldKey = deriveFieldEncryptionKey(identity.privateKey);\n this.client.setIdentity(identity);\n }\n\n // ── persisted state (per network + identity) ────────────────────────────────\n\n private stateKey(kind: string): string {\n if (!this.identity) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n return `wallet-api-mailbox:${kind}:${this.client.network}:${this.identity.chainPubkey}`;\n }\n\n /**\n * The persistent seen-set (S7): content-derived delivery ids — i.e. the\n * canonical `SHA-256(tokenId ‖ stateHash)` encoding of the (tokenId,\n * stateHash) pairs this wallet has already resolved.\n */\n private async readSeen(): Promise<Set<string>> {\n const raw = await this.stateStore.get(this.stateKey('seen'));\n if (!raw) return new Set();\n try {\n return new Set(JSON.parse(raw) as string[]);\n } catch {\n return new Set();\n }\n }\n\n private async addSeen(deliveryIds: string[]): Promise<void> {\n if (deliveryIds.length === 0) return;\n const seen = await this.readSeen();\n for (const id of deliveryIds) seen.add(id);\n await this.stateStore.set(this.stateKey('seen'), JSON.stringify([...seen]));\n }\n\n private async readCursorState(): Promise<{ cursor: bigint; syncEpoch: bigint } | null> {\n const raw = await this.stateStore.get(this.stateKey('cursor'));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as { cursor?: string; syncEpoch?: string };\n if (typeof parsed.cursor !== 'string' || typeof parsed.syncEpoch !== 'string') return null;\n return { cursor: BigInt(parsed.cursor), syncEpoch: BigInt(parsed.syncEpoch) };\n } catch {\n return null;\n }\n }\n\n private async persistCursorState(cursor: bigint, syncEpoch: bigint): Promise<void> {\n await this.stateStore.set(\n this.stateKey('cursor'),\n JSON.stringify({ cursor: cursor.toString(), syncEpoch: syncEpoch.toString() })\n );\n }\n\n // ── deliver (S3: upload → deposit) ──────────────────────────────────────────\n\n /**\n * Idempotent per (token, state): the upload is content-addressed (412 =\n * already present = success — §5.2) and the deposit is idempotent by the\n * content-derived entry_id — it succeeds even after the recipient claimed\n * (§6), which is what makes the sender's journal replay safe.\n */\n async deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt> {\n if (!this.fieldKey) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n const keys = composeDeliveryKeys(await this.deriveKeys(blob));\n\n // §5.2/§8.2: the wallet-api wire carries the RAW token bytes — the\n // cross-port blob argument is the sphere envelope; unwrap at the boundary\n // (the real backend content-addresses and Token.fromCBOR's exactly these\n // bytes; the 39051 envelope never crosses the §16 API).\n const wire = unwrapTokenBlobBytes(blob);\n\n // Upload the finished blob (checksum-bound presigned PUT — §5.2).\n const sha = bytesToHex(sha256(wire));\n const urls = await this.client.getUploadUrls([{ sha256: sha, size: wire.length }]);\n const upload = urls.find((u) => u.sha256 === sha);\n if (!upload) {\n throw new WalletApiError(`upload-urls response missing sha256 ${sha}`, 'PROTOCOL');\n }\n await this.client.uploadBlob(upload.putUrl, wire); // 412 = already present = success (§5.2)\n\n // Deposit (idempotent by entry_id — §6). The memo is S6-encrypted BEFORE\n // it leaves the device (§8.3): the operator never sees plaintext.\n let entryId: string;\n try {\n entryId = await this.client.depositMailbox({\n recipientPubkey,\n key: upload.key,\n transferId: options.transferId,\n stateHash: keys.stateHash,\n tokenId: keys.tokenId,\n ...(options.memo !== undefined ? { memo: encryptField(this.fieldKey, options.memo) } : {}),\n });\n } catch (err) {\n // §6 CONFLICT = the backend already holds a record of EXACTLY this\n // (tokenId, stateHash) with different bytes. Reachable honestly in one\n // way: a resume re-derives the byte-identical TRANSACTION (E.1), but\n // the aggregator RECOMPUTES inclusion proofs per request (st-sdk#126),\n // so once the tree has advanced the rebuilt token blob's proof bytes —\n // and its content-addressed key — differ from the originally-deposited\n // blob's. The state itself is identical and bound to this recipient by\n // its own predicate (§8.2), so the existing entry IS this delivery:\n // succeed idempotently with the content-derived id (the port contract —\n // deliver is idempotent per (token, state), S7). A foreign-spend\n // conflict can never reach here: the engine's E.2 match-verify raises\n // TransferConflictError before anything is delivered.\n if (err instanceof WalletApiError && err.status === 409) {\n return { deliveryId: keys.deliveryId };\n }\n throw err;\n }\n\n // covenant §3.1-4: the id is CONTENT-DERIVED — computed client-side; a\n // backend returning anything else (e.g. a row id) is a protocol violation.\n if (entryId !== keys.deliveryId) {\n throw new WalletApiError(\n `mailbox deposit returned a non-content-derived entryId (got ${entryId}, expected ${keys.deliveryId})`,\n 'PROTOCOL'\n );\n }\n return { deliveryId: keys.deliveryId };\n }\n\n // ── incoming (S3: mailbox pull) ─────────────────────────────────────────────\n\n /**\n * Pull entries since the given cursor (or the persisted one), loop while\n * `more`, and yield claimable deliveries not yet in the seen-set. The\n * persisted cursor advances to the server's read pointer (§6) — entries at\n * or below it are all resolved; the seen-set (not the cursor) is the replay\n * guard, so a cursor reset is safe.\n */\n async *incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery> {\n const persisted = await this.readCursorState();\n let since: bigint;\n if (sinceCursor !== undefined) {\n since = BigInt(sinceCursor);\n } else {\n since = persisted?.cursor ?? 0n;\n }\n\n const seen = await this.readSeen();\n let page = await this.client.listMailbox(since);\n // §5.4: a syncEpoch change (server restore) invalidates cursor continuity —\n // discard the persisted cursor and re-pull from the start; the seen-set\n // absorbs the replays.\n if (persisted !== null && page.syncEpoch !== persisted.syncEpoch && sinceCursor === undefined) {\n page = await this.client.listMailbox(0n);\n }\n\n for (;;) {\n for (const entry of page.entries) {\n if (seen.has(entry.entryId)) continue; // persistent replay guard (S7)\n if (entry.status !== 'unclaimed') {\n // Resolved here earlier or on another of the owner's devices —\n // record and skip (claimed/rejected filtering for the consumer).\n seen.add(entry.entryId);\n await this.addSeen([entry.entryId]);\n continue;\n }\n yield this.toIncomingDelivery(entry);\n }\n await this.persistCursorState(page.readPointer, page.syncEpoch);\n if (!page.more) break;\n const lastSeq = page.entries.length > 0 ? page.entries[page.entries.length - 1].seq : since;\n page = await this.client.listMailbox(lastSeq);\n }\n }\n\n private toIncomingDelivery(entry: MailboxEntry): IncomingDelivery {\n const client = this.client;\n const fieldKey = this.fieldKey;\n const deriveKeys = (bytes: Uint8Array): Promise<{ tokenId: string; stateHash: string }> => this.deriveKeys(bytes);\n\n // S6: decrypt the memo envelope. NOTE: the S6 key is wallet-scoped (HKDF\n // from the wallet key), so only memos written by THIS wallet's key\n // decrypt (self-sends / multi-device). A counterparty's memo fails\n // authentication and is surfaced as absent rather than as ciphertext.\n let memo: string | undefined;\n if (entry.memo !== undefined && fieldKey) {\n try {\n memo = decryptField(fieldKey, entry.memo);\n } catch {\n logger.debug(TAG, `memo for entry ${entry.entryId.slice(0, 12)}… not decryptable with this wallet's key`);\n }\n }\n\n return {\n deliveryId: entry.entryId,\n transferId: entry.transferId,\n senderPubkey: entry.senderPubkey,\n ...(memo !== undefined ? { memo } : {}),\n cursor: entry.seq.toString(),\n async fetchBlob(): Promise<Uint8Array> {\n if (entry.blobCollected || !entry.getUrl) {\n // §6: only RESOLVED entries lose their blob after retention; a\n // pending entry's blob is always retained — treat this as a server\n // contract violation rather than silently skipping value.\n throw new WalletApiError(\n `mailbox entry ${entry.entryId} has no fetchable blob (blobCollected)`,\n 'PROTOCOL'\n );\n }\n const bytes = await client.fetchBlob(entry.getUrl);\n // §8.2: the recipient never trusts the backend — re-derive the\n // content-derived id from the ACTUAL bytes and match it.\n const keys = composeDeliveryKeys(await deriveKeys(bytes));\n if (keys.deliveryId !== entry.entryId) {\n throw new WalletApiError(\n `mailbox blob does not match its entry id (expected ${entry.entryId}, bytes derive ${keys.deliveryId})`,\n 'PROTOCOL'\n );\n }\n return bytes;\n },\n };\n }\n\n // ── ack (claim / reject) ────────────────────────────────────────────────────\n\n async ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void> {\n if (disposition === 'claimed') {\n // Custody is the provider's COMPOSITION-TIME property (S7): the\n // own-storage preset bakes 'external' in, so every claim here sends\n // intoInventory:false — zero inventory writes (§6 delivery-only claim) —\n // even when ack is called with no thought given to options.\n const intoInventory = this.custody === 'inventory';\n const result = await this.client.claimMailbox([deliveryId], intoInventory);\n const failed = result.failed.find((f) => f.entryId === deliveryId);\n if (failed) {\n // §16: the defensive lineage bucket — unreachable in normal flows.\n throw new WalletApiError(\n `mailbox claim failed for ${deliveryId}: ${failed.code}`,\n 'CONFLICT'\n );\n }\n // claimed / alreadyClaimed (idempotent — possibly via another device).\n } else {\n // §6: terminal for discovery only — the entry stays claimable and its\n // blob retained (retryable after e.g. a trustbase fix).\n await this.client.rejectMailbox([deliveryId]);\n }\n await this.addSeen([deliveryId]); // the persistent (tokenId, stateHash) seen-set (S7)\n }\n\n // ── wake (optional hook — §9: a nudge, never a correctness dependency) ──────\n\n onWake(callback: () => void): () => void {\n let closed = false;\n let close: (() => void) | null = null;\n void this.client\n .connectWakeSocket((wake) => {\n if (wake.stream === 'mailbox') callback();\n })\n .then((handle) => {\n if (closed) handle.close();\n else close = () => handle.close();\n })\n .catch((err) => {\n // Wakes are best-effort; polling remains the correctness path (§9).\n logger.debug(TAG, 'wake socket unavailable (polling continues):', err);\n });\n return () => {\n closed = true;\n close?.();\n };\n }\n}\n\nexport function createWalletApiMailboxProvider(\n config: WalletApiMailboxProviderConfig\n): WalletApiMailboxProvider {\n return new WalletApiMailboxProvider(config);\n}\n","/**\n * impl/shared/wallet-api/composition.ts — port composition (sdk-changes S4/S7,\n * covenant §3.1-6).\n *\n * The Sphere frontend is a VIEW: all I/O sits behind three independently\n * swappable ports — storage (`TokenStorageProvider`), delivery\n * (`DeliveryProvider`) and the engine/aggregator config (`OracleProvider`) —\n * selected at composition time. wallet-api ships as ONE implementation of each\n * port, not as the port:\n *\n * - {@link createSphereProviders} — the composition root: take any platform\n * base bundle (`createBrowserProviders` / `createNodeProviders`) and select\n * each port independently.\n * - {@link createWalletApiProviders} — the FULL wallet-api preset: thin\n * storage (server inventory custody) + mailbox delivery with custody\n * `'inventory'`.\n * - {@link createOwnStorageWalletApiProviders} — the delivery-only preset:\n * the app's own (local) storage keeps custody; the mailbox provider is\n * constructed with custody `'external'`, so every ack sends\n * `intoInventory: false` — zero server inventory writes by construction\n * (ARCHITECTURE §6 delivery-only claim; never a per-call flag).\n *\n * Asset-transport routing (S4): composing a delivery port moves ASSETS to it;\n * messaging, group chat and nametag bindings stay on the Nostr transport in\n * the base bundle. Payment requests ride wallet-api whenever the `walletApi`\n * client these presets return is passed to `Sphere.init` — `PaymentsModule`\n * detects the §16 payment-request capability on it and does not install the\n * Nostr payment-request channel (sdk-changes S4).\n */\n\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../../../storage';\nimport type { TransportProvider } from '../../../transport';\nimport type { DeliveryProvider } from '../../../transport/delivery-provider';\nimport type { OracleProvider } from '../../../oracle';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport { WalletApiClient } from '../../../wallet-api';\nimport type { FetchLike, KeyValueStore, WebSocketFactoryLike } from '../../../wallet-api';\nimport { WalletApiTokenStorageProvider } from './WalletApiTokenStorageProvider';\nimport { WalletApiMailboxProvider } from './WalletApiMailboxProvider';\n\n// =============================================================================\n// Composition root\n// =============================================================================\n\n/** The minimum any platform base bundle provides (browser/node factories do). */\nexport interface SphereBaseProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n tokenStorage: TokenStorageProvider<TxfStorageDataBase>;\n}\n\n/** Independent port selection (sdk-changes S7): any combination is legal. */\nexport interface SphereProviderPorts {\n /** Storage port override (token inventory + blob custody). */\n storage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Delivery port (assets move to it; messaging stays on the base transport). */\n delivery?: DeliveryProvider;\n /** Engine/aggregator config port override. */\n engine?: OracleProvider;\n}\n\n/**\n * Compose a Sphere provider bundle with each port selected independently\n * (covenant §3.1-6). Unselected ports keep the base bundle's implementation.\n */\nexport function createSphereProviders<B extends SphereBaseProviders>(\n base: B,\n ports: SphereProviderPorts = {}\n): B & { delivery?: DeliveryProvider } {\n return {\n ...base,\n ...(ports.storage ? { tokenStorage: ports.storage } : {}),\n ...(ports.engine ? { oracle: ports.engine } : {}),\n ...(ports.delivery ? { delivery: ports.delivery } : {}),\n };\n}\n\n// =============================================================================\n// wallet-api presets (S4)\n// =============================================================================\n\nexport interface WalletApiCompositionConfig {\n /** Backend base URL — https off-loopback (ARCHITECTURE §4, client-enforced). */\n baseUrl: string;\n /** Network name (e.g. 'testnet2') — required end-to-end (ARCHITECTURE §14). */\n network: string;\n /**\n * Stable per-device label (ARCHITECTURE §4 — one session row per (owner,\n * device); the refresh token is stored under it). Pass a persisted value;\n * when omitted a fresh random label is generated per construction, which\n * still works but starts every run with a challenge sign-in.\n */\n deviceId?: string;\n /** Reuse an existing client (tests / advanced wiring). */\n client?: WalletApiClient;\n /** Refresh-token / cursor / seen-set persistence; defaults to `base.storage`. */\n stateStore?: KeyValueStore;\n /** Injectable fetch (defaults to `globalThis.fetch`). */\n fetchFn?: FetchLike;\n /** Injectable WebSocket factory (defaults to `globalThis.WebSocket`). */\n webSocketFactory?: WebSocketFactoryLike;\n /** Local token verification for `recoverRemoved()` (wire the engine here). */\n verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n}\n\n/** What the wallet-api presets add to the base bundle. */\nexport interface WalletApiProviderExtras {\n delivery: DeliveryProvider;\n /**\n * The S1 client — pass to `Sphere.init({ walletApi })` for the S4 auth\n * lifecycle. NOT optional in spirit (#515): wallet-api CUSTODY artifacts\n * (the thin storage provider, delivery custody `'inventory'`) without this\n * client are an illegal composition — `Sphere.init` /\n * `PaymentsModule.initialize` throw `INVALID_CONFIG` (fail-closed) instead\n * of silently degrading to local-custody semantics.\n */\n walletApi: WalletApiClient;\n}\n\nfunction buildClient(base: SphereBaseProviders, config: WalletApiCompositionConfig): {\n client: WalletApiClient;\n stateStore: KeyValueStore;\n} {\n // StorageProvider satisfies KeyValueStore structurally (get/set/remove);\n // all client/provider keys are self-namespaced per network + pubkey.\n const stateStore: KeyValueStore = config.stateStore ?? base.storage;\n const client =\n config.client ??\n new WalletApiClient({\n baseUrl: config.baseUrl,\n network: config.network,\n deviceId: config.deviceId ?? `sphere-${crypto.randomUUID()}`,\n storage: stateStore,\n fetchFn: config.fetchFn,\n webSocketFactory: config.webSocketFactory,\n });\n return { client, stateStore };\n}\n\n/**\n * The FULL wallet-api preset (S4): wallet-api keeps inventory custody —\n * thin/lazy storage over the server's value index + mailbox delivery with\n * custody `'inventory'` (claims perform the §6 ownership handoff).\n */\nexport function createWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & WalletApiProviderExtras {\n const { client, stateStore } = buildClient(base, config);\n const tokenStorage = new WalletApiTokenStorageProvider({\n client,\n stateStore,\n verifyToken: config.verifyToken,\n });\n const delivery = new WalletApiMailboxProvider({ client, custody: 'inventory', stateStore });\n return { ...base, tokenStorage, delivery, walletApi: client };\n}\n\n/**\n * The OWN-STORAGE preset (S7: own storage + wallet-api delivery — a\n * supported, tested composition): the base bundle's local storage keeps\n * custody; wallet-api is purely the delivery rail. Custody `'external'` is\n * baked in at construction — every ack sends `intoInventory: false`, so a\n * recipient's claim performs ZERO inventory writes even when `ack` is called\n * with no thought given to options (sdk-changes S7). Senders never call\n * apply; their sends close via `intents/{id}/complete` alone (ARCHITECTURE\n * §6 storage-opt-out senders), which the wallet-api client (passed as\n * `walletApi`) provides.\n */\nexport function createOwnStorageWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & WalletApiProviderExtras {\n const { client, stateStore } = buildClient(base, config);\n const delivery = new WalletApiMailboxProvider({ client, custody: 'external', stateStore });\n return { ...base, delivery, walletApi: client };\n}\n","/**\n * Browser Download Utilities\n * Functions for downloading wallet backups as files\n */\n\nimport type { Sphere } from '../../core/Sphere';\nimport type { WalletJSON, WalletJSONExportOptions } from '../../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DownloadTextOptions {\n /** Password for encryption */\n password?: string;\n /** Number of addresses to include */\n addressCount?: number;\n /** Custom filename (without extension) */\n filename?: string;\n}\n\nexport interface DownloadJSONOptions extends WalletJSONExportOptions {\n /** Custom filename (without extension) */\n filename?: string;\n /** Pretty print JSON (default: true) */\n pretty?: boolean;\n}\n\n// =============================================================================\n// Core Download Function\n// =============================================================================\n\n/**\n * Download content as a file in the browser\n */\nexport function downloadFile(\n content: string | Blob,\n filename: string,\n mimeType: string = 'text/plain'\n): void {\n const blob = content instanceof Blob\n ? content\n : new Blob([content], { type: mimeType });\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n\n link.href = url;\n link.download = filename;\n link.click();\n\n // Clean up the URL after a short delay\n setTimeout(() => URL.revokeObjectURL(url), 100);\n}\n\n/**\n * Download text content as a .txt file\n */\nexport function downloadTextFile(content: string, filename: string): void {\n downloadFile(content, filename, 'text/plain');\n}\n\n/**\n * Download JSON content as a .json file\n */\nexport function downloadJSONFile(content: object | string, filename: string): void {\n const jsonString = typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2);\n downloadFile(jsonString, filename, 'application/json');\n}\n\n// =============================================================================\n// Wallet Download Functions\n// =============================================================================\n\n/**\n * Download wallet backup as text file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletText(sphere);\n *\n * // Download encrypted backup\n * downloadWalletText(sphere, { password: 'secret' });\n *\n * // Custom filename\n * downloadWalletText(sphere, { filename: 'my-backup' });\n * ```\n */\nexport function downloadWalletText(sphere: Sphere, options: DownloadTextOptions = {}): void {\n const content = sphere.exportToTxt({\n password: options.password,\n addressCount: options.addressCount,\n });\n\n const filename = options.filename\n ? `${options.filename}.txt`\n : `sphere-wallet-${Date.now()}.txt`;\n\n downloadTextFile(content, filename);\n}\n\n/**\n * Download wallet backup as JSON file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletJSON(sphere);\n *\n * // Download encrypted backup\n * downloadWalletJSON(sphere, { password: 'secret' });\n *\n * // Include multiple addresses\n * downloadWalletJSON(sphere, { addressCount: 5 });\n * ```\n */\nexport function downloadWalletJSON(sphere: Sphere, options: DownloadJSONOptions = {}): void {\n const json = sphere.exportToJSON({\n password: options.password,\n addressCount: options.addressCount,\n includeMnemonic: options.includeMnemonic,\n });\n\n const filename = options.filename\n ? `${options.filename}.json`\n : `sphere-wallet-${Date.now()}.json`;\n\n const jsonString = options.pretty !== false\n ? JSON.stringify(json, null, 2)\n : JSON.stringify(json);\n\n downloadFile(jsonString, filename, 'application/json');\n}\n\n/**\n * Download pre-built WalletJSON as file\n */\nexport function downloadWalletJSONData(json: WalletJSON, filename?: string): void {\n const name = filename || `sphere-wallet-${Date.now()}.json`;\n downloadJSONFile(json, name.endsWith('.json') ? name : `${name}.json`);\n}\n\n// =============================================================================\n// File Reading Utilities\n// =============================================================================\n\n/**\n * Read a file as text\n */\nexport function readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\n/**\n * Read a file as ArrayBuffer (for binary files like .dat)\n */\nexport function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(file);\n });\n}\n\n/**\n * Read a file as Uint8Array (for binary files like .dat)\n */\nexport async function readFileAsUint8Array(file: File): Promise<Uint8Array> {\n const buffer = await readFileAsArrayBuffer(file);\n return new Uint8Array(buffer);\n}\n","/**\n * Centralized network-config resolution + a startup consistency assertion.\n *\n * Principle: resolve a network's full configuration in ONE place, and refuse to\n * run a network whose configuration is provably broken (e.g. a null or mismatched\n * trust base would silently accept unverified tokens — a funds risk).\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport { getEmbeddedTrustBase } from './trustbase-loader';\nimport { SphereError } from '../../core/errors';\n\n/**\n * Known trust-base networkId per network. The embedded trust base for a network\n * MUST carry this id; a mismatch means the wrong trust base is wired to a network\n * (e.g. the testnet trust base under testnet2), which would verify tokens against\n * the wrong chain. mainnet/dev have no pinned id yet (mainnet is not onboarded).\n */\nconst EXPECTED_NETWORK_ID: Partial<Record<NetworkType, number>> = {\n // v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),\n // so both expect the testnet2 trust base (networkId 4).\n testnet: 4,\n testnet2: 4,\n};\n\nexport interface ResolvedNetworkConfig {\n readonly network: NetworkType;\n readonly config: NetworkConfig;\n readonly trustBase: unknown | null;\n readonly networkId: number | undefined;\n}\n\n/** Resolve a network's full config + embedded trust base in one place. */\nexport function resolveNetworkConfig(network: NetworkType): ResolvedNetworkConfig {\n const config = NETWORKS[network];\n const trustBase = getEmbeddedTrustBase(network);\n const networkId = (trustBase as { networkId?: number } | null)?.networkId;\n return { network, config, trustBase, networkId };\n}\n\n/**\n * Fail loud if a network is unsafe to run. Conservative on purpose: it rejects\n * only provably-broken configs, so currently-valid networks (testnet/testnet2/dev)\n * keep working while mainnet — which has no embedded trust base — is blocked until\n * onboarded (otherwise it would silently accept unverified tokens). The stricter\n * hygiene checks (e.g. no cross-network registry reuse) live in the per-network CI\n * test, not at runtime, so they can't break a valid deployment.\n */\nexport function assertNetworkConsistency(network: NetworkType): void {\n const { trustBase, networkId } = resolveNetworkConfig(network);\n if (trustBase == null) {\n throw new SphereError(\n `Network \"${network}\" has no embedded trust base — refusing to run (tokens would be unverified).`,\n 'INVALID_CONFIG',\n );\n }\n const expected = EXPECTED_NETWORK_ID[network];\n if (expected !== undefined && networkId !== expected) {\n throw new SphereError(\n `Network \"${network}\" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,\n 'INVALID_CONFIG',\n );\n }\n}\n","/**\n * 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';\nimport type { NetworkType } from '../../../constants';\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 network?: NetworkType,\n): Promise<{ keyPair: unknown; ipnsName: string }> {\n const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadLibp2pModules();\n\n // Per-network IPNS: bake the network into the HKDF info so the same wallet key derives a\n // DISTINCT IPNS identity per network — prevents a v2 wallet re-pulling v1 tokens from the\n // old (shared) IPNS record. No network → legacy info (backward compatible).\n const info = network ? `${IPNS_HKDF_INFO}:${network}` : IPNS_HKDF_INFO;\n const derivedKey = deriveEd25519KeyMaterial(privateKeyHex, info);\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 network?: NetworkType,\n): Promise<string> {\n const { ipnsName } = await deriveIpnsIdentity(privateKeyHex, network);\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 /** IPNS context captured at save() time — ensures flush writes to the correct\n * IPNS record even if identity changes between save() and flush(). */\n capturedIpnsKeyPair: unknown | null = null;\n capturedIpnsName: string | null = null;\n\n get isEmpty(): boolean {\n return this.txfData === null;\n }\n\n clear(): void {\n this.txfData = null;\n this.capturedIpnsKeyPair = null;\n this.capturedIpnsName = 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 this.capturedIpnsKeyPair = other.capturedIpnsKeyPair;\n this.capturedIpnsName = other.capturedIpnsName;\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 InventoryView,\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n} from '../../../storage';\nimport { WholeBlobInventoryAdapter } from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\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 /** Stored config for createForAddress() cloning */\n private readonly _config: IpfsStorageConfig | undefined;\n private readonly _statePersistenceCtor: IpfsStatePersistence | undefined;\n\n constructor(\n config?: IpfsStorageConfig,\n statePersistence?: IpfsStatePersistence,\n ) {\n this._config = config;\n this._statePersistenceCtor = statePersistence;\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, this._config?.network);\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 logger.debug('IPFS-Storage', `shutdown: ipnsName=${this.ipnsName?.slice(0, 20)}..., pendingEmpty=${this.pendingBuffer.isEmpty}, capturedIpns=${this.pendingBuffer.capturedIpnsName?.slice(0, 20) ?? 'none'}`);\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 // Capture IPNS context NOW so the flush writes to the correct IPNS record\n // even if identity changes between save() and flush() (address switch).\n this.pendingBuffer.txfData = data;\n this.pendingBuffer.capturedIpnsKeyPair = this.ipnsKeyPair;\n this.pendingBuffer.capturedIpnsName = this.ipnsName;\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, overrideIpns?: { keyPair: unknown; name: string }): Promise<SaveResult> {\n const ipnsKeyPair = overrideIpns?.keyPair ?? this.ipnsKeyPair;\n const ipnsName = overrideIpns?.name ?? this.ipnsName;\n\n const metaAddr = (data as unknown as TxfStorageDataBase)?._meta?.address;\n logger.debug('IPFS-Storage', `_doSave: ipnsName=${ipnsName?.slice(0, 20)}..., override=${!!overrideIpns}, meta.address=${metaAddr?.slice(0, 20) ?? 'none'}`);\n\n if (!ipnsKeyPair || !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: 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 ipnsKeyPair,\n cid,\n newSeq,\n this.ipnsLifetimeMs,\n );\n\n // Publish to all gateways\n const publishResult = await this.httpClient.publishIpns(\n 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(ipnsName, {\n cid,\n sequence: newSeq,\n gateway: 'local',\n });\n this.cache.setContent(cid, updatedData as unknown as TxfStorageDataBase);\n this.cache.markIpnsFresh(ipnsName);\n\n // Persist state\n await this.statePersistence.save(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 // Use captured IPNS context from save() time — prevents writing to wrong\n // IPNS record if identity changed between save() and flush().\n const overrideIpns = active.capturedIpnsKeyPair && active.capturedIpnsName\n ? { keyPair: active.capturedIpnsKeyPair, name: active.capturedIpnsName }\n : undefined;\n const result = await this._doSave(baseData, overrideIpns);\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 // ── Lazy inventory port (sdk-changes S2) — default whole-blob adapter ──────\n\n /** Default lazy-port adapter over this store (sdk-changes S2). */\n private readonly inventoryAdapter = new WholeBlobInventoryAdapter<TData>(this);\n\n listInventory(since?: bigint): Promise<InventoryView> {\n return this.inventoryAdapter.listInventory(since);\n }\n\n getToken(tokenId: string): Promise<TokenBlob> {\n return this.inventoryAdapter.getToken(tokenId);\n }\n\n applyDelta(transferId: string, spent: string[], added: ApplyDeltaAdded[], opts?: ApplyDeltaOptions): Promise<void> {\n return this.inventoryAdapter.applyDelta(transferId, spent, added, opts);\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 * Create an independent instance for a different address.\n * Shares the same gateway/timeout config but has fresh IPNS state.\n */\n createForAddress(): IpfsStorageProvider<TData> {\n return new IpfsStorageProvider<TData>(this._config, this._statePersistenceCtor);\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 * Per-network cache keys. The registry cache MUST be namespaced by remoteUrl:\n * testnet and testnet2 ship DIFFERENT coinIds for the same symbol, so a single\n * global key let a stale testnet snapshot be served for a testnet2 request —\n * wrong/missing icons AND self-mint resolving the wrong-network coinId. Keying\n * by remoteUrl isolates the snapshots so a network can never read another's.\n */\n private cacheKeys(): { data: string; ts: string } {\n const base = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE;\n const baseTs = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS;\n // No remoteUrl configured (local-only usage): keep the legacy bare key.\n if (!this.remoteUrl) return { data: base, ts: baseTs };\n // Namespace by remoteUrl so testnet and testnet2 snapshots never collide.\n return { data: `${base}:${this.remoteUrl}`, ts: `${baseTs}:${this.remoteUrl}` };\n }\n\n /**\n * Load definitions from StorageProvider cache.\n * Only applies if cache exists and is fresh (within refreshIntervalMs).\n */\n private async loadFromCache(): Promise<boolean> {\n if (!this.storage) return false;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n const [cached, cachedTs] = await Promise.all([\n this.storage.get(dataKey),\n this.storage.get(tsKey),\n ]);\n\n if (!cached || !cachedTs) return false;\n\n const ts = parseInt(cachedTs, 10);\n if (isNaN(ts)) return false;\n\n // Check freshness\n const age = Date.now() - ts;\n if (age > this.refreshIntervalMs) return false;\n\n // Don't overwrite data from a more recent remote fetch\n if (this.lastRefreshAt > ts) return false;\n\n const data: unknown = JSON.parse(cached);\n if (!this.isValidDefinitionsArray(data)) return false;\n\n this.applyDefinitions(data as TokenDefinition[]);\n this.lastRefreshAt = ts;\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Save definitions to StorageProvider cache.\n */\n private async saveToCache(definitions: TokenDefinition[]): Promise<void> {\n if (!this.storage) return;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n await Promise.all([\n this.storage.set(dataKey, JSON.stringify(definitions)),\n this.storage.set(tsKey, String(Date.now())),\n ]);\n } catch {\n // Cache save failure is non-critical\n }\n }\n\n // ===========================================================================\n // Remote Refresh\n // ===========================================================================\n\n /**\n * Apply an array of token definitions to the internal maps.\n * Clears existing data before applying.\n */\n private applyDefinitions(definitions: TokenDefinition[]): void {\n this.definitionsById.clear();\n this.definitionsBySymbol.clear();\n this.definitionsByName.clear();\n\n for (const def of definitions) {\n const idLower = def.id.toLowerCase();\n this.definitionsById.set(idLower, def);\n\n if (def.symbol) {\n this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);\n }\n\n this.definitionsByName.set(def.name.toLowerCase(), def);\n }\n }\n\n /**\n * Validate that data is an array of objects with 'id' field\n */\n private isValidDefinitionsArray(data: unknown): boolean {\n return Array.isArray(data) && data.every((item) => item && typeof item === 'object' && 'id' in item);\n }\n\n /**\n * Fetch token definitions from the remote URL and update the registry.\n * On success, also persists to StorageProvider cache.\n * Returns true on success, false on failure. On failure, existing data is preserved.\n * Concurrent calls are deduplicated — only one fetch runs at a time.\n */\n async refreshFromRemote(): Promise<boolean> {\n if (!this.remoteUrl) {\n return false;\n }\n\n // Deduplicate concurrent calls\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doRefresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doRefresh(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(this.remoteUrl!, {\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n logger.warn('TokenRegistry', `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);\n return false;\n }\n\n const data: unknown = await response.json();\n\n if (!this.isValidDefinitionsArray(data)) {\n logger.warn('TokenRegistry', 'Remote data is not a valid token definitions array');\n return false;\n }\n\n const definitions = data as TokenDefinition[];\n this.applyDefinitions(definitions);\n this.lastRefreshAt = Date.now();\n\n // Persist to cache (fire-and-forget)\n this.saveToCache(definitions);\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn('TokenRegistry', `Remote refresh failed: ${message}`);\n return false;\n }\n }\n\n /**\n * Start periodic auto-refresh from the remote URL.\n * Does an immediate fetch, then repeats at the configured interval.\n */\n startAutoRefresh(intervalMs?: number): void {\n this.stopAutoRefresh();\n\n if (intervalMs !== undefined) {\n this.refreshIntervalMs = intervalMs;\n }\n\n // Immediate first fetch (fire-and-forget)\n this.refreshFromRemote();\n\n this.refreshTimer = setInterval(() => {\n this.refreshFromRemote();\n }, this.refreshIntervalMs);\n }\n\n /**\n * Stop periodic auto-refresh\n */\n stopAutoRefresh(): void {\n if (this.refreshTimer !== null) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n /**\n * Timestamp of the last successful remote refresh (0 if never refreshed)\n */\n getLastRefreshAt(): number {\n return this.lastRefreshAt;\n }\n\n // ===========================================================================\n // Lookup Methods\n // ===========================================================================\n\n /**\n * Get token definition by hex coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined if not found\n */\n getDefinition(coinId: string): TokenDefinition | undefined {\n if (!coinId) return undefined;\n return this.definitionsById.get(coinId.toLowerCase());\n }\n\n /**\n * Get token definition by symbol (e.g., \"UCT\", \"BTC\")\n * @param symbol - Token symbol (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionBySymbol(symbol: string): TokenDefinition | undefined {\n if (!symbol) return undefined;\n return this.definitionsBySymbol.get(symbol.toUpperCase());\n }\n\n /**\n * Get token definition by name (e.g., \"bitcoin\", \"ethereum\")\n * @param name - Token name (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionByName(name: string): TokenDefinition | undefined {\n if (!name) return undefined;\n return this.definitionsByName.get(name.toLowerCase());\n }\n\n /**\n * Get token symbol for a coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol (e.g., \"UCT\") or truncated ID if not found\n */\n getSymbol(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.symbol) {\n return def.symbol;\n }\n // Fallback: return first 6 chars of ID uppercased\n return coinId.slice(0, 6).toUpperCase();\n }\n\n /**\n * Get token name for a coin ID\n * @param coinId - 64-character hex string\n * @returns Name (e.g., \"Bitcoin\") or coin ID if not found\n */\n getName(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.name) {\n // Capitalize first letter\n return def.name.charAt(0).toUpperCase() + def.name.slice(1);\n }\n return coinId;\n }\n\n /**\n * Get decimal places for a coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0 if not found\n */\n getDecimals(coinId: string): number {\n const def = this.getDefinition(coinId);\n return def?.decimals ?? 0;\n }\n\n /**\n * Get icon URL for a coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG format over SVG\n * @returns Icon URL or null if not found\n */\n getIconUrl(coinId: string, preferPng = true): string | null {\n const def = this.getDefinition(coinId);\n if (!def?.icons || def.icons.length === 0) {\n return null;\n }\n\n if (preferPng) {\n const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes('.png'));\n if (pngIcon) return pngIcon.url;\n }\n\n return def.icons[0].url;\n }\n\n /**\n * Check if a coin ID is known in the registry\n * @param coinId - 64-character hex string\n * @returns true if the coin is in the registry\n */\n isKnown(coinId: string): boolean {\n return this.definitionsById.has(coinId.toLowerCase());\n }\n\n /**\n * Get all token definitions\n * @returns Array of all token definitions\n */\n getAllDefinitions(): TokenDefinition[] {\n return Array.from(this.definitionsById.values());\n }\n\n /**\n * Get all fungible token definitions\n * @returns Array of fungible token definitions\n */\n getFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'fungible');\n }\n\n /**\n * Get all non-fungible token definitions\n * @returns Array of non-fungible token definitions\n */\n getNonFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'non-fungible');\n }\n\n /**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdBySymbol(symbol: string): string | undefined {\n const def = this.getDefinitionBySymbol(symbol);\n return def?.id;\n }\n\n /**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdByName(name: string): string | undefined {\n const def = this.getDefinitionByName(name);\n return def?.id;\n }\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Get token definition by coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined\n */\nexport function getTokenDefinition(coinId: string): TokenDefinition | undefined {\n return TokenRegistry.getInstance().getDefinition(coinId);\n}\n\n/**\n * Get token symbol by coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol or truncated ID\n */\nexport function getTokenSymbol(coinId: string): string {\n return TokenRegistry.getInstance().getSymbol(coinId);\n}\n\n/**\n * Get token name by coin ID\n * @param coinId - 64-character hex string\n * @returns Name or coin ID\n */\nexport function getTokenName(coinId: string): string {\n return TokenRegistry.getInstance().getName(coinId);\n}\n\n/**\n * Get token decimals by coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0\n */\nexport function getTokenDecimals(coinId: string): number {\n return TokenRegistry.getInstance().getDecimals(coinId);\n}\n\n/**\n * Get token icon URL by coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG over SVG\n * @returns Icon URL or null\n */\nexport function getTokenIconUrl(coinId: string, preferPng = true): string | null {\n return TokenRegistry.getInstance().getIconUrl(coinId, preferPng);\n}\n\n/**\n * Check if coin ID is in registry\n * @param coinId - 64-character hex string\n * @returns true if known\n */\nexport function isKnownToken(coinId: string): boolean {\n return TokenRegistry.getInstance().isKnown(coinId);\n}\n\n/**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdBySymbol(symbol: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdBySymbol(symbol);\n}\n\n/**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdByName(name: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdByName(name);\n}\n\n/**\n * Normalize a coin identifier to its canonical hash coinId.\n *\n * Accepts both symbolic names (\"BTC\", \"ETH\") and hash coinIds (64-char hex).\n * If the input is a short symbol, resolves it via the TokenRegistry.\n * Returns the input unchanged if it's already a hash coinId or unknown.\n *\n * @public\n *\n * @remarks\n * Heuristic: inputs matching `length <= 20 && /^[A-Za-z0-9]+$/` are treated\n * as symbolic names and looked up in the registry. Long inputs, hyphenated\n * inputs (\"TOKEN-123\"), or dotted inputs (\"BTC.CASH\") pass through unchanged.\n *\n * **Stability:** This function depends on `TokenRegistry` content. Adding a\n * new symbol that shadows a previously-unknown short alphanumeric coinId\n * changes normalization semantics retroactively. Consumers building stable\n * keys against this output should be aware that registry growth is a\n * non-breaking change *for unknown inputs* but a breaking change *for inputs\n * that newly resolve*.\n *\n * @param coinId - A symbolic name or hash coinId.\n * @returns The canonical hash coinId, or the original string if not resolvable.\n */\nexport function normalizeCoinId(coinId: string): string {\n // Short alphanumeric strings are likely symbolic names (BTC, ETH, USDU, etc.)\n if (coinId.length <= 20 && /^[A-Za-z0-9]+$/.test(coinId)) {\n const resolved = getCoinIdBySymbol(coinId);\n if (resolved) return resolved;\n }\n return coinId;\n}\n\n/**\n * Compare two coin identifiers for equality, normalizing both sides.\n *\n * Handles mixed-format comparisons: \"BTC\" vs hash coinId, or two hash coinIds.\n * Both values are normalized to hash coinIds via the TokenRegistry before comparison.\n *\n * @public\n *\n * @remarks\n * Reflexive byte-equality short-circuit comes first; otherwise both sides\n * are normalized via {@link normalizeCoinId} and compared. Inherits the\n * registry-dependence caveat from `normalizeCoinId`. Two distinct symbols\n * that both fail to resolve will compare unequal even if they're semantic\n * aliases — register them as aliases in `TokenRegistry` for matching.\n *\n * @param a - First coin identifier (symbol or hash coinId).\n * @param b - Second coin identifier (symbol or hash coinId).\n * @returns true if both resolve to the same canonical coinId.\n */\nexport function coinIdsMatch(a: string, b: string): boolean {\n if (a === b) return true;\n return normalizeCoinId(a) === normalizeCoinId(b);\n}\n","/**\n * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n 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,\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":["Buffer","isLE","bytesToHex","hexToBytes","DB_NAME","DB_VERSION","connectionSeq","db","Buffer","Buffer","success","message","senderNametag","hexToBytes","bytesToHex","hexToBytes","hexToBytes","bytesToHex","isBytes","anumber","abytes","aexists","aoutput","clean","createView","abytes","isLE","anumber","createView","anumber","abytes","clean","abytes","clean","aexists","aoutput","abytes","clean","bytesToHex","hexToBytes","data","saveResult","createBrowserWebSocket","netConfig","Buffer"]}
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","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha2.ts","../../../token-engine/sdk.ts","../../../token-engine/token-blob.ts","../../../storage/whole-blob-inventory-adapter.ts","../../../impl/browser/storage/IndexedDBTokenStorageProvider.ts","../../../transport/NostrTransportProvider.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","../../../core/crypto.ts","../../../wallet-api/errors.ts","../../../wallet-api/challenge.ts","../../../wallet-api/codec.ts","../../../wallet-api/client.ts","../../../impl/shared/wallet-api/WalletApiTokenStorageProvider.ts","../../../transport/delivery-provider.ts","../../../node_modules/@noble/hashes/src/hmac.ts","../../../node_modules/@noble/hashes/src/hkdf.ts","../../../node_modules/@noble/ciphers/src/utils.ts","../../../node_modules/@noble/ciphers/src/_arx.ts","../../../node_modules/@noble/ciphers/src/_poly1305.ts","../../../node_modules/@noble/ciphers/src/chacha.ts","../../../core/field-encryption.ts","../../../impl/shared/wallet-api/WalletApiMailboxProvider.ts","../../../impl/shared/wallet-api/composition.ts","../../../impl/browser/download.ts","../../../impl/shared/network.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';\n// Wallet-api storage provider (platform-neutral, sdk-changes S2)\nexport * from '../shared/wallet-api';\n\nexport * from './download';\n\n// Re-export shared types for convenience\nexport type {\n BaseTransportConfig,\n BaseOracleConfig,\n L1Config,\n BaseProviders,\n} from '../shared';\n\n// =============================================================================\n// Convenience Factory\n// =============================================================================\n\nimport { logger as sdkLogger } from '../../core/logger';\nimport { SphereError } from '../../core/errors';\nimport { assertNetworkConsistency } from '../shared/network';\nimport { createIndexedDBStorageProvider, type IndexedDBStorageProviderConfig, 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 // Fail loud: a missing network would silently load the wrong-network providers.\n if (!config?.network) {\n throw new SphereError('createBrowserProviders: config.network is required.', 'INVALID_CONFIG');\n }\n const network = config.network;\n // Refuse provably-broken networks (e.g. a null/mismatched trust base would\n // silently accept unverified tokens) before building any provider.\n assertNetworkConsistency(network);\n\n // Configure global logger: top-level debug enables all, per-provider overrides are additive.\n // Only override global debug flag when explicitly provided — don't reset a previously-configured value.\n if (config?.debug !== undefined) {\n sdkLogger.configure({ debug: config.debug });\n }\n if (config?.transport?.debug) sdkLogger.setTagDebug('Nostr', true);\n if (config?.oracle?.debug) sdkLogger.setTagDebug('Aggregator', true);\n if (config?.price?.debug) sdkLogger.setTagDebug('Price', true);\n\n // Resolve configurations using shared utilities\n const transportConfig = resolveTransportConfig(network, config?.transport);\n const oracleConfig = resolveOracleConfig(network, config?.oracle);\n const l1Config = resolveL1Config(network, config?.l1);\n const tokenSyncConfig = resolveTokenSyncConfig(network, config?.tokenSync);\n\n const storage = createIndexedDBStorageProvider({ ...config?.storage, network });\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 network,\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({ network }),\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 // The source state was consumed by a DIFFERENT transaction (lost race, not a\n // resume) — raised as TransferConflictError (token-engine/errors.ts, Part E.2).\n | 'TRANSFER_CONFLICT'\n | '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 // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n\n 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 /** Last processed Nostr DM (gift-wrap) event timestamp (unix seconds), keyed per pubkey */\n LAST_DM_EVENT_TS: 'last_dm_event_ts',\n /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\n /** Cached token registry JSON (fetched from remote) */\n TOKEN_REGISTRY_CACHE: 'token_registry_cache',\n /** Timestamp of last token registry cache update (ms since epoch) */\n TOKEN_REGISTRY_CACHE_TS: 'token_registry_cache_ts',\n /** Cached price data JSON (from CoinGecko or other provider) */\n PRICE_CACHE: 'price_cache',\n /** Timestamp of last price cache update (ms since epoch) */\n PRICE_CACHE_TS: 'price_cache_ts',\n} as const;\n\n/**\n * Per-address storage keys (one per derived address)\n * Final key format: sphere_{DIRECT_xxx_yyy}_{key}\n * Example: sphere_DIRECT_abc123_xyz789_pending_transfers\n *\n * Note: Token data (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 /**\n * FINISHED v2 token blobs awaiting transport delivery. Written the moment a\n * transfer/split output is certified on-chain (the source is already spent),\n * removed after successful delivery — survives transport failures + crashes\n * so the recipient's token is never lost with the process.\n */\n PENDING_V2_DELIVERIES: 'pending_v2_deliveries',\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 // Invoice / Accounting storage keys\n /** Set of cancelled invoice IDs (JSON string array) */\n CANCELLED_INVOICES: 'cancelled_invoices',\n /** Set of closed invoice IDs (JSON string array) */\n CLOSED_INVOICES: 'closed_invoices',\n /** Frozen balances for terminated invoices (JSON map: invoiceId → FrozenInvoiceBalances) */\n FROZEN_BALANCES: 'frozen_balances',\n /** Auto-return settings (JSON: AutoReturnSettings) */\n AUTO_RETURN: 'auto_return',\n /** Auto-return dedup ledger (JSON: AutoReturnLedger) */\n AUTO_RETURN_LEDGER: 'auto_return_ledger',\n /** Invoice-transfer index metadata (JSON: Record<invoiceId, { terminated, frozenAt? }>) */\n INV_LEDGER_INDEX: 'inv_ledger_index',\n /** Token scan state watermarks (JSON: Record<tokenId, txCount>) */\n TOKEN_SCAN_STATE: 'token_scan_state',\n // Swap storage keys\n /** Per-swap key: swap:{swapId} */\n SWAP_RECORD_PREFIX: 'swap:',\n /** Lightweight index array for listing */\n SWAP_INDEX: 'swap_index',\n} as const;\n\n/**\n * Per-address keys that are ALSO per-network: token/payment operational state. Mixing these\n * across networks is unsafe (e.g. a testnet2 auto-return ledger firing a real send on mainnet).\n * Chat/identity per-address keys (CONVERSATIONS/MESSAGES/GROUP_CHAT_*) are network-AGNOSTIC and\n * are deliberately NOT listed — they stay per-address only.\n */\nconst NETWORK_SCOPED_ADDRESS_KEYS: readonly string[] = [\n STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS,\n STORAGE_KEYS_ADDRESS.OUTBOX,\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,\n STORAGE_KEYS_ADDRESS.PENDING_V2_DELIVERIES,\n STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,\n STORAGE_KEYS_ADDRESS.PROCESSED_COMBINED_TRANSFER_IDS,\n STORAGE_KEYS_ADDRESS.CANCELLED_INVOICES,\n STORAGE_KEYS_ADDRESS.CLOSED_INVOICES,\n STORAGE_KEYS_ADDRESS.FROZEN_BALANCES,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN,\n STORAGE_KEYS_ADDRESS.AUTO_RETURN_LEDGER,\n STORAGE_KEYS_ADDRESS.INV_LEDGER_INDEX,\n STORAGE_KEYS_ADDRESS.TOKEN_SCAN_STATE,\n STORAGE_KEYS_ADDRESS.SWAP_INDEX,\n];\n\n/** Composite per-address key prefixes (modules store `{addressId}_{prefix}{id}`) — per-network. */\nconst NETWORK_SCOPED_ADDRESS_PREFIXES: readonly string[] = [\n STORAGE_KEYS_ADDRESS.SWAP_RECORD_PREFIX, // 'swap:'\n 'inv_ledger:', // AccountingModule INV_LEDGER_PREFIX\n];\n\n/**\n * True if a storage key is a per-NETWORK token/payment key (so the storage provider adds the\n * network segment). Handles the bare form ('auto_return_ledger'), the module-built addressId\n * form ('DIRECT_a_b_auto_return_ledger'), and composites ('{addressId}_swap:{id}'). Chat/identity\n * keys return false — they remain per-address, network-agnostic.\n */\nexport function isNetworkScopedAddressKey(key: string): boolean {\n for (const k of NETWORK_SCOPED_ADDRESS_KEYS) {\n if (key === k || key.endsWith(`_${k}`)) return true;\n }\n for (const p of NETWORK_SCOPED_ADDRESS_PREFIXES) {\n if (key.startsWith(p) || key.includes(`_${p}`)) return true;\n }\n return false;\n}\n\n/** @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\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/**\n * Complete configuration for one network. Every field is required, so adding a\n * network (or a field) that is missing any of these is a COMPILE error at the\n * NETWORKS literal below (via `satisfies`) — a half-configured network can never\n * be defined silently.\n */\nexport interface NetworkConfig {\n readonly name: string;\n readonly aggregatorUrl: string;\n readonly nostrRelays: readonly string[];\n readonly ipfsGateways: readonly string[];\n readonly electrumUrl: string;\n readonly groupRelays: readonly string[];\n readonly tokenRegistryUrl: string;\n}\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 // v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The\n // old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot\n // run against it. 'testnet2' stays as an alias of the same configuration.\n testnet: {\n name: 'Testnet2',\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs/electrum)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n testnet2: {\n name: 'Testnet2',\n // v2 state-transition gateway (networkId 4 comes from the trust base). apiKey is env-injected.\n aggregatorUrl: 'https://gateway.testnet2.unicity.network',\n nostrRelays: TEST_NOSTR_RELAYS, // reuse testnet infra (shared relays/ipfs/electrum)\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl:\n 'https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json',\n },\n // NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot\n // operate against them until their gateways are cut over to the v2 protocol —\n // wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.\n dev: {\n name: 'Development',\n aggregatorUrl: DEV_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n tokenRegistryUrl: TOKEN_REGISTRY_URL,\n },\n} as const satisfies Record<string, NetworkConfig>;\n\nexport type NetworkType = keyof typeof NETWORKS;\n\n// =============================================================================\n// 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 INVOICE_TOKEN_TYPE_HEX = '14676a280bda4275baf865b67cd4c611bcd58c9bf8226d508acaa10a8fcaccc6' as const;\n\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * Browser LocalStorage Provider\n * Implements StorageProvider using browser localStorage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface LocalStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Custom storage instance (for testing/SSR) */\n storage?: Storage;\n /**\n * When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global/seed keys stay shared.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class LocalStorageProvider implements StorageProvider {\n readonly id = 'localStorage';\n readonly name = 'Local Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser localStorage for single-device persistence';\n\n private config: Required<Pick<LocalStorageProviderConfig, 'prefix' | 'debug'>> & {\n storage: Storage;\n };\n private network?: NetworkType;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n\n constructor(config?: LocalStorageProviderConfig) {\n // SSR fallback: use in-memory storage if localStorage unavailable\n const storage = config?.storage ?? this.getStorageSafe();\n\n this.config = {\n prefix: config?.prefix ?? 'sphere_',\n storage,\n debug: config?.debug ?? false,\n };\n this.network = config?.network;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Test storage availability\n const testKey = `${this.config.prefix}_test`;\n this.config.storage.setItem(testKey, 'test');\n this.config.storage.removeItem(testKey);\n\n this.status = 'connected';\n this.log('Connected to localStorage');\n } catch (error) {\n this.status = 'error';\n throw new SphereError(`LocalStorage not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.log('Disconnected from localStorage');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.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 // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n // Add address ID prefix for per-address data\n const id = this.identity.chainPubkey;\n return `${this.config.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.config.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected') {\n throw new SphereError('LocalStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n private getStorageSafe(): Storage {\n if (typeof window !== 'undefined' && window.localStorage) {\n return window.localStorage;\n }\n\n // SSR fallback: in-memory storage\n return createInMemoryStorage();\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('LocalStorage', message, ...args);\n }\n}\n\n// =============================================================================\n// In-Memory Storage (SSR Fallback)\n// =============================================================================\n\nfunction createInMemoryStorage(): Storage {\n const data = new Map<string, string>();\n\n return {\n get length() {\n return data.size;\n },\n clear() {\n data.clear();\n },\n getItem(key: string) {\n return data.get(key) ?? null;\n },\n setItem(key: string, value: string) {\n data.set(key, value);\n },\n removeItem(key: string) {\n data.delete(key);\n },\n key(index: number) {\n return Array.from(data.keys())[index] ?? null;\n },\n };\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createLocalStorageProvider(\n config?: LocalStorageProviderConfig\n): LocalStorageProvider {\n return new LocalStorageProvider(config);\n}\n","/**\n * Browser IndexedDB Storage Provider\n * Implements StorageProvider using IndexedDB for large-capacity browser storage\n */\n\nimport { logger } from '../../../core/logger';\nimport { SphereError } from '../../../core/errors';\nimport type { ProviderStatus, FullIdentity, TrackedAddressEntry } from '../../../types';\nimport type { StorageProvider } from '../../../storage';\nimport { STORAGE_KEYS_ADDRESS, STORAGE_KEYS_GLOBAL, isNetworkScopedAddressKey, type NetworkType } from '../../../constants';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nconst DB_NAME = 'sphere-storage';\nconst DB_VERSION = 1;\nconst STORE_NAME = 'kv';\n\nexport interface IndexedDBStorageProviderConfig {\n /** Key prefix (default: 'sphere_') */\n prefix?: string;\n /** Database name (default: 'sphere-storage') */\n dbName?: string;\n /**\n * Network. When set, token/payment per-address keys are network-scoped; chat/identity\n * per-address keys and global (seed) keys stay shared across networks.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class IndexedDBStorageProvider implements StorageProvider {\n readonly id = 'indexeddb-storage';\n readonly name = 'IndexedDB Storage';\n readonly type = 'local' as const;\n readonly description = 'Browser IndexedDB for large-capacity persistence';\n\n private prefix: string;\n private dbName: string;\n private network?: NetworkType;\n private debug: boolean;\n private identity: FullIdentity | null = null;\n private status: ProviderStatus = 'disconnected';\n private db: IDBDatabase | null = null;\n /** Monotonic connection ID for tracing open/close pairs */\n private connId = 0;\n\n constructor(config?: IndexedDBStorageProviderConfig) {\n this.prefix = config?.prefix ?? 'sphere_';\n this.dbName = config?.dbName ?? DB_NAME;\n this.network = config?.network;\n this.debug = config?.debug ?? false;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected' && this.db) return;\n\n // Retry once — a pending deleteDatabase() from a prior clear() can\n // block open() until the deletion completes. A short delay is usually\n // enough for the browser to finish the deletion.\n for (let attempt = 0; attempt < 2; attempt++) {\n this.status = 'connecting';\n const t0 = Date.now();\n logger.debug('IndexedDB',` connect: opening db=${this.dbName}, attempt=${attempt + 1}/2`);\n\n try {\n this.db = await Promise.race([\n this.openDatabase(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('IndexedDB open timed out after 5s')), 5000),\n ),\n ]);\n this.status = 'connected';\n logger.debug('IndexedDB',` connect: connected db=${this.dbName} connId=${this.connId} (${Date.now() - t0}ms)`);\n return;\n } catch (error) {\n logger.warn('IndexedDB',` connect: open failed db=${this.dbName} attempt=${attempt + 1} (${Date.now() - t0}ms):`, error);\n if (attempt === 0) {\n this.status = 'disconnected';\n await new Promise((r) => setTimeout(r, 1000));\n continue;\n }\n this.status = 'error';\n throw new SphereError(`IndexedDB not available: ${error}`, 'STORAGE_ERROR');\n }\n }\n }\n\n async disconnect(): Promise<void> {\n const cid = this.connId;\n logger.debug('IndexedDB',` disconnect: db=${this.dbName} connId=${cid} wasConnected=${!!this.db}`);\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.db !== null;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // StorageProvider Implementation\n // ===========================================================================\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.log('Identity set:', identity.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 const isPerAddressKey = Object.values(STORAGE_KEYS_ADDRESS).includes(key as typeof STORAGE_KEYS_ADDRESS[keyof typeof STORAGE_KEYS_ADDRESS]);\n // Token/payment keys (incl. module composites like `{addressId}_auto_return_ledger`,\n // `{addressId}_swap:{id}`) are ALSO per-network; chat/identity per-address keys are NOT.\n const net = this.network && isNetworkScopedAddressKey(key) ? `${this.network}_` : '';\n\n if (isPerAddressKey && this.identity?.chainPubkey) {\n const id = this.identity.chainPubkey;\n return `${this.prefix}${net}${id}_${key}`;\n }\n\n // Global key OR a module-built composite (already addressId-prefixed). Add the network\n // segment only for per-network token/payment keys; seed/identity/chat stay unprefixed.\n return `${this.prefix}${net}${key}`;\n }\n\n private ensureConnected(): void {\n if (this.status !== 'connected' || !this.db) {\n throw new SphereError('IndexedDBStorageProvider not connected', 'STORAGE_ERROR');\n }\n }\n\n // ===========================================================================\n // Private: IndexedDB Operations\n // ===========================================================================\n\n private openDatabase(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n\n request.onsuccess = () => {\n const db = request.result;\n const cid = ++connectionSeq;\n this.connId = cid;\n\n // Auto-close when another context requests version change or deletion.\n // Prevents leaked connections (e.g. React StrictMode double-mount)\n // from blocking deleteDatabase() or version upgrades.\n db.onversionchange = () => {\n logger.debug('IndexedDB',` onversionchange: auto-closing db=${this.dbName} connId=${cid}`);\n db.close();\n if (this.db === db) {\n this.db = null;\n this.status = 'disconnected';\n }\n };\n resolve(db);\n };\n\n // onblocked fires when another connection (e.g. other tab) holds\n // the database at a lower version. Log it — onsuccess will follow\n // once the other connection closes.\n request.onblocked = () => {\n logger.warn('IndexedDB',` open blocked by another connection, db=${this.dbName}`);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME, { keyPath: 'k' });\n }\n };\n });\n }\n\n private idbGet(key: string): Promise<{ k: string; v: string } | undefined> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.get(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? undefined);\n });\n }\n\n private idbPut(entry: { k: string; v: string }): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.put(entry);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbDelete(key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.delete(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private idbCount(key: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.count(key);\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n });\n }\n\n private idbGetAll(): Promise<{ k: string; v: string }[]> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readonly');\n const store = tx.objectStore(STORE_NAME);\n const request = store.getAll();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result ?? []);\n });\n }\n\n private idbClear(): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = this.db!.transaction(STORE_NAME, 'readwrite');\n const store = tx.objectStore(STORE_NAME);\n const request = store.clear();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n }\n\n private log(message: string, ...args: unknown[]): void {\n logger.debug('IndexedDB', message, ...args);\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createIndexedDBStorageProvider(\n config?: IndexedDBStorageProviderConfig\n): IndexedDBStorageProvider {\n return new IndexedDBStorageProvider(config);\n}\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out {@link https://www.rfc-editor.org/rfc/rfc4634 | RFC 4634} and\n * {@link https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf | FIPS 180-4}.\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, oidNist, rotr, type TRet } from './utils.ts';\n\n/**\n * SHA-224 / SHA-256 round constants from RFC 6234 §5.1: the first 32 bits\n * of the cube roots of the first 64 primes (2..311).\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable SHA-224 / SHA-256 message schedule buffer `W_t` from RFC 6234 §6.2 step 1. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\n\n/** Internal SHA-224 / SHA-256 compression engine from RFC 6234 §6.2. */\nabstract class SHA2_32B<T extends SHA2_32B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected abstract A: number;\n protected abstract B: number;\n protected abstract C: number;\n protected abstract D: number;\n protected abstract E: number;\n protected abstract F: number;\n protected abstract G: number;\n protected abstract H: number;\n\n constructor(outputLen: number) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\n/** Internal SHA-256 hash class grounded in RFC 6234 §6.2. */\nexport class _SHA256 extends SHA2_32B<_SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n constructor() {\n super(32);\n }\n}\n\n/** Internal SHA-224 hash class grounded in RFC 6234 §6.2 and §8.5. */\nexport class _SHA224 extends SHA2_32B<_SHA224> {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// SHA-384 / SHA-512 round constants from RFC 6234 §5.2:\n// 80 full 64-bit words split into high/low halves.\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable high-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\n// Reusable low-half schedule buffer for the RFC 6234 §6.4 64-bit `W_t` words.\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\n/** Internal SHA-384 / SHA-512 compression engine from RFC 6234 §6.4. */\nabstract class SHA2_64B<T extends SHA2_64B<T>> extends HashMD<T> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected abstract Ah: number;\n protected abstract Al: number;\n protected abstract Bh: number;\n protected abstract Bl: number;\n protected abstract Ch: number;\n protected abstract Cl: number;\n protected abstract Dh: number;\n protected abstract Dl: number;\n protected abstract Eh: number;\n protected abstract El: number;\n protected abstract Fh: number;\n protected abstract Fl: number;\n protected abstract Gh: number;\n protected abstract Gl: number;\n protected abstract Hh: number;\n protected abstract Hl: number;\n\n constructor(outputLen: number) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA512_W[i] = s0 + s1 + SHA512_W[i - 7] + SHA512_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n // HashMD callers route post-destroy usability through `destroyed`; zeroizing alone still leaves\n // update()/digest() callable on reused instances.\n this.destroyed = true;\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/** Internal SHA-512 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA512 extends SHA2_64B<_SHA512> {\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor() {\n super(64);\n }\n}\n\n/** Internal SHA-384 hash class grounded in RFC 6234 §6.3 and §6.4. */\nexport class _SHA384 extends SHA2_64B<_SHA384> {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See the repo-side derivation recipe in `test/misc/sha2-gen-iv.js`.\n * These IV literals are checked against that script rather than a dedicated\n * local RFC section.\n */\n\n/** SHA-512/224 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA-512/256 IV derived by the SHA-512/t recipe in `test/misc/sha2-gen-iv.js` and\n * stored as sixteen big-endian 32-bit halves. */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\n/** Internal SHA-512/224 hash class using the derived `T224_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_224 extends SHA2_64B<_SHA512_224> {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\n/** Internal SHA-512/256 hash class using the derived `T256_IV` and the shared\n * RFC 6234 §6.4 compression engine. */\nexport class _SHA512_256 extends SHA2_64B<_SHA512_256> {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:\n *\n * - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.\n * - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n * - Each sha256 hash is executing 2^18 bit operations.\n * - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-256.\n * ```ts\n * sha256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha256: TRet<CHash<_SHA256>> = /* @__PURE__ */ createHasher(\n () => new _SHA256(),\n /* @__PURE__ */ oidNist(0x01)\n);\n/**\n * SHA2-224 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-224.\n * ```ts\n * sha224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha224: TRet<CHash<_SHA224>> = /* @__PURE__ */ createHasher(\n () => new _SHA224(),\n /* @__PURE__ */ oidNist(0x04)\n);\n\n/**\n * SHA2-512 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512.\n * ```ts\n * sha512(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512: TRet<CHash<_SHA512>> = /* @__PURE__ */ createHasher(\n () => new _SHA512(),\n /* @__PURE__ */ oidNist(0x03)\n);\n/**\n * SHA2-384 hash function from RFC 4634.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-384.\n * ```ts\n * sha384(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha384: TRet<CHash<_SHA384>> = /* @__PURE__ */ createHasher(\n () => new _SHA384(),\n /* @__PURE__ */ oidNist(0x02)\n);\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/256.\n * ```ts\n * sha512_256(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_256: TRet<CHash<_SHA512_256>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_256(),\n /* @__PURE__ */ oidNist(0x06)\n);\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on {@link https://eprint.iacr.org/2010/548.pdf | truncated SHA512}.\n * @param msg - message bytes to hash\n * @returns Digest bytes.\n * @example\n * Hash a message with SHA2-512/224.\n * ```ts\n * sha512_224(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const sha512_224: TRet<CHash<_SHA512_224>> = /* @__PURE__ */ createHasher(\n () => new _SHA512_224(),\n /* @__PURE__ */ oidNist(0x05)\n);\n","/**\n * token-engine/sdk.ts — the SINGLE place that imports @unicitylabs/state-transition-sdk.\n *\n * The v1 cut-over is done: the canonical package name now resolves to the v2 SDK\n * (the migration-era `state-transition-sdk-v2` npm alias is gone, and v1 with it).\n *\n * Everything else in token-engine/ imports SDK symbols from `./sdk`, never from the package directly.\n * ESLint `no-restricted-imports` forbids importing the SDK anywhere except this file.\n *\n * Import path note: subpaths are `…/lib/<path>.js` (no `/src/`); no root barrel.\n */\n\n// ── client / aggregator / proof / network / trust base ──────────────────────\nexport { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient.js';\nexport { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/AggregatorClient.js';\nexport type { IAggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/IAggregatorClient.js';\nexport { NetworkId } from '@unicitylabs/state-transition-sdk/lib/api/NetworkId.js';\nexport { CertificationData } from '@unicitylabs/state-transition-sdk/lib/api/CertificationData.js';\nexport { CertificationResponse, CertificationStatus } from '@unicitylabs/state-transition-sdk/lib/api/CertificationResponse.js';\nexport { StateId } from '@unicitylabs/state-transition-sdk/lib/api/StateId.js';\nexport { InclusionProof } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProof.js';\nexport { InclusionProofResponse } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProofResponse.js';\nexport { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/api/bft/RootTrustBase.js';\nexport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js';\nexport { InclusionProofVerificationStatus } from '@unicitylabs/state-transition-sdk/lib/transaction/verification/rule/InclusionProofVerificationRule.js';\n\n// ── token / transactions ────────────────────────────────────────────────────\nexport { Token } from '@unicitylabs/state-transition-sdk/lib/transaction/Token.js';\nexport { MintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransaction.js';\nexport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction.js';\nexport { CertifiedMintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedMintTransaction.js';\nexport { CertifiedTransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedTransferTransaction.js';\nexport { TokenId } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenId.js';\nexport { TokenType } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenType.js';\nexport { TokenSalt } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenSalt.js';\nexport type { ITransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/ITransaction.js';\nexport { MintJustificationVerifierService } from '@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js';\n\n// ── predicates / unlock scripts ─────────────────────────────────────────────\nexport type { IPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/IPredicate.js';\nexport type { IUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/IUnlockScript.js';\nexport { EncodedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js';\nexport { SignaturePredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js';\nexport { SignaturePredicateUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicateUnlockScript.js';\nexport { BurnPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/BurnPredicate.js';\nexport { PredicateVerifierService } from '@unicitylabs/state-transition-sdk/lib/predicate/verification/PredicateVerifierService.js';\n\n// ── crypto / hashing ────────────────────────────────────────────────────────\nexport { SigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/SigningService.js';\nexport { Signature } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Signature.js';\nexport { MintSigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/MintSigningService.js';\nexport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/HashAlgorithm.js';\nexport { DataHash } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHash.js';\nexport { DataHasher } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasher.js';\nexport { DataHasherFactory } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasherFactory.js';\n\n// ── CBOR serialization ──────────────────────────────────────────────────────\nexport { CborSerializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborSerializer.js';\nexport { CborDeserializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborDeserializer.js';\nexport { CborError } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborError.js';\n\n// ── payment / value / split ─────────────────────────────────────────────────\nexport type { IPaymentData } from '@unicitylabs/state-transition-sdk/lib/payment/IPaymentData.js';\nexport { Asset } from '@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js';\nexport { AssetId } from '@unicitylabs/state-transition-sdk/lib/payment/asset/AssetId.js';\nexport { PaymentAssetCollection } from '@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js';\nexport { TokenSplit } from '@unicitylabs/state-transition-sdk/lib/payment/TokenSplit.js';\nexport { SplitTokenRequest } from '@unicitylabs/state-transition-sdk/lib/payment/SplitTokenRequest.js';\nexport { SplitToken } from '@unicitylabs/state-transition-sdk/lib/payment/SplitToken.js';\nexport { SplitAssetProof } from '@unicitylabs/state-transition-sdk/lib/payment/SplitAssetProof.js';\nexport { SplitMintJustification } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustification.js';\nexport { SplitMintJustificationVerifier } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustificationVerifier.js';\n\n// ── verification result types ───────────────────────────────────────────────\nexport { VerificationStatus } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationStatus.js';\nexport { VerificationResult } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationResult.js';\n\n// ── unicity-id (nametag) ────────────────────────────────────────────────────\nexport { UnicityId } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityId.js';\nexport { UnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdMintTransaction.js';\nexport { CertifiedUnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/CertifiedUnicityIdMintTransaction.js';\nexport { UnicityIdToken } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdToken.js';\n\n// ── util ────────────────────────────────────────────────────────────────────\nexport { HexConverter } from '@unicitylabs/state-transition-sdk/lib/util/HexConverter.js';\nexport { BigintConverter } from '@unicitylabs/state-transition-sdk/lib/util/BigintConverter.js';\nexport { BitString } from '@unicitylabs/state-transition-sdk/lib/util/BitString.js';\n","/**\n * token-engine/token-blob.ts — storage/transport codec for a wallet token.\n *\n * A {@link TokenBlob} is `{ v, network, tokenId, token }` where `token` is the v2\n * `Token.toCBOR()` bytes and `tokenId` is the genesis-stable id (stored so dedup /\n * listing / tombstone keys need no engine call). This codec wraps it in a\n * sphere-private CBOR envelope so the wallet's own storage format can version\n * independently of the SDK's token CBOR. The decoded value is re-derivable from\n * `token`, so it is NOT stored here.\n */\n\nimport { CborDeserializer, CborError, CborSerializer } from './sdk';\nimport type { TokenBlob } from './types';\n\n/** Sphere-private CBOR tag (distinct from SpherePaymentData's 39050). */\nconst TOKEN_BLOB_TAG = 39051n;\n/** Current blob envelope version. */\nexport const TOKEN_BLOB_VERSION = 1;\n\n/** Encode a blob as `tag(39051)[ v, network, tokenId, token ]`. */\nexport function encodeTokenBlob(blob: TokenBlob): Uint8Array {\n return CborSerializer.encodeTag(\n TOKEN_BLOB_TAG,\n CborSerializer.encodeArray(\n CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),\n CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),\n CborSerializer.encodeTextString(blob.tokenId),\n CborSerializer.encodeByteString(blob.token),\n ),\n );\n}\n\n/** Decode a blob produced by {@link encodeTokenBlob}. */\nexport function decodeTokenBlob(bytes: Uint8Array): TokenBlob {\n const tag = CborDeserializer.decodeTag(bytes);\n if (tag.tag !== TOKEN_BLOB_TAG) {\n throw new CborError(`Invalid TokenBlob tag: ${tag.tag}`);\n }\n const fields = CborDeserializer.decodeArray(tag.data, 4);\n const v = Number(CborDeserializer.decodeUnsignedInteger(fields[0]));\n if (v !== TOKEN_BLOB_VERSION) {\n throw new CborError(`Unsupported TokenBlob version: ${v}`);\n }\n return {\n v,\n network: Number(CborDeserializer.decodeUnsignedInteger(fields[1])),\n tokenId: CborDeserializer.decodeTextString(fields[2]),\n token: CborDeserializer.decodeByteString(fields[3]),\n };\n}\n\n/**\n * The wallet-api WIRE bytes for a blob (ARCHITECTURE §5.2/§8.2): the backend\n * stores, content-addresses and validates the INNER `token` bytes (the v2\n * `Token.toCBOR()` form — `Token.fromCBOR` at §8.2 step 3). The sphere-private\n * 39051 envelope never crosses the §16 API. Tolerant on input: envelope bytes\n * unwrap to their inner `token` bytes; anything else is already wire format\n * and passes through. (The phase-2 harness caught the envelope leaking onto\n * the wire — the real backend 422s it at §8.2 step 3 with \"Invalid CBOR tag\n * for Token: 39051\"; only the test fake's matching wrongness had masked it.)\n */\nexport function unwrapTokenBlobBytes(bytes: Uint8Array): Uint8Array {\n try {\n return decodeTokenBlob(bytes).token;\n } catch {\n return bytes;\n }\n}\n","/**\n * storage/whole-blob-inventory-adapter.ts — default lazy-port adapter for\n * whole-blob providers (sdk-changes S2).\n *\n * The lazy inventory port (`listInventory` / `getToken` / `applyDelta`) is the\n * wallet's hot path against wallet-api, but the port extension must keep every\n * EXISTING whole-blob provider conformant — swappability is a covenant\n * (sdk-changes S7), not an aspiration. This adapter derives the three methods\n * from the provider's own `load()`/`save()`:\n *\n * - `listInventory()` — the view computed from the loaded set: every stored\n * token is `'active'`, with synthetic `seq`/`cursor`, `more: false`, and\n * `syncEpoch: 0n` (a local store has no server restore to signal). There is\n * no change journal, so `since` is ignored and the full active snapshot is\n * returned — a degenerate but coherent cursor model.\n * - `getToken()` — decoded from the loaded set (v2 blob entries only).\n * - `applyDelta()` — tombstones `spent` entries (the provider's own tombstone\n * handling deletes the stored blobs) and asserts `added` outputs are already\n * present: a whole-blob provider cannot materialize bytes from a blob-store\n * key — the engine path `save()`s its outputs first. Naturally idempotent:\n * an already-tombstoned `spent` row and an already-present (or since-moved)\n * `added` row are success, mirroring ARCHITECTURE §5.3 step 4.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { decodeTokenBlob } from '../token-engine/token-blob';\nimport type { TokenBlob } from '../token-engine/types';\nimport { SphereError } from '../core/errors';\nimport type {\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n InventoryItem,\n InventoryView,\n LoadResult,\n SaveResult,\n TxfStorageDataBase,\n TxfTombstone,\n} from './storage-provider';\n\n/** The slice of a whole-blob provider the adapter needs. */\nexport interface WholeBlobStore<TData extends TxfStorageDataBase = TxfStorageDataBase> {\n load(identifier?: string): Promise<LoadResult<TData>>;\n save(data: TData): Promise<SaveResult>;\n}\n\n/** A stored token entry that carries a v2 blob (the UI token record). */\ninterface V2TokenEntry {\n sdkData: string;\n coinId?: string;\n amount?: string;\n}\n\nconst TOKEN_KEY_RE = /^_[0-9a-f]{4,}$/i;\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\n/** A v2 engine blob is even-length hex (CBOR); legacy v1 TXF is JSON. */\nfunction isV2BlobHex(sdkData: unknown): sdkData is string {\n return (\n typeof sdkData === 'string' &&\n sdkData.length >= 2 &&\n sdkData.length % 2 === 0 &&\n /^[0-9a-f]+$/i.test(sdkData)\n );\n}\n\nfunction isV2TokenEntry(entry: unknown): entry is V2TokenEntry {\n return (\n typeof entry === 'object' &&\n entry !== null &&\n !('genesis' in entry) &&\n isV2BlobHex((entry as V2TokenEntry).sdkData)\n );\n}\n\n/** Stored token entries keyed by genesis tokenId (the `_<id>` key minus `_`). */\nfunction collectTokenEntries(data: TxfStorageDataBase): Map<string, unknown> {\n const out = new Map<string, unknown>();\n for (const [key, value] of Object.entries(data)) {\n if (!TOKEN_KEY_RE.test(key)) continue;\n if (value === null || typeof value !== 'object') continue;\n out.set(key.slice(1), value);\n }\n return out;\n}\n\nfunction entryAssets(entry: unknown): InventoryItem['assets'] {\n if (!isV2TokenEntry(entry)) return undefined;\n const { coinId, amount } = entry;\n if (typeof coinId !== 'string' || typeof amount !== 'string' || !/^[0-9]+$/.test(amount)) {\n return undefined;\n }\n return [{ coinId, amount: BigInt(amount) }];\n}\n\n/**\n * Default-adapter implementation of the S2 lazy port over a whole-blob store.\n * Providers instantiate one lazily and delegate their `listInventory` /\n * `getToken` / `applyDelta` to it (minimal edits, shared semantics).\n */\nexport class WholeBlobInventoryAdapter<TData extends TxfStorageDataBase = TxfStorageDataBase> {\n constructor(private readonly store: WholeBlobStore<TData>) {}\n\n private async loadData(): Promise<TData> {\n const result = await this.store.load();\n if (!result.success || !result.data) {\n throw new SphereError(\n `Whole-blob inventory adapter: load() failed${result.error ? `: ${result.error}` : ''}`,\n 'STORAGE_ERROR'\n );\n }\n return result.data;\n }\n\n /** View computed from the loaded set: all `'active'`, synthetic seq/cursor, `more: false`. */\n async listInventory(_since?: bigint): Promise<InventoryView> {\n const data = await this.loadData();\n const entries = [...collectTokenEntries(data).entries()].sort(([a], [b]) => (a < b ? -1 : 1));\n const items: InventoryItem[] = entries.map(([tokenId, entry], index) => ({\n tokenId,\n status: 'active' as const,\n assets: entryAssets(entry),\n seq: BigInt(index + 1),\n }));\n return { cursor: BigInt(items.length), syncEpoch: 0n, more: false, items };\n }\n\n /** Serve a token's decoded blob from the loaded set (v2 blob entries only). */\n async getToken(tokenId: string): Promise<TokenBlob> {\n const data = await this.loadData();\n const entry = collectTokenEntries(data).get(tokenId);\n if (entry === undefined) {\n throw new SphereError(`Token ${tokenId} not found in storage`, 'STORAGE_ERROR');\n }\n if (!isV2TokenEntry(entry)) {\n throw new SphereError(`Token ${tokenId} has no v2 blob (legacy TXF entry)`, 'STORAGE_ERROR');\n }\n const blob = decodeTokenBlob(hexToBytes(entry.sdkData));\n if (blob.tokenId !== tokenId) {\n throw new SphereError(\n `Stored blob tokenId mismatch for ${tokenId} (blob carries ${blob.tokenId})`,\n 'STORAGE_ERROR'\n );\n }\n return blob;\n }\n\n /**\n * Tombstone `spent`, assert `added` present. Mirrors the §5.3 apply\n * semantics a local store can honor; idempotent by construction (replays\n * find spent already tombstoned / added already present → success).\n */\n async applyDelta(\n _transferId: string,\n spent: string[],\n added: ApplyDeltaAdded[],\n _opts?: ApplyDeltaOptions\n ): Promise<void> {\n const addedIds = new Set(added.map((a) => a.tokenId));\n for (const tokenId of spent) {\n if (addedIds.has(tokenId)) {\n // Mirrors the server's 422 (ARCHITECTURE §5.3): a self-send routes\n // through the mailbox, never through one apply.\n throw new SphereError(\n `applyDelta: token ${tokenId} appears in both spent and added`,\n 'VALIDATION_ERROR'\n );\n }\n }\n\n const data = await this.loadData();\n const entries = collectTokenEntries(data);\n const tombstoned = new Set((data._tombstones ?? []).map((t) => t.tokenId));\n\n for (const { tokenId } of added) {\n // A whole-blob store cannot fetch bytes by blob-store key; the engine\n // path save()s outputs before recording the spend. Present (or already\n // moved on to a tombstone) is success; never-seen is a caller bug.\n if (!entries.has(tokenId) && !tombstoned.has(tokenId)) {\n throw new SphereError(\n `applyDelta: added token ${tokenId} is not in storage — save() engine outputs before applyDelta`,\n 'STORAGE_ERROR'\n );\n }\n }\n\n const newTombstones: TxfTombstone[] = [];\n for (const tokenId of spent) {\n const entry = entries.get(tokenId);\n if (entry === undefined) continue; // already tombstoned/unknown → success (§5.3 step 4)\n let stateHash = '';\n if (isV2TokenEntry(entry)) {\n try {\n const blob = decodeTokenBlob(hexToBytes(entry.sdkData));\n stateHash = bytesToHex(sha256(blob.token));\n } catch {\n // Tombstone without a state hash is still a valid removal record.\n }\n }\n newTombstones.push({ tokenId, stateHash, timestamp: Date.now() });\n delete data[`_${tokenId}`];\n }\n\n if (newTombstones.length === 0) return; // pure replay — nothing to write\n\n data._tombstones = [...(data._tombstones ?? []), ...newTombstones];\n const result = await this.store.save(data);\n if (!result.success) {\n throw new SphereError(\n `Whole-blob inventory adapter: save() failed${result.error ? `: ${result.error}` : ''}`,\n 'STORAGE_ERROR'\n );\n }\n }\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, InventoryView, ApplyDeltaAdded, ApplyDeltaOptions } from '../../../storage';\nimport { WholeBlobInventoryAdapter } from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport type { NetworkType } 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 /**\n * Network this store belongs to. Baked into the DB name so testnet/testnet2/mainnet\n * (and v1/v2) never share a token DB for the same wallet identity.\n */\n network?: NetworkType;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/** Global connection counter for diagnostic tracing */\nlet connectionSeq = 0;\n\nexport class 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 const base = config?.dbNamePrefix ?? DB_NAME;\n // Bake the network into the prefix so the DB name AND clear()/findPrefixedDatabases\n // (which filter by prefix) are per-network — clear() never wipes another network.\n this.dbNamePrefix = config?.network ? `${base}-${config.network}` : base;\n this.dbName = this.dbNamePrefix;\n this.debug = config?.debug ?? false;\n }\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n // Scope the DB by chainPubkey: always present, and NOT truncated like getAddressId's\n // DIRECT_first6_last6 — so two different wallets can never collide into one DB.\n // Network is already in dbNamePrefix → the DB is per-network + per-identity.\n this.dbName = `${this.dbNamePrefix}-${identity.chainPubkey}`;\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 // ── Lazy inventory port (sdk-changes S2) — default whole-blob adapter ──────\n\n /** Default lazy-port adapter over this store (sdk-changes S2). */\n private readonly inventoryAdapter = new WholeBlobInventoryAdapter<TxfStorageDataBase>(this);\n\n listInventory(since?: bigint): Promise<InventoryView> {\n return this.inventoryAdapter.listInventory(since);\n }\n\n getToken(tokenId: string): Promise<TokenBlob> {\n return this.inventoryAdapter.getToken(tokenId);\n }\n\n applyDelta(transferId: string, spent: string[], added: ApplyDeltaAdded[], opts?: ApplyDeltaOptions): Promise<void> {\n return this.inventoryAdapter.applyDelta(transferId, spent, added, opts);\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 * Create an independent instance for a different address.\n * The new instance shares the same config but has its own IDB connection.\n */\n createForAddress(): IndexedDBTokenStorageProvider {\n return new IndexedDBTokenStorageProvider({\n dbNamePrefix: this.dbNamePrefix,\n debug: this.debug,\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 /** In-memory max DM (gift-wrap) event timestamp. */\n private lastDmEventTs: number = 0;\n /** Fallback 'since' timestamp for first-time address subscriptions (consumed once). */\n private fallbackSince: number | null = null;\n /** Fallback 'since' timestamp for DM (gift-wrap) subscriptions (consumed once). */\n private fallbackDmSince: number | null = null;\n private identity: FullIdentity | null = null;\n private keyManager: NostrKeyManager | null = null;\n private status: ProviderStatus = 'disconnected';\n\n // NostrClient from nostr-js-sdk handles all WebSocket management,\n // keepalive pings, reconnection, and NIP-42 authentication\n private nostrClient: NostrClient | null = null;\n private mainSubscriptionId: string | null = null;\n\n // Event handlers\n private processedEventIds = new Set<string>();\n private messageHandlers: Set<MessageHandler> = new Set();\n private 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 * Get the WebSocket factory (used by MultiAddressTransportMux to share the same factory).\n */\n getWebSocketFactory(): WebSocketFactory {\n return this.config.createWebSocket;\n }\n\n /**\n * Get the configured relay URLs.\n */\n getConfiguredRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get the storage adapter.\n */\n getStorageAdapter(): TransportStorageAdapter | null {\n return this.storage;\n }\n\n /**\n * Get the underlying NostrClient (or null if not yet connected).\n *\n * Exposed so {@link MultiAddressTransportMux} can share the same\n * client/socket pair instead of opening a duplicate WebSocket per\n * relay (#123). The transport owns the client's lifecycle — callers\n * MUST NOT call {@code disconnect()} on the returned instance.\n */\n getNostrClient(): NostrClient | null {\n return this.nostrClient;\n }\n\n /**\n * Suppress event subscriptions — unsubscribe wallet/chat filters\n * but keep the connection alive for resolve/identity-binding operations.\n * Used when MultiAddressTransportMux takes over event handling.\n */\n suppressSubscriptions(): void {\n if (!this.nostrClient) return;\n\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Prevent re-subscription on reconnect by marking subscriptions as suppressed\n this._subscriptionsSuppressed = true;\n logger.debug('Nostr', 'Subscriptions suppressed — mux handles event routing');\n }\n\n // Flag to prevent re-subscription after suppressSubscriptions()\n private _subscriptionsSuppressed = false;\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n try {\n // Ensure keyManager exists for NostrClient\n if (!this.keyManager) {\n // Create a temporary key manager - will be replaced when setIdentity is called\n const tempKey = Buffer.alloc(32);\n crypto.getRandomValues(tempKey);\n this.keyManager = NostrKeyManager.fromPrivateKey(tempKey);\n }\n\n // Create NostrClient with robust connection handling:\n // - autoReconnect: automatic reconnection with exponential backoff\n // - pingIntervalMs: keepalive pings to detect stale connections\n // - NIP-42 AUTH handling built-in\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16, // exponential backoff cap\n pingIntervalMs: 15000, // 15 second keepalive pings (more aggressive to prevent drops)\n // Bump query timeout from the SDK default of 5s to 20s.\n // Real-world testnet observation (2026-05-01): under transient\n // relay overload, kind:30078 (nametag binding) queries take 5-7s\n // to return EVENT messages, even though the binding is on the\n // relay. The default 5s timeout fires before the event arrives,\n // resolveNametag returns null, and downstream sendDM throws\n // INVALID_RECIPIENT — causing every np.propose_deal to fail. 20s\n // gives the slow path enough headroom to complete while still\n // bailing reasonably fast on a truly broken relay.\n queryTimeoutMs: 20000,\n });\n\n // Add connection event listener for logging\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n this.emitEvent({ type: 'transport:reconnecting', timestamp: Date.now() });\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n // Re-establish subscriptions — the relay drops them on disconnect.\n this.subscribeToEvents().catch((err) => {\n logger.error('Nostr', 'Failed to re-subscribe after reconnect:', err);\n });\n },\n });\n\n // Connect to all relays (with timeout to prevent indefinite hang)\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport connection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n\n // Need at least one successful connection\n if (!this.nostrClient.isConnected()) {\n throw new SphereError('Failed to connect to any relay', 'TRANSPORT_ERROR');\n }\n\n this.status = 'connected';\n this.emitEvent({ type: 'transport:connected', timestamp: Date.now() });\n logger.debug('Nostr', 'Connected to', this.nostrClient.getConnectedRelays().size, 'relays');\n\n // Set up subscriptions\n if (this.identity) {\n await this.subscribeToEvents();\n }\n } catch (error) {\n this.status = 'error';\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (this.nostrClient) {\n this.nostrClient.disconnect();\n this.nostrClient = null;\n }\n this.mainSubscriptionId = null;\n this.walletSubscriptionId = null;\n this.chatSubscriptionId = null;\n this.chatEoseFired = false;\n this.status = 'disconnected';\n this.emitEvent({ type: 'transport:disconnected', timestamp: Date.now() });\n logger.debug('Nostr', 'Disconnected from all relays');\n }\n\n isConnected(): boolean {\n return this.status === 'connected' && this.nostrClient?.isConnected() === true;\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // Dynamic Relay Management\n // ===========================================================================\n\n /**\n * Get list of configured relay URLs\n */\n getRelays(): string[] {\n return [...this.config.relays];\n }\n\n /**\n * Get list of currently connected relay URLs\n */\n getConnectedRelays(): string[] {\n if (!this.nostrClient) return [];\n return Array.from(this.nostrClient.getConnectedRelays());\n }\n\n /**\n * Add a new relay dynamically\n * Will connect immediately if provider is already connected\n */\n async addRelay(relayUrl: string): Promise<boolean> {\n // Check if already configured\n if (this.config.relays.includes(relayUrl)) {\n logger.debug('Nostr', 'Relay already configured:', relayUrl);\n return false;\n }\n\n // Add to config\n this.config.relays.push(relayUrl);\n\n // Connect if provider is connected\n if (this.status === 'connected' && this.nostrClient) {\n try {\n await this.nostrClient.connect(relayUrl);\n logger.debug('Nostr', 'Added and connected to relay:', relayUrl);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: true },\n });\n return true;\n } catch (error) {\n logger.debug('Nostr', 'Failed to connect to new relay:', relayUrl, error);\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false, error: String(error) },\n });\n return false;\n }\n }\n\n this.emitEvent({\n type: 'transport:relay_added',\n timestamp: Date.now(),\n data: { relay: relayUrl, connected: false },\n });\n return true;\n }\n\n /**\n * Remove a relay dynamically\n * Will disconnect from the relay if connected\n * NOTE: NostrClient doesn't support removing individual relays at runtime.\n * We remove from config so it won't be used on next connect().\n */\n async removeRelay(relayUrl: string): Promise<boolean> {\n const index = this.config.relays.indexOf(relayUrl);\n if (index === -1) {\n logger.debug('Nostr', 'Relay not found:', relayUrl);\n return false;\n }\n\n // Remove from config\n this.config.relays.splice(index, 1);\n logger.debug('Nostr', 'Removed relay from config:', relayUrl);\n\n this.emitEvent({\n type: 'transport:relay_removed',\n timestamp: Date.now(),\n data: { relay: relayUrl },\n });\n\n // Check if we still have connections\n if (this.nostrClient && !this.nostrClient.isConnected() && this.status === 'connected') {\n this.status = 'error';\n this.emitEvent({\n type: 'transport:error',\n timestamp: Date.now(),\n data: { error: 'No connected relays remaining' },\n });\n }\n\n return true;\n }\n\n /**\n * Check if a relay is configured\n */\n hasRelay(relayUrl: string): boolean {\n return this.config.relays.includes(relayUrl);\n }\n\n /**\n * Check if a relay is currently connected\n */\n isRelayConnected(relayUrl: string): boolean {\n if (!this.nostrClient) return false;\n return this.nostrClient.getConnectedRelays().has(relayUrl);\n }\n\n // ===========================================================================\n // TransportProvider Implementation\n // ===========================================================================\n\n async setIdentity(identity: FullIdentity): Promise<void> {\n this.identity = identity;\n\n // Clear per-address state so stale dedup entries from previous address\n // don't block legitimate events for the new address.\n this.processedEventIds.clear();\n this.lastEventTs = 0;\n this.lastDmEventTs = 0;\n this.fallbackDmSince = null;\n\n // Create NostrKeyManager from private key\n const secretKey = Buffer.from(identity.privateKey, 'hex');\n this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);\n\n // Use Nostr-format pubkey (32 bytes / 64 hex chars) from keyManager\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Identity set, Nostr pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // If we already have a NostrClient with a temp key, we need to reconnect with the real key\n // NostrClient doesn't support changing key at runtime\n if (this.nostrClient && this.status === 'connected') {\n logger.debug('Nostr', 'Identity changed while connected - recreating NostrClient');\n const oldClient = this.nostrClient;\n\n // Create new client with real identity\n this.nostrClient = new NostrClient(this.keyManager, {\n autoReconnect: this.config.autoReconnect,\n reconnectIntervalMs: this.config.reconnectDelay,\n maxReconnectIntervalMs: this.config.reconnectDelay * 16,\n pingIntervalMs: 15000, // 15 second keepalive pings\n queryTimeoutMs: 20000, // see same option above for rationale\n });\n\n // Add connection event listener\n this.nostrClient.addConnectionListener({\n onConnect: (url) => {\n logger.debug('Nostr', 'NostrClient connected to relay:', url);\n },\n onDisconnect: (url, reason) => {\n logger.debug('Nostr', 'NostrClient disconnected from relay:', url, 'reason:', reason);\n },\n onReconnecting: (url, attempt) => {\n logger.debug('Nostr', 'NostrClient reconnecting to relay:', url, 'attempt:', attempt);\n },\n onReconnected: (url) => {\n logger.debug('Nostr', 'NostrClient reconnected to relay:', url);\n },\n });\n\n // Connect with new identity, set up subscriptions, then disconnect old client\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\n `Transport reconnection timed out after ${this.config.timeout}ms`\n )), this.config.timeout)\n ),\n ]);\n await this.subscribeToEvents();\n oldClient.disconnect();\n } else if (this.isConnected()) {\n // Already connected with right key, just subscribe\n await this.subscribeToEvents();\n }\n }\n\n setFallbackSince(sinceSeconds: number): void {\n this.fallbackSince = sinceSeconds;\n }\n\n setFallbackDmSince(sinceSeconds: number): void {\n this.fallbackDmSince = sinceSeconds;\n }\n\n /**\n * Get the Nostr-format public key (32 bytes / 64 hex chars)\n * This is the x-coordinate only, without the 02/03 prefix.\n */\n getNostrPubkey(): string {\n if (!this.keyManager) {\n throw new SphereError('KeyManager not initialized - call setIdentity first', 'NOT_INITIALIZED');\n }\n return this.keyManager.getPublicKeyHex();\n }\n\n async sendMessage(recipientPubkey: string, content: string): Promise<string> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Wrap content with sender nametag for Sphere app compatibility\n const senderNametag = this.identity?.nametag;\n const wrappedContent = senderNametag\n ? JSON.stringify({ senderNametag, text: content })\n : content;\n\n // Create NIP-17 gift-wrapped message (kind 1059) for recipient\n const giftWrap = NIP17.createGiftWrap(this.keyManager!, nostrRecipient, wrappedContent);\n\n await this.publishWithVerification(giftWrap, 3, 'dm');\n\n // NIP-17 self-wrap: send a copy to ourselves so relay can replay sent messages.\n // Content includes recipientPubkey and originalId for dedup against the live-sent record.\n const selfWrapContent = JSON.stringify({\n selfWrap: true,\n originalId: giftWrap.id,\n recipientPubkey,\n senderNametag,\n text: content,\n });\n const selfPubkey = this.keyManager!.getPublicKeyHex();\n const selfGiftWrap = NIP17.createGiftWrap(this.keyManager!, selfPubkey, selfWrapContent);\n this.publishEvent(selfGiftWrap).catch(err => {\n logger.debug('Nostr', 'Self-wrap publish failed:', err);\n });\n\n this.emitEvent({\n type: 'message:sent',\n timestamp: Date.now(),\n data: { recipient: recipientPubkey },\n });\n\n return giftWrap.id;\n }\n\n onMessage(handler: MessageHandler): () => void {\n this.messageHandlers.add(handler);\n\n // Flush any messages that arrived before this handler was registered\n if (this.pendingMessages.length > 0) {\n const pending = this.pendingMessages;\n this.pendingMessages = [];\n logger.debug('Nostr', 'Flushing', pending.length, 'buffered messages to new handler');\n for (const message of pending) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error (buffered):', error);\n }\n }\n }\n\n return () => this.messageHandlers.delete(handler);\n }\n\n 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.publishWithVerification(event, 3, 'token_transfer');\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 onChatReady(handler: () => void): () => void {\n // If EOSE already fired, invoke immediately\n if (this.chatEoseFired) {\n try { handler(); } catch { /* ignore */ }\n return () => {};\n }\n this.chatEoseHandlers.push(handler);\n return () => {\n this.chatEoseHandlers = this.chatEoseHandlers.filter(h => h !== handler);\n };\n }\n\n // ===========================================================================\n // Composing Indicators (NIP-59 kind 25050)\n // ===========================================================================\n\n onComposing(handler: ComposingHandler): () => void {\n this.composingHandlers.add(handler);\n return () => this.composingHandlers.delete(handler);\n }\n\n async sendComposingIndicator(recipientPubkey: string, content: string): Promise<void> {\n this.ensureReady();\n\n // NIP-17 requires 32-byte x-only pubkey; strip 02/03 prefix if present\n const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith('02') || recipientPubkey.startsWith('03'))\n ? recipientPubkey.slice(2)\n : recipientPubkey;\n\n // Build NIP-17 gift wrap with kind 25050 rumor (instead of kind 14 for DMs).\n // We replicate the three-layer NIP-59 envelope because NIP17.createGiftWrap\n // hardcodes kind 14 for the inner rumor.\n const giftWrap = this.createCustomKindGiftWrap(nostrRecipient, content, COMPOSING_INDICATOR_KIND);\n await this.publishEvent(giftWrap);\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Routes to the appropriate specific resolve method based on identifier format.\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n // @nametag\n if (identifier.startsWith('@')) {\n return this.resolveNametagInfo(identifier.slice(1));\n }\n\n // DIRECT:// 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 await this.ensureConnectedForResolve();\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 await this.ensureConnectedForResolve();\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 await this.ensureConnectedForResolve();\n\n const binding = await this.nostrClient!.queryBindingByAddress(address);\n if (!binding) return null;\n\n return this.bindingInfoToPeerInfo(binding);\n }\n\n /**\n * Convert a BindingInfo (from nostr-js-sdk) to PeerInfo (sphere-sdk type).\n */\n private async bindingInfoToPeerInfo(binding: BindingInfo, nametag?: string): Promise<PeerInfo> {\n return {\n nametag: nametag || binding.nametag,\n transportPubkey: binding.transportPubkey,\n chainPubkey: binding.publicKey || '',\n l1Address: binding.l1Address || '',\n directAddress: binding.directAddress || '',\n timestamp: binding.timestamp,\n };\n }\n\n /**\n * Resolve transport pubkey (Nostr pubkey) to full peer info.\n * Queries binding events authored by the given pubkey.\n */\n async resolveTransportPubkeyInfo(transportPubkey: string): Promise<PeerInfo | null> {\n await this.ensureConnectedForResolve();\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [transportPubkey],\n limit: 5,\n });\n\n if (events.length === 0) return null;\n\n // Sort by timestamp descending and take the most recent\n events.sort((a, b) => b.created_at - a.created_at);\n const bindingEvent = events[0];\n\n try {\n const content = JSON.parse(bindingEvent.content);\n\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 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 await this.ensureConnectedForResolve();\n\n if (transportPubkeys.length === 0) return [];\n\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: transportPubkeys,\n limit: transportPubkeys.length * 2,\n });\n\n if (events.length === 0) return [];\n\n // Group by author, take most recent per author\n const byAuthor = new Map<string, NostrEvent>();\n for (const event of events) {\n const existing = byAuthor.get(event.pubkey);\n if (!existing || event.created_at > existing.created_at) {\n byAuthor.set(event.pubkey, event);\n }\n }\n\n const results: PeerInfo[] = [];\n for (const [pubkey, event] of byAuthor) {\n try {\n const content = JSON.parse(event.content);\n 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 timestamp: event.created_at * 1000,\n });\n } catch {\n // Skip unparseable events\n }\n }\n\n return results;\n }\n\n /**\n * Recover nametag for the current identity by searching for encrypted nametag events\n * Used after wallet import to recover associated nametag\n * @returns Decrypted nametag or null if none found\n */\n async recoverNametag(): Promise<string | null> {\n await this.ensureConnectedForResolve();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n if (!this.identity || !this.keyManager) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n\n const nostrPubkey = this.getNostrPubkey();\n logger.debug('Nostr', 'Searching for nametag events for pubkey:', nostrPubkey.slice(0, 16) + '...');\n\n // Query for nametag binding events authored by this pubkey\n const events = await this.queryEvents({\n kinds: [EVENT_KINDS.NAMETAG_BINDING],\n authors: [nostrPubkey],\n limit: 10, // Get recent events in case of updates\n });\n\n if (events.length === 0) {\n logger.debug('Nostr', 'No nametag events found for this pubkey');\n return null;\n }\n\n // Sort by timestamp descending to get most recent\n events.sort((a, b) => b.created_at - a.created_at);\n\n // Try to decrypt nametag from events\n for (const event of events) {\n try {\n const content = JSON.parse(event.content);\n if (content.encrypted_nametag) {\n const decrypted = await decryptNametag(\n content.encrypted_nametag,\n this.identity.privateKey\n );\n if (decrypted) {\n logger.debug('Nostr', 'Recovered Unicity ID:', decrypted);\n return decrypted;\n }\n }\n } catch {\n // Try next event\n continue;\n }\n }\n\n logger.debug('Nostr', 'Could not decrypt Unicity ID from any event');\n return null;\n }\n\n /**\n * Publish identity binding event on Nostr.\n * Without nametag: publishes base binding (chainPubkey, 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 try {\n const success = await this.nostrClient!.publishNametagBinding(\n nametag,\n nostrPubkey,\n {\n publicKey: chainPubkey,\n l1Address,\n directAddress,\n },\n );\n\n if (success) {\n logger.debug('Nostr', 'Published identity binding with Unicity ID:', nametag, 'for pubkey:', nostrPubkey.slice(0, 16) + '...');\n }\n return success;\n } catch (error) {\n // publishNametagBinding throws if nametag is already claimed\n if (error instanceof Error && error.message.includes('already claimed')) {\n logger.debug('Nostr', 'Unicity ID already taken:', nametag);\n return false;\n }\n throw error;\n }\n }\n\n // No nametag — delegate to nostr-js-sdk for base identity binding\n const success = await this.nostrClient!.publishIdentityBinding({\n publicKey: chainPubkey,\n 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 /** Persist the max DM (gift-wrap) event timestamp for the since filter on next connect. */\n private updateLastDmEventTimestamp(createdAt: number): void {\n if (!this.storage || !this.keyManager) return;\n if (createdAt <= this.lastDmEventTs) return;\n\n this.lastDmEventTs = createdAt;\n const pubkey = this.keyManager.getPublicKeyHex();\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${pubkey.slice(0, 16)}`;\n\n this.storage.set(storageKey, createdAt.toString()).catch(err => {\n logger.debug('Nostr', 'Failed to save last DM event timestamp:', err);\n });\n }\n\n private async handleDirectMessage(event: NostrEvent): Promise<void> {\n // NIP-04 (kind 4) is deprecated for DMs - only used for legacy token transfers\n // DMs should come through NIP-17 (kind 1059 gift wrap) via handleGiftWrap\n // This handler is kept for backwards compatibility but does NOT dispatch to messageHandlers\n logger.debug('Nostr', 'Ignoring NIP-04 kind 4 event (DMs use NIP-17):', event.id?.slice(0, 12));\n }\n\n private async handleGiftWrap(event: NostrEvent): Promise<void> {\n if (!this.identity || !this.keyManager) {\n logger.debug('Nostr', 'handleGiftWrap: no identity/keyManager');\n return;\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pm = NIP17.unwrap(event as any, this.keyManager);\n\n // Persist DM timestamp after successful unwrap so failed decryptions\n // do not advance the since filter and permanently skip events.\n // Use real wall-clock time, NOT event.created_at — NIP-17 gift wraps\n // randomize created_at by ±2 days for privacy, so it can be in the future.\n this.updateLastDmEventTimestamp(Math.floor(Date.now() / 1000));\n\n logger.debug('Nostr', 'Gift wrap unwrapped, sender:', pm.senderPubkey?.slice(0, 16), 'kind:', pm.kind);\n\n // Handle self-wrap (sent message copy for relay replay)\n if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.selfWrap && parsed.recipientPubkey) {\n logger.debug('Nostr', 'Self-wrap replay for recipient:', parsed.recipientPubkey?.slice(0, 16));\n const message: IncomingMessage = {\n id: parsed.originalId || pm.eventId,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n recipientTransportPubkey: parsed.recipientPubkey,\n content: parsed.text ?? '',\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n isSelfWrap: true,\n };\n for (const handler of this.messageHandlers) {\n try { handler(message); } catch (e) { logger.debug('Nostr', 'Self-wrap handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON self-wrap\n }\n logger.debug('Nostr', 'Skipping own non-self-wrap message');\n return;\n }\n\n // Handle read receipts (kind 15)\n if (isReadReceipt(pm)) {\n logger.debug('Nostr', 'Read receipt from:', pm.senderPubkey?.slice(0, 16), 'for:', pm.replyToEventId);\n if (pm.replyToEventId) {\n const receipt: IncomingReadReceipt = {\n senderTransportPubkey: pm.senderPubkey,\n messageEventId: pm.replyToEventId,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.readReceiptHandlers) {\n try { handler(receipt); } catch (e) { logger.debug('Nostr', 'Read receipt handler error:', e); }\n }\n }\n return;\n }\n\n // Handle composing indicators (kind 25050)\n if (pm.kind === COMPOSING_INDICATOR_KIND) {\n let senderNametag: string | undefined;\n let expiresIn = 30000;\n try {\n const parsed = JSON.parse(pm.content);\n senderNametag = parsed.senderNametag || undefined;\n expiresIn = parsed.expiresIn ?? 30000;\n } catch {\n // Payload parse failed — use defaults\n }\n const indicator = {\n senderPubkey: pm.senderPubkey,\n senderNametag,\n expiresIn,\n };\n logger.debug('Nostr', 'Composing indicator from:', indicator.senderNametag || pm.senderPubkey?.slice(0, 16));\n for (const handler of this.composingHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Composing handler error:', e); }\n }\n return;\n }\n\n // Handle typing indicators (JSON content with type: 'typing')\n try {\n const parsed = JSON.parse(pm.content);\n if (parsed?.type === 'typing') {\n logger.debug('Nostr', 'Typing indicator from:', pm.senderPubkey?.slice(0, 16));\n const indicator: IncomingTypingIndicator = {\n senderTransportPubkey: pm.senderPubkey,\n senderNametag: parsed.senderNametag,\n timestamp: pm.timestamp * 1000,\n };\n for (const handler of this.typingIndicatorHandlers) {\n try { handler(indicator); } catch (e) { logger.debug('Nostr', 'Typing handler error:', e); }\n }\n return;\n }\n } catch {\n // Not JSON — continue to chat message handling\n }\n\n if (!isChatMessage(pm)) {\n logger.debug('Nostr', 'Skipping unknown message kind:', pm.kind);\n return;\n }\n\n // Sphere app wraps DM content as JSON: {senderNametag, text}\n let content = pm.content;\n let senderNametag: string | undefined;\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed === 'object' && parsed.text !== undefined) {\n content = parsed.text;\n senderNametag = parsed.senderNametag || undefined;\n }\n } catch {\n // Plain text — use as-is\n }\n\n logger.debug('Nostr', 'DM received from:', senderNametag || pm.senderPubkey?.slice(0, 16), 'content:', content?.slice(0, 50));\n\n const message: IncomingMessage = {\n // Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.\n // This ensures read receipts reference an ID the sender recognizes.\n id: event.id,\n senderTransportPubkey: pm.senderPubkey,\n senderNametag,\n content,\n timestamp: pm.timestamp * 1000,\n encrypted: true,\n };\n\n this.emitEvent({ type: 'message:received', timestamp: Date.now() });\n\n if (this.messageHandlers.size === 0) {\n logger.debug('Nostr', 'No message handlers registered, buffering message for later delivery');\n this.pendingMessages.push(message);\n } else {\n logger.debug('Nostr', 'Dispatching to', this.messageHandlers.size, 'handlers');\n for (const handler of this.messageHandlers) {\n try {\n handler(message);\n } catch (error) {\n logger.debug('Nostr', 'Message handler error:', error);\n }\n }\n }\n } catch (err) {\n // Expected for gift wraps meant for other recipients\n logger.debug('Nostr', 'Gift wrap decrypt failed (expected if not for us):', (err as Error)?.message?.slice(0, 50));\n }\n }\n\n private 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 const MAX_ATTEMPTS = 3;\n const RETRY_BASE_DELAY_MS = 500;\n const RETRY_JITTER_MS = 200;\n\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n // Snapshot this.nostrClient at the top of each iteration. Using a local\n // reference (instead of `this.nostrClient.publishEvent(...)`) prevents a\n // concurrent disconnect() during the subsequent await from turning the\n // call into a TypeError on null.\n const client = this.nostrClient;\n if (!client) {\n throw new SphereError('Transport disconnected during retry', 'TRANSPORT_ERROR');\n }\n\n try {\n // Re-create SDK event each attempt to avoid reusing the same event ID\n // in nostr-js-sdk's pendingOks map (which would leak timers from prior attempts)\n const sdkEvent = NostrEventClass.fromJSON(event);\n await client.publishEvent(sdkEvent);\n return;\n } catch (err: unknown) {\n lastError = err;\n const rawMessage = err instanceof Error ? err.message : String(err);\n\n // Only retry relay-level rejections (nostr-js-sdk wraps these as \"Event rejected: <reason>\")\n // or relay broadcast failures (\"sent 0 of N\"). All other errors (disconnected, closed,\n // no connected relays) are non-transient and should fail immediately.\n // Locale-invariant match with `en-US` to avoid Turkish-locale surprises\n // where `'I'.toLowerCase()` yields `'ı'` (dotless i).\n const lowered = rawMessage.toLocaleLowerCase('en-US');\n const isRelayRejection =\n lowered.startsWith('event rejected:') ||\n lowered.startsWith('sent 0 of');\n\n if (!isRelayRejection || attempt === MAX_ATTEMPTS) {\n break;\n }\n\n // Add jitter to desynchronize retries across concurrent clients\n const delay = RETRY_BASE_DELAY_MS + Math.floor(Math.random() * RETRY_JITTER_MS);\n logger.debug(\n 'Nostr',\n `publishEvent attempt ${attempt}/${MAX_ATTEMPTS} failed (${rawMessage}); retrying in ${delay}ms`\n );\n await new Promise((r) => setTimeout(r, delay));\n }\n }\n\n const reason = lastError instanceof Error ? lastError.message : String(lastError);\n logger.error('Nostr', `publishEvent failed after ${MAX_ATTEMPTS} attempts: ${reason}`);\n // Chain the original error as `cause` to preserve context for debugging\n throw new SphereError(\n `Failed to publish event: ${reason}`,\n 'TRANSPORT_ERROR',\n lastError\n );\n }\n\n /**\n * Publish an event with verification: after publishing, query the relay to\n * confirm the event was stored. Retries up to `maxAttempts` times on failure.\n *\n * This is critical for token transfers and DMs where silent loss means\n * funds or messages disappear. The nostr-js-sdk's publishEvent resolves on\n * a 5s timeout even without relay confirmation, so verification is needed.\n */\n private async publishWithVerification(\n event: NostrEvent,\n maxAttempts: number = 3,\n label: string = 'event',\n ): Promise<void> {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await this.publishEvent(event);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Permanent rejection — don't retry (e.g., \"Event rejected: invalid signature\")\n if (msg.includes('Event rejected') && !msg.includes('rate') && !msg.includes('limit')) {\n throw err;\n }\n if (attempt === maxAttempts) throw err;\n logger.debug('Nostr', `${label} publish attempt ${attempt} failed (${msg}), retrying in ${attempt}s...`);\n await new Promise(r => setTimeout(r, 1000 * attempt));\n continue;\n }\n\n // Verify: query the relay for this specific event by ID.\n // Jittered delay to let relay index + reduce temporal fingerprinting.\n await new Promise(r => setTimeout(r, 300 + Math.random() * 1200));\n try {\n const found = await this.queryEvents({\n ids: [event.id],\n limit: 1,\n });\n if (found.length > 0) {\n if (attempt > 1) {\n logger.debug('Nostr', `${label} verified on relay after ${attempt} attempt(s)`);\n }\n return; // confirmed on relay\n }\n } catch {\n // Query failed — can't verify. If this is the last attempt,\n // accept the publish as best-effort.\n if (attempt === maxAttempts) {\n logger.debug('Nostr', `${label} verification query failed — accepting publish as best-effort`);\n return;\n }\n }\n\n // Event not found on relay — retry with increasing backoff\n if (attempt < maxAttempts) {\n const delay = Math.min(2000 * attempt, 10000);\n logger.debug('Nostr', `${label} not found on relay, retrying in ${delay}ms (attempt ${attempt}/${maxAttempts})...`);\n await new Promise(r => setTimeout(r, delay));\n } else {\n throw new SphereError(\n `${label} not verified on relay after ${maxAttempts} attempts — delivery failed`,\n 'TRANSPORT_ERROR',\n );\n }\n }\n }\n\n async fetchPendingEvents(): Promise<void> {\n if (!this.nostrClient?.isConnected() || !this.keyManager) {\n throw new SphereError('Transport not connected', 'TRANSPORT_ERROR');\n }\n\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n\n const walletFilter = new Filter();\n walletFilter.kinds = [\n EVENT_KINDS.DIRECT_MESSAGE,\n EVENT_KINDS.TOKEN_TRANSFER,\n EVENT_KINDS.PAYMENT_REQUEST,\n EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,\n EventKinds.GIFT_WRAP, // NIP-17 gift-wrapped DMs (swap proposals, invoice receipts, etc.)\n ];\n walletFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have randomized created_at (±172800 s). Extend the\n // catch-up window by the full randomization range so events sent recently\n // but timestamped far in the past are not missed.\n walletFilter.since = Math.floor(Date.now() / 1000) - 86400 - 172800;\n\n // Capture client reference after the guard to avoid a null-deref race:\n // a concurrent disconnect() can set this.nostrClient = null between the\n // isConnected() check above and the subscribe() call below.\n const client = this.nostrClient;\n\n // Collect events first, then process after EOSE\n const events: NostrEvent[] = [];\n // Declared outside the Promise so it's available for cleanup after resolve.\n let subId: string | undefined;\n\n await new Promise<void>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let settled = false;\n\n // settle() only resolves the Promise — unsubscribe happens AFTER the Promise\n // resolves (below), where subId is guaranteed to be the real subscription ID.\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n resolve();\n };\n\n try {\n subId = client.subscribe(walletFilter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch (err) {\n reject(err);\n return;\n }\n\n if (!settled) {\n timeout = setTimeout(() => settle(), 5000);\n }\n });\n\n // Unsubscribe AFTER the Promise resolves — subId is now the real subscription ID.\n // This also ensures onEvent cannot fire while we iterate events below.\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n\n // Process collected events sequentially (dedup skips already-processed ones)\n for (const event of events) {\n await this.handleEvent(event);\n }\n }\n\n private async queryEvents(filterObj: NostrFilter): Promise<NostrEvent[]> {\n if (!this.nostrClient || !this.nostrClient.isConnected()) {\n throw new SphereError('No connected relays', 'TRANSPORT_ERROR');\n }\n\n // Capture client reference after the guard — same disconnect() race as fetchPendingEvents.\n const client = this.nostrClient;\n\n const events: NostrEvent[] = [];\n const filter = new Filter(filterObj);\n // Declared outside the Promise so unsubscription can happen after resolve(),\n // where subId is guaranteed to be the real subscription ID (not in TDZ).\n let subId: string | undefined;\n\n return new Promise((resolve) => {\n let settled = false;\n const timeout = setTimeout(() => {\n if (settled) return;\n settled = true;\n if (subId) {\n try { client.unsubscribe(subId); } catch { /* disconnected */ }\n }\n logger.warn('Nostr', `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });\n resolve(events);\n }, 15000);\n\n const settle = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeout);\n if (subId) { try { client.unsubscribe(subId); } catch { /* disconnected */ } }\n resolve(events);\n };\n\n try {\n subId = client.subscribe(filter, {\n onEvent: (event) => {\n events.push({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => settle(),\n });\n } catch {\n clearTimeout(timeout);\n resolve(events);\n return;\n }\n\n });\n }\n\n // ===========================================================================\n // Private: Subscriptions\n // ===========================================================================\n\n // Track subscription IDs for cleanup\n private walletSubscriptionId: string | null = null;\n private chatSubscriptionId: string | null = null;\n\n // Chat EOSE handlers — fired once when relay finishes delivering stored DMs\n private chatEoseHandlers: Array<() => void> = [];\n private chatEoseFired = false;\n\n private async subscribeToEvents(): Promise<void> {\n logger.debug('Nostr', 'subscribeToEvents called, identity:', !!this.identity, 'keyManager:', !!this.keyManager, 'nostrClient:', !!this.nostrClient);\n if (this._subscriptionsSuppressed) {\n logger.debug('Nostr', 'subscribeToEvents: suppressed — mux handles event routing');\n return;\n }\n if (!this.identity || !this.keyManager || !this.nostrClient) {\n logger.debug('Nostr', 'subscribeToEvents: skipped - no identity, keyManager, or nostrClient');\n return;\n }\n\n // Unsubscribe from previous subscriptions if any\n if (this.walletSubscriptionId) {\n this.nostrClient.unsubscribe(this.walletSubscriptionId);\n this.walletSubscriptionId = null;\n }\n if (this.chatSubscriptionId) {\n this.nostrClient.unsubscribe(this.chatSubscriptionId);\n this.chatSubscriptionId = null;\n }\n if (this.mainSubscriptionId) {\n this.nostrClient.unsubscribe(this.mainSubscriptionId);\n this.mainSubscriptionId = null;\n }\n\n // Use 32-byte Nostr pubkey (x-coordinate only), not 33-byte compressed key\n const nostrPubkey = this.keyManager.getPublicKeyHex();\n logger.debug('Nostr', 'Subscribing with Nostr pubkey:', nostrPubkey);\n\n // Determine 'since' filter from persisted last event timestamp.\n // - Existing wallet: resume from last processed event (inclusive >=, dedup handles replays)\n // - Fresh wallet / no storage: use current time (no historical replay)\n let since: number;\n if (this.storage) {\n const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(storageKey);\n if (stored) {\n since = parseInt(stored, 10);\n this.lastEventTs = since; // Seed in-memory tracker from storage\n this.fallbackSince = null; // Stored value takes priority\n logger.debug('Nostr', 'Resuming from stored event timestamp:', since);\n } else if (this.fallbackSince !== null) {\n // No stored timestamp but caller provided a fallback (e.g. address creation time).\n // This ensures events sent while the address was inactive are not missed.\n since = this.fallbackSince;\n this.lastEventTs = since;\n this.fallbackSince = null; // Consume once\n logger.debug('Nostr', 'Using fallback since timestamp:', since);\n } else {\n // No stored timestamp, no fallback = fresh wallet, start from now\n since = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored timestamp, starting from now:', since);\n }\n } catch (err) {\n logger.debug('Nostr', 'Failed to read last event timestamp, falling back to now:', err);\n since = Math.floor(Date.now() / 1000);\n this.fallbackSince = null;\n }\n } else {\n // No storage adapter — fallback to last 24h (legacy behavior)\n since = Math.floor(Date.now() / 1000) - 86400;\n logger.debug('Nostr', 'No storage adapter, using 24h fallback');\n }\n\n // Subscribe to wallet events (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 // Determine 'since' for DM (gift-wrap) subscription independently from wallet events.\n let dmSince: number;\n if (this.storage) {\n const dmStorageKey = `${STORAGE_KEYS_GLOBAL.LAST_DM_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;\n try {\n const stored = await this.storage.get(dmStorageKey);\n const parsed = stored ? parseInt(stored, 10) : NaN;\n if (Number.isFinite(parsed)) {\n dmSince = parsed;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Stored value takes priority\n logger.debug('Nostr', 'DM resuming from stored timestamp:', dmSince);\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null; // Consume once\n logger.debug('Nostr', 'DM using fallback since timestamp:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No stored DM timestamp, starting from now:', dmSince);\n }\n } catch (err) {\n if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'Storage read failed, using DM fallback since:', dmSince, err);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'Failed to read last DM event timestamp, falling back to now:', err);\n }\n }\n } else if (this.fallbackDmSince !== null) {\n dmSince = this.fallbackDmSince;\n this.lastDmEventTs = dmSince;\n this.fallbackDmSince = null;\n logger.debug('Nostr', 'No storage adapter for DM, using fallback since:', dmSince);\n } else {\n dmSince = Math.floor(Date.now() / 1000);\n logger.debug('Nostr', 'No storage adapter for DM, starting from now:', dmSince);\n }\n\n const chatFilter = new Filter();\n chatFilter.kinds = [EventKinds.GIFT_WRAP];\n chatFilter['#p'] = [nostrPubkey];\n // NIP-17 gift wraps have created_at randomized ±2 days for privacy.\n // Without this offset, ~50% of messages are silently dropped by the relay\n // because their randomized timestamp lands before the `since` filter.\n // Math.max(0, ...) prevents negative timestamps when dmSince is small.\n chatFilter.since = Math.max(0, dmSince - TIMESTAMP_RANDOMIZATION);\n\n this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {\n onEvent: (event) => {\n logger.debug('Nostr', 'Received chat event kind:', event.kind, 'id:', event.id?.slice(0, 12));\n this.handleEvent({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n onEndOfStoredEvents: () => {\n logger.debug('Nostr', 'Chat subscription ready (EOSE)');\n if (!this.chatEoseFired) {\n this.chatEoseFired = true;\n for (const handler of this.chatEoseHandlers) {\n try { handler(); } catch { /* ignore */ }\n }\n this.chatEoseHandlers = [];\n }\n },\n onError: (_subId, error) => {\n logger.debug('Nostr', 'Chat subscription error:', error);\n },\n });\n logger.debug('Nostr', 'Chat subscription created, subId:', this.chatSubscriptionId);\n }\n\n private subscribeToTags(tags: string[]): void {\n if (!this.nostrClient) return;\n\n const key = tags.sort().join(':');\n const filter = new Filter({\n kinds: [EVENT_KINDS.BROADCAST],\n '#t': tags,\n since: Math.floor(Date.now() / 1000) - 3600, // Last hour\n });\n\n const subId = this.nostrClient.subscribe(filter, {\n onEvent: (event) => {\n this.handleBroadcast({\n id: event.id,\n kind: event.kind,\n content: event.content,\n tags: event.tags,\n pubkey: event.pubkey,\n created_at: event.created_at,\n sig: event.sig,\n });\n },\n });\n\n this.broadcastSubscriptions.set(key, subId);\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private async decryptContent(content: string, senderPubkey: string): Promise<string> {\n if (!this.keyManager) throw new SphereError('KeyManager not initialized', 'NOT_INITIALIZED');\n\n // Decrypt content using NIP-04 (using hex variant for string keys)\n const decrypted = await NIP04.decryptHex(\n content,\n this.keyManager.getPrivateKeyHex(),\n senderPubkey\n );\n\n // Strip known prefixes for compatibility with nostr-js-sdk\n return this.stripContentPrefix(decrypted);\n }\n\n /**\n * Strip known content prefixes (nostr-js-sdk compatibility)\n * Handles: payment_request:, token_transfer:, etc.\n */\n private stripContentPrefix(content: string): string {\n const prefixes = [\n 'payment_request:',\n 'token_transfer:',\n 'payment_response:',\n ];\n\n for (const prefix of prefixes) {\n if (content.startsWith(prefix)) {\n return content.slice(prefix.length);\n }\n }\n\n return content;\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureConnected(): void {\n if (!this.isConnected()) {\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n }\n\n /**\n * Async version of ensureConnected — reconnects if the original transport\n * lost its WebSocket while subscriptions are suppressed (mux handles events).\n * Used by resolve methods which are always async.\n */\n private async ensureConnectedForResolve(): Promise<void> {\n if (this.isConnected()) return;\n\n // When mux is active, the original transport can lose its idle WebSocket.\n // Reconnect transparently so resolve operations still work.\n if (this._subscriptionsSuppressed && this.nostrClient) {\n logger.debug('Nostr', 'Suppressed transport disconnected — reconnecting for resolve');\n try {\n await Promise.race([\n this.nostrClient.connect(...this.config.relays),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('reconnect timeout')), 5000)\n ),\n ]);\n if (this.nostrClient.isConnected()) {\n this.status = 'connected';\n return;\n }\n } catch {\n // Fall through to throw\n }\n }\n\n throw new SphereError('NostrTransportProvider not connected', 'TRANSPORT_ERROR');\n }\n\n private ensureReady(): void {\n this.ensureConnected();\n if (!this.identity) {\n throw new SphereError('Identity not set', 'NOT_INITIALIZED');\n }\n }\n\n private emitEvent(event: TransportEvent): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(event);\n } catch (error) {\n logger.debug('Nostr', 'Event callback error:', error);\n }\n }\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom inner rumor kind.\n * Replicates the three-layer NIP-59 envelope (rumor → seal → gift wrap)\n * because NIP17.createGiftWrap hardcodes kind 14 for the inner rumor.\n */\n private createCustomKindGiftWrap(recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n return NostrTransportProvider.createCustomKindGiftWrap(this.keyManager!, recipientPubkeyHex, content, rumorKind);\n }\n\n /**\n * Create a NIP-17 gift wrap with a custom rumor kind.\n * Shared between NostrTransportProvider and MultiAddressTransportMux.\n */\n static createCustomKindGiftWrap(keyManager: NostrKeyManager, recipientPubkeyHex: string, content: string, rumorKind: number): NostrEventClass {\n const senderPubkey = keyManager.getPublicKeyHex();\n const now = Math.floor(Date.now() / 1000);\n\n // 1. Create Rumor (unsigned inner event with custom kind)\n const rumorTags: string[][] = [['p', recipientPubkeyHex]];\n const rumorSerialized = JSON.stringify([0, senderPubkey, now, rumorKind, rumorTags, content]);\n const rumorId = bytesToHex(sha256Noble(new TextEncoder().encode(rumorSerialized)));\n const rumor = { id: rumorId, pubkey: senderPubkey, created_at: now, kind: rumorKind, tags: rumorTags, content };\n\n // 2. Create Seal (kind 13, signed by sender, encrypts rumor)\n const recipientPubkeyBytes = hexToBytes(recipientPubkeyHex);\n const encryptedRumor = NIP44.encrypt(JSON.stringify(rumor), keyManager.getPrivateKey(), recipientPubkeyBytes);\n const sealTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const seal = NostrEventClass.create(keyManager, {\n kind: EventKinds.SEAL,\n tags: [],\n content: encryptedRumor,\n created_at: sealTimestamp,\n });\n\n // 3. Create Gift Wrap (kind 1059, signed by ephemeral key, encrypts seal)\n const ephemeralKeys = NostrKeyManager.generate();\n const encryptedSeal = NIP44.encrypt(JSON.stringify(seal.toJSON()), ephemeralKeys.getPrivateKey(), recipientPubkeyBytes);\n const wrapTimestamp = now + Math.floor(Math.random() * 2 * TIMESTAMP_RANDOMIZATION) - TIMESTAMP_RANDOMIZATION;\n const giftWrap = NostrEventClass.create(ephemeralKeys, {\n kind: EventKinds.GIFT_WRAP,\n tags: [['p', recipientPubkeyHex]],\n content: encryptedSeal,\n created_at: wrapTimestamp,\n });\n ephemeralKeys.clear();\n\n return giftWrap;\n }\n\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface NostrEvent {\n id: string;\n kind: number;\n content: string;\n tags: string[][];\n pubkey: string;\n created_at: number;\n sig: string;\n}\n\ninterface NostrFilter {\n ids?: string[];\n authors?: string[];\n kinds?: number[];\n '#p'?: string[];\n '#t'?: string[];\n '#d'?: string[];\n since?: number;\n until?: number;\n limit?: number;\n}\n","/**\n * WebSocket Abstraction\n * Platform-independent WebSocket interface for cross-platform support\n */\n\n// =============================================================================\n// WebSocket Interface\n// =============================================================================\n\n/**\n * Minimal WebSocket interface compatible with browser and Node.js\n */\nexport interface IWebSocket {\n readonly readyState: number;\n\n send(data: string): void;\n close(code?: number, reason?: string): void;\n\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: IMessageEvent) => void) | null;\n}\n\nexport interface IMessageEvent {\n data: string;\n}\n\n/**\n * WebSocket ready states (same as native WebSocket)\n */\nexport const WebSocketReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\n\n/**\n * Factory function to create WebSocket instances\n * Different implementations for browser (native) vs Node.js (ws package)\n */\nexport type WebSocketFactory = (url: string) => IWebSocket;\n\n// =============================================================================\n// UUID Generator\n// =============================================================================\n\n/**\n * Generate a unique ID (platform-independent)\n * Browser: crypto.randomUUID()\n * Node: crypto.randomUUID() or uuid package\n */\nexport type UUIDGenerator = () => string;\n\n/**\n * Default UUID generator using crypto.randomUUID\n * Works in modern browsers and Node 19+\n */\nexport function defaultUUIDGenerator(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n","/**\n * Browser Transport Exports\n * Re-exports shared transport with browser-specific WebSocket factory\n */\n\nimport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\nimport type { IWebSocket } from '../../../transport/websocket';\n\n// Re-export shared types and classes\nexport {\n NostrTransportProvider,\n type NostrTransportProviderConfig,\n} from '../../../transport/NostrTransportProvider';\n\nexport {\n type IWebSocket,\n type IMessageEvent,\n type WebSocketFactory,\n type UUIDGenerator,\n WebSocketReadyState,\n defaultUUIDGenerator,\n} from '../../../transport/websocket';\n\n// =============================================================================\n// Browser WebSocket Factory\n// =============================================================================\n\n/**\n * Browser WebSocket factory using native WebSocket\n * Cast to IWebSocket since native WebSocket is a superset\n */\nexport function createBrowserWebSocket(url: string): IWebSocket {\n return new WebSocket(url) as unknown as IWebSocket;\n}\n\n/**\n * Create NostrTransportProvider with browser WebSocket\n * Convenience factory that injects browser-native WebSocket\n */\nexport function createNostrTransportProvider(\n config?: Omit<NostrTransportProviderConfig, 'createWebSocket'>\n): NostrTransportProvider {\n return new NostrTransportProvider({\n ...config,\n createWebSocket: createBrowserWebSocket,\n });\n}\n","/**\n * Unicity Aggregator Provider\n *\n * Post v1-cutover this is a thin NETWORK-CONFIG provider for the v2 token\n * engine: it loads the root trust base (JSON) via the injected platform loader\n * and exposes the gateway URL + API key. The engine (token-engine/) builds its\n * own SDK clients from these — no state-transition SDK objects live here.\n *\n * `validateToken` remains as a best-effort JSON-RPC check for LEGACY v1 TXF\n * tokens still present in storage (display-path only).\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 ValidationResult,\n OracleEvent,\n OracleEventCallback,\n TrustBaseLoader,\n} from './oracle-provider';\nimport { DEFAULT_AGGREGATOR_TIMEOUT } from '../constants';\nimport { SphereError } from '../core/errors';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface UnicityAggregatorProviderConfig {\n /** Aggregator (gateway) URL */\n url: string;\n /** API key for authentication */\n apiKey?: string;\n /** Request timeout (ms) */\n timeout?: number;\n /** Skip trust base loading (dev only) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n /** Trust base loader (platform-specific) */\n trustBaseLoader?: TrustBaseLoader;\n}\n\n// =============================================================================\n// RPC Response Types\n// =============================================================================\n\ninterface RpcValidateResponse {\n valid?: boolean;\n spent?: boolean;\n stateHash?: string;\n error?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class UnicityAggregatorProvider implements OracleProvider {\n readonly id = 'unicity-aggregator';\n readonly name = 'Unicity Aggregator';\n readonly type = 'network' as const;\n readonly description = 'Unicity gateway network config (trust base + URL + API key) for the v2 token engine';\n\n private config: Required<Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'>> & {\n trustBaseLoader?: TrustBaseLoader;\n };\n private status: ProviderStatus = 'disconnected';\n private eventCallbacks: Set<OracleEventCallback> = new Set();\n\n /** Raw trust-base JSON as loaded (the v2 token engine parses it itself). */\n private trustBaseJson: unknown | null = null;\n\n // Cache for spent states reported by validateToken (immutable once spent)\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 // v2 token-engine config surface\n // ===========================================================================\n\n /** Raw trust-base JSON (for constructing the v2 token engine); null when unavailable. */\n getTrustBaseJson(): unknown | null {\n return this.trustBaseJson;\n }\n\n /** The gateway URL this provider is configured against. */\n getAggregatorUrl(): string {\n return this.config.url;\n }\n\n /** The gateway API key (for the v2 token engine); undefined when none is configured. */\n getApiKey(): string | undefined {\n return this.config.apiKey || undefined;\n }\n\n // ===========================================================================\n // BaseProvider Implementation\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.status === 'connected') return;\n\n this.status = 'connecting';\n\n // Mark as connected — actual connectivity is verified on first operation.\n this.status = 'connected';\n this.emitEvent({ type: 'oracle:connected', timestamp: Date.now() });\n this.log('Connected to oracle:', this.config.url);\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n this.emitEvent({ type: 'oracle:disconnected', timestamp: Date.now() });\n this.log('Disconnected from oracle');\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ===========================================================================\n // OracleProvider Implementation\n // ===========================================================================\n\n async initialize(trustBaseJson?: unknown): Promise<void> {\n if (trustBaseJson) {\n this.trustBaseJson = trustBaseJson;\n } else if (!this.config.skipVerification && this.config.trustBaseLoader) {\n try {\n const loaded = await this.config.trustBaseLoader.load();\n if (loaded) {\n this.trustBaseJson = loaded;\n }\n } catch (error) {\n this.log('Failed to load trust base:', error);\n }\n }\n\n await this.connect();\n this.log('Initialized with trust base JSON:', !!this.trustBaseJson);\n }\n\n /**\n * Best-effort RPC validation for LEGACY v1 TXF tokens (display path only).\n * v2 blob tokens are verified via the token engine and never reach this.\n */\n async validateToken(tokenData: unknown): Promise<ValidationResult> {\n this.ensureConnected();\n\n try {\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 // 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// Testnet2 (networkId 4) — embedded copy of bft-trustbase.testnet2.json\nexport const TRUSTBASE_TESTNET2 = {\n version: 1,\n networkId: 4,\n epoch: 1,\n epochStartRound: 1,\n rootNodes: [\n {\n nodeId: '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy',\n sigKey: '0x02b2b1daf4dee562ef5ed5b89e1e0e6cf0bf952ce3af12be6531e40f8bd1a7e682',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M',\n sigKey: '0x02822d7ce59d2ff4f2da4cef361d5a80a3a74c351fe61a8a2c908a54841930cee0',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC',\n sigKey: '0x0352ef027d60921e252839bea60e2f1810473fb0ad8cb89044b101aecdea5f356a',\n stake: 1,\n },\n {\n nodeId: '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF',\n sigKey: '0x02ef71c49c77ca9510ad84596522b887e4df0b3262fce80192ddcfb6276efdf901',\n stake: 1,\n },\n ],\n quorumThreshold: 3,\n stateHash: '',\n changeRecordHash: '',\n previousEntryHash: '',\n signatures: {\n '16Uiu2HAm33LfAYP811b4fpW5x8qejmujuB1LFCXPGGTT6bumHrvy':\n '0xe9289562b9faf283ae264ce42ef641133237bca6b7f232e1e293305f0cf994d3262cee1ca9c8de93fc5b7da1466486123b6219e5308843b8ead173472dad35e501',\n '16Uiu2HAmFCiccNnmDEdcBiBq5Giz5VsEUzdcL8uDe2QPTjE3ZL3M':\n '0x45e26943305c264d5dc5c3069cd5dbd2d43feb35d56d580656b80b33f4126e2a735b0c8eace0ef638128d9dbf5eb854dd9ad3ba42f9eafded60251bf7edb454501',\n '16Uiu2HAmFzgCoCdKt8y7so3w5jfgxc2FCW8p1bffHzY3L6zqc8BC':\n '0xe2263c1cb24b6d933ead249901c7fd83fa884d95b935a63f1489b07ea342281071cbf2117ad27be29cde9207d8d87e8b331630718d92d644faf2fccbc9dba4c000',\n '16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF':\n '0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301',\n },\n};\n\n// Mainnet trust base (TODO: add when available)\nexport const TRUSTBASE_MAINNET = null;\n\n// Dev trust base (same as testnet for now)\nexport const TRUSTBASE_DEV = TRUSTBASE_TESTNET;\n","/**\n * Shared TrustBase Loader Logic\n * Common embedded trustbase data and base loader\n */\n\nimport { TRUSTBASE_TESTNET2, TRUSTBASE_MAINNET, TRUSTBASE_DEV } from '../../assets/trustbase';\nimport type { NetworkType } from '../../constants';\nimport { SphereError } from '../../core/errors';\n\nexport interface TrustBaseLoader {\n load(): Promise<unknown | null>;\n}\n\n/**\n * Get embedded trustbase data by network\n */\nexport function getEmbeddedTrustBase(network: NetworkType): unknown | null {\n switch (network) {\n case 'mainnet':\n return TRUSTBASE_MAINNET;\n // v1 cutover: 'testnet' is now an alias of testnet2 (NETWORKS.testnet points\n // at the testnet2 gateway), so it MUST resolve the testnet2 trust base —\n // a mismatched trust base would make the engine reject every proof.\n case 'testnet':\n case 'testnet2':\n return TRUSTBASE_TESTNET2;\n case 'dev':\n return TRUSTBASE_DEV;\n default:\n // Fail loud: an unknown network must not silently resolve to the testnet trust base.\n throw new SphereError(`getEmbeddedTrustBase: unknown network \"${network}\"`, 'INVALID_CONFIG');\n }\n}\n\n/**\n * Base TrustBase loader with embedded fallback\n */\nexport abstract class BaseTrustBaseLoader implements TrustBaseLoader {\n protected network: NetworkType;\n\n constructor(network: NetworkType = 'testnet') {\n this.network = network;\n }\n\n /**\n * Try to load from external source (file, URL, etc.)\n * Override in subclass\n */\n protected abstract loadFromExternal(): Promise<unknown | null>;\n\n async load(): Promise<unknown | null> {\n // Try external source first\n const external = await this.loadFromExternal();\n if (external) {\n return external;\n }\n\n // Fallback to embedded data\n return getEmbeddedTrustBase(this.network);\n }\n}\n","/**\n * Browser Oracle Exports\n * Re-exports shared oracle with browser-specific TrustBaseLoader\n */\n\nimport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\nimport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\nimport { BaseTrustBaseLoader } from '../../shared/trustbase-loader';\nimport type { NetworkType } from '../../../constants';\nimport { SphereError } from '../../../core/errors';\n\n// Re-export shared types and classes\nexport {\n UnicityAggregatorProvider,\n type UnicityAggregatorProviderConfig,\n UnicityOracleProvider,\n type UnicityOracleProviderConfig,\n} from '../../../oracle/UnicityAggregatorProvider';\n\nexport type { TrustBaseLoader } from '../../../oracle/oracle-provider';\n\n// =============================================================================\n// Browser TrustBase Loader\n// =============================================================================\n\n/**\n * Browser TrustBase loader - fetches from URL or uses embedded data\n */\nexport class BrowserTrustBaseLoader extends BaseTrustBaseLoader {\n private url?: string;\n\n /**\n * @param networkOrUrl - a NetworkType, or a URL to fetch the trust base from\n * @param fallbackNetwork - network used for the embedded fallback when a URL is\n * supplied (and the fetch fails). Required when networkOrUrl is a URL — we must\n * never silently fall back to testnet on a different network.\n */\n constructor(networkOrUrl?: NetworkType | string, fallbackNetwork?: NetworkType) {\n if (!networkOrUrl) {\n throw new SphereError('BrowserTrustBaseLoader: network or trustBaseUrl is required.', 'INVALID_CONFIG');\n }\n if (networkOrUrl.startsWith('/') || networkOrUrl.startsWith('http')) {\n if (!fallbackNetwork) {\n throw new SphereError(\n 'BrowserTrustBaseLoader: fallbackNetwork is required when a trustBaseUrl is supplied.',\n 'INVALID_CONFIG',\n );\n }\n super(fallbackNetwork);\n this.url = networkOrUrl;\n } else {\n super(networkOrUrl as NetworkType);\n }\n }\n\n protected async loadFromExternal(): Promise<unknown | null> {\n if (!this.url) return null;\n\n try {\n const response = await fetch(this.url);\n if (response.ok) {\n return await response.json();\n }\n } catch {\n // Fall through to embedded\n }\n return null;\n }\n}\n\n/**\n * Create browser TrustBase loader\n */\nexport function createBrowserTrustBaseLoader(\n networkOrUrl?: NetworkType | string,\n fallbackNetwork?: NetworkType,\n): TrustBaseLoader {\n return new BrowserTrustBaseLoader(networkOrUrl, fallbackNetwork);\n}\n\n// =============================================================================\n// Browser Factory\n// =============================================================================\n\n/**\n * Create UnicityAggregatorProvider with browser TrustBase loader\n */\nexport function createUnicityAggregatorProvider(\n config: Omit<UnicityAggregatorProviderConfig, 'trustBaseLoader'> & {\n trustBaseUrl?: string;\n network?: NetworkType;\n }\n): UnicityAggregatorProvider {\n const { trustBaseUrl, network, ...restConfig } = config;\n // Fail loud: without a network we cannot pick the right embedded trust base.\n if (!trustBaseUrl && !network) {\n throw new SphereError(\n 'createUnicityAggregatorProvider: network or trustBaseUrl required.',\n 'INVALID_CONFIG',\n );\n }\n return new UnicityAggregatorProvider({\n ...restConfig,\n // When a URL is supplied, `network` (if any) is the embedded-fallback network.\n trustBaseLoader: createBrowserTrustBaseLoader(trustBaseUrl ?? network, network),\n });\n}\n\n/** @deprecated Use createUnicityAggregatorProvider instead */\nexport const createUnicityOracleProvider = createUnicityAggregatorProvider;\n","/**\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/**\n * Recover the compressed secp256k1 public key from a signed message.\n *\n * Use this when the server needs to identify the signer (not just verify\n * a signature against a known pubkey). Combined with `sphere.resolve(pubkey)`\n * it gives a fully cryptographically-attributable identity for backend auth,\n * without trusting any client-supplied identifier claim.\n *\n * @param message - The plaintext that was signed\n * @param signature - 130-char hex (v + r + s) as produced by `signMessage`\n * @returns - Compressed 66-char hex pubkey\n * @throws - On malformed signature length or out-of-range recovery byte\n */\nexport function recoverPubkeyFromSignature(message: string, signature: string): string {\n if (signature.length !== 130) {\n throw new SphereError(\n `Invalid signature length: expected 130 hex chars, got ${signature.length}`,\n 'SIGNING_ERROR',\n );\n }\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) {\n throw new SphereError(\n `Invalid recovery byte: v=${v} out of range [0..3]`,\n 'SIGNING_ERROR',\n );\n }\n\n const hashHex = hashSignMessage(message);\n const hashBytes = Buffer.from(hashHex, 'hex');\n\n const recovered = ec.recoverPubKey(hashBytes, { r, s }, v);\n return recovered.encode('hex', true);\n}\n\n// Re-export elliptic instance for advanced use cases\nexport { ec };\n","/**\n * wallet-api/errors.ts — the client's typed error surface (S1).\n *\n * HTTP error mapping follows ARCHITECTURE §16: `{ error: { code, message } }`\n * with 401 auth · 403 ownership/addressee · 404 unknown · 409 conflict\n * (lineage/ownership) · 413 too large · 422 validation · 429 rate-limit/quota.\n */\n\nexport type WalletApiErrorCode =\n /** Construction/config problems (bad base URL, missing identity). */\n | 'CONFIG'\n /** The server challenge failed template verification — never signed. */\n | 'CHALLENGE_TEMPLATE'\n /** 401 — session invalid and re-auth also failed. */\n | 'UNAUTHORIZED'\n /** 403 — ownership/addressee check failed. */\n | 'FORBIDDEN'\n /** 404 — unknown resource. */\n | 'NOT_FOUND'\n /** 409 — lineage/ownership conflict (e.g. evidenced-tombstone re-add — §5.3). */\n | 'CONFLICT'\n /** 413 — blob too large. */\n | 'TOO_LARGE'\n /** 422 — validation failure (malformed body, failed §8.2 pipeline). */\n | 'VALIDATION'\n /** 429 — rate limit / quota. */\n | 'RATE_LIMITED'\n /** Transport-level failure (fetch threw, connection refused, …). */\n | 'NETWORK'\n /** The response did not match the §16 contract (parse/shape failure). */\n | 'PROTOCOL'\n /** Any other non-2xx status. */\n | 'SERVER';\n\nconst STATUS_TO_CODE: Record<number, WalletApiErrorCode> = {\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 409: 'CONFLICT',\n 413: 'TOO_LARGE',\n 422: 'VALIDATION',\n 429: 'RATE_LIMITED',\n};\n\nexport class WalletApiError extends Error {\n readonly code: WalletApiErrorCode;\n /** HTTP status when the error came from a response. */\n readonly status?: number;\n\n constructor(message: string, code: WalletApiErrorCode, status?: number, cause?: unknown) {\n super(message);\n this.name = 'WalletApiError';\n this.code = code;\n this.status = status;\n if (cause !== undefined) (this as { cause?: unknown }).cause = cause;\n }\n\n static fromStatus(status: number, message: string): WalletApiError {\n return new WalletApiError(message, STATUS_TO_CODE[status] ?? 'SERVER', status);\n }\n}\n\n/**\n * The auth challenge failed template verification (S1): wrong domain prefix,\n * foreign pubkey, or implausible timestamps. The spend key never signs\n * server-chosen text that fails this check.\n */\nexport class ChallengeTemplateError extends WalletApiError {\n constructor(message: string) {\n super(message, 'CHALLENGE_TEMPLATE');\n this.name = 'ChallengeTemplateError';\n }\n}\n","/**\n * wallet-api/challenge.ts — auth-challenge template verification (S1,\n * ARCHITECTURE §4).\n *\n * The spend key NEVER signs arbitrary server-chosen text. Before signing, the\n * client verifies the challenge:\n * 1. begins with the fixed domain-separation prefix\n * `unicity:wallet-api:auth:v1\\n`;\n * 2. embeds the client's OWN pubkey (and the nonce returned alongside it);\n * 3. carries plausible timestamps (`issuedAt` near now, `expiresAt` in the\n * future, a sane validity window).\n *\n * The accepted grammar matches the REAL backend (wallet-api M1,\n * src/auth/service.ts): the prefix followed by a single-line JSON object with\n * `network`, `pubkey`, `nonce`, `issuedAt`, `expiresAt` (ISO-8601). This\n * grammar is the client's acceptance contract: a server whose challenge does\n * not parse is refused, by design. The fake server\n * (tests/support/fake-wallet-api.ts) produces exactly this form.\n */\n\nimport { ChallengeTemplateError } from './errors';\n\n/** Fixed domain-separation prefix (ARCHITECTURE §4 step 1). */\nexport const AUTH_CHALLENGE_PREFIX = 'unicity:wallet-api:auth:v1\\n';\n\n/** Allowed clock skew between client and server (ms). */\nconst MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;\n\n/** Maximum plausible challenge validity window (server default: NONCE_TTL = 5 min). */\nconst MAX_VALIDITY_WINDOW_MS = 60 * 60 * 1000;\n\nexport interface ChallengeExpectation {\n /** The client's own compressed pubkey hex — the challenge MUST embed it. */\n pubkey: string;\n /** The nonce returned by `POST /v1/auth/challenge` alongside the challenge. */\n nonce: string;\n /** The client's network — refused if the challenge names a different one. */\n network: string;\n /** Current time, ms since epoch. */\n nowMs: number;\n}\n\nfunction parseFields(body: string): Map<string, string> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n throw new ChallengeTemplateError('Challenge body is not the single-line JSON object the backend emits');\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new ChallengeTemplateError('Challenge body must be a JSON object');\n }\n const fields = new Map<string, string>();\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value !== 'string') {\n throw new ChallengeTemplateError(`Challenge field \"${key}\" must be a string`);\n }\n fields.set(key, value);\n }\n return fields;\n}\n\nfunction requireField(fields: Map<string, string>, key: string): string {\n const value = fields.get(key);\n if (value === undefined || value === '') {\n throw new ChallengeTemplateError(`Challenge is missing the \"${key}\" field`);\n }\n return value;\n}\n\nfunction parseTimestamp(value: string, key: string): number {\n const ms = Date.parse(value);\n if (Number.isNaN(ms)) {\n throw new ChallengeTemplateError(`Challenge \"${key}\" is not a parseable timestamp: ${value}`);\n }\n return ms;\n}\n\n/**\n * Verify a server auth challenge before signing. Throws\n * {@link ChallengeTemplateError} on ANY violation — the caller MUST refuse to\n * sign in that case.\n */\nexport function verifyChallengeTemplate(challenge: string, expect: ChallengeExpectation): void {\n if (typeof challenge !== 'string' || !challenge.startsWith(AUTH_CHALLENGE_PREFIX)) {\n throw new ChallengeTemplateError('Challenge does not start with the unicity:wallet-api:auth:v1 domain prefix');\n }\n const fields = parseFields(challenge.slice(AUTH_CHALLENGE_PREFIX.length));\n\n const pubkey = requireField(fields, 'pubkey');\n if (pubkey.toLowerCase() !== expect.pubkey.toLowerCase()) {\n throw new ChallengeTemplateError('Challenge embeds a different pubkey than this wallet');\n }\n\n const nonce = requireField(fields, 'nonce');\n if (nonce !== expect.nonce) {\n throw new ChallengeTemplateError('Challenge nonce does not match the nonce issued with it');\n }\n\n const network = requireField(fields, 'network');\n if (network !== expect.network) {\n throw new ChallengeTemplateError(`Challenge is for network \"${network}\", expected \"${expect.network}\"`);\n }\n\n const issuedAt = parseTimestamp(requireField(fields, 'issuedAt'), 'issuedAt');\n const expiresAt = parseTimestamp(requireField(fields, 'expiresAt'), 'expiresAt');\n\n if (issuedAt > expect.nowMs + MAX_CLOCK_SKEW_MS) {\n throw new ChallengeTemplateError('Challenge issuedAt is in the future');\n }\n if (expiresAt <= expect.nowMs - MAX_CLOCK_SKEW_MS) {\n throw new ChallengeTemplateError('Challenge is already expired');\n }\n if (expiresAt <= issuedAt || expiresAt - issuedAt > MAX_VALIDITY_WINDOW_MS) {\n throw new ChallengeTemplateError('Challenge validity window is implausible');\n }\n}\n","/**\n * wallet-api/codec.ts — runtime wire ⇄ domain conversion for §16 responses.\n *\n * Hand-rolled validators (repo convention — no zod dependency). The hard rule\n * (ARCHITECTURE §11): asset amounts are decimal strings on the wire and\n * `bigint` in domain types — never a JS `number` (they exceed 2^53). Cursors,\n * seqs and the syncEpoch are parsed the same way; the codec tolerates either\n * a decimal string (the Postgres `bigint`-over-`pg` wire form) or a safe\n * integer number for those counters, but amounts MUST be strings.\n */\n\nimport type { InventoryItem } from '../storage/storage-provider';\nimport { WalletApiError } from './errors';\nimport type {\n BlobUrlEntry,\n CoinBalance,\n HistoryPage,\n HistoryWireRecord,\n IntentRecord,\n InventoryPage,\n MailboxClaimResult,\n MailboxEntry,\n MailboxPage,\n PaymentRequestRecord,\n PaymentRequestsPage,\n UploadUrlEntry,\n} from './types';\n\nfunction protocolError(message: string): WalletApiError {\n return new WalletApiError(message, 'PROTOCOL');\n}\n\nfunction asRecord(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw protocolError(`${what} is not an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction asArray(value: unknown, what: string): unknown[] {\n if (!Array.isArray(value)) throw protocolError(`${what} is not an array`);\n return value;\n}\n\nfunction asString(value: unknown, what: string): string {\n if (typeof value !== 'string') throw protocolError(`${what} is not a string`);\n return value;\n}\n\n/** Counter (cursor/seq/syncEpoch): decimal string or safe integer → bigint. */\nexport function parseCounter(value: unknown, what: string): bigint {\n if (typeof value === 'string' && /^[0-9]+$/.test(value)) return BigInt(value);\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return BigInt(value);\n throw protocolError(`${what} is not a non-negative integer counter`);\n}\n\n/** Amount: decimal string ONLY (§11) → bigint. */\nexport function parseAmount(value: unknown, what: string): bigint {\n if (typeof value === 'string' && /^[0-9]+$/.test(value)) return BigInt(value);\n throw protocolError(`${what} is not a decimal-string amount`);\n}\n\nfunction parseAssets(value: unknown, what: string): InventoryItem['assets'] {\n if (value === undefined || value === null) return undefined;\n return asArray(value, what).map((raw, i) => {\n const rec = asRecord(raw, `${what}[${i}]`);\n return {\n coinId: asString(rec.coinId, `${what}[${i}].coinId`),\n amount: parseAmount(rec.amount, `${what}[${i}].amount`),\n };\n });\n}\n\nfunction parseInventoryItem(raw: unknown, what: string): InventoryItem {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'active' && status !== 'removed') {\n throw protocolError(`${what}.status is neither 'active' nor 'removed'`);\n }\n return {\n tokenId: asString(rec.tokenId, `${what}.tokenId`),\n status,\n assets: parseAssets(rec.assets, `${what}.assets`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n };\n}\n\n/** `GET /v1/inventory` → {@link InventoryPage} (§16). */\nexport function parseInventoryPage(body: unknown): InventoryPage {\n const rec = asRecord(body, 'inventory response');\n if (typeof rec.more !== 'boolean') throw protocolError('inventory response .more is not a boolean');\n return {\n cursor: parseCounter(rec.cursor, 'inventory response .cursor'),\n syncEpoch: parseCounter(rec.syncEpoch, 'inventory response .syncEpoch'),\n more: rec.more,\n items: asArray(rec.items, 'inventory response .items').map((raw, i) =>\n parseInventoryItem(raw, `items[${i}]`)\n ),\n };\n}\n\n/** `GET /v1/balances` → {@link CoinBalance}[] (§16). */\nexport function parseBalances(body: unknown): CoinBalance[] {\n const rec = asRecord(body, 'balances response');\n return asArray(rec.balances, 'balances response .balances').map((raw, i) => {\n const b = asRecord(raw, `balances[${i}]`);\n const tokenCount = b.tokenCount;\n if (typeof tokenCount !== 'number' || !Number.isSafeInteger(tokenCount) || tokenCount < 0) {\n throw protocolError(`balances[${i}].tokenCount is not a non-negative integer`);\n }\n return {\n coinId: asString(b.coinId, `balances[${i}].coinId`),\n total: parseAmount(b.total, `balances[${i}].total`),\n tokenCount,\n };\n });\n}\n\n/** `POST /v1/tokens/blob-urls` → {@link BlobUrlEntry}[] (§16). */\nexport function parseBlobUrls(body: unknown): BlobUrlEntry[] {\n const rec = asRecord(body, 'blob-urls response');\n return asArray(rec.urls, 'blob-urls response .urls').map((raw, i) => {\n const u = asRecord(raw, `urls[${i}]`);\n return {\n tokenId: asString(u.tokenId, `urls[${i}].tokenId`),\n getUrl: asString(u.getUrl, `urls[${i}].getUrl`),\n };\n });\n}\n\n/** `POST /v1/tokens/upload-urls` → {@link UploadUrlEntry}[] (§16). */\nexport function parseUploadUrls(body: unknown): UploadUrlEntry[] {\n const rec = asRecord(body, 'upload-urls response');\n return asArray(rec.urls, 'upload-urls response .urls').map((raw, i) => {\n const u = asRecord(raw, `urls[${i}]`);\n return {\n sha256: asString(u.sha256, `urls[${i}].sha256`),\n key: asString(u.key, `urls[${i}].key`),\n putUrl: asString(u.putUrl, `urls[${i}].putUrl`),\n };\n });\n}\n\n/** `POST /v1/inventory/apply` → cursor (§16). */\nexport function parseApplyResult(body: unknown): bigint {\n const rec = asRecord(body, 'apply response');\n return parseCounter(rec.cursor, 'apply response .cursor');\n}\n\n/** `GET /v1/intents` → {@link IntentRecord}[] (§16). */\nexport function parseIntents(body: unknown): IntentRecord[] {\n const rec = asRecord(body, 'intents response');\n return asArray(rec.intents, 'intents response .intents').map((raw, i) => {\n const it = asRecord(raw, `intents[${i}]`);\n const status = asString(it.status, `intents[${i}].status`);\n if (status !== 'open' && status !== 'completed' && status !== 'aborted') {\n throw protocolError(`intents[${i}].status is not a known intent status`);\n }\n const createdAt = Date.parse(asString(it.createdAt, `intents[${i}].createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`intents[${i}].createdAt is not a timestamp`);\n return {\n transferId: asString(it.transferId, `intents[${i}].transferId`),\n payload: asString(it.payload, `intents[${i}].payload`),\n status,\n createdAt,\n };\n });\n}\n\n// ── mailbox (§6/§16) ──────────────────────────────────────────────────────────\n\n/** `POST /v1/mailbox` → entryId (§16). */\nexport function parseDepositResult(body: unknown): string {\n const rec = asRecord(body, 'mailbox deposit response');\n return asString(rec.entryId, 'mailbox deposit response .entryId');\n}\n\nfunction parseMailboxEntry(raw: unknown, what: string): MailboxEntry {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'unclaimed' && status !== 'claimed' && status !== 'rejected') {\n throw protocolError(`${what}.status is not a known mailbox entry status`);\n }\n const createdAt = Date.parse(asString(rec.createdAt, `${what}.createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`${what}.createdAt is not a timestamp`);\n return {\n entryId: asString(rec.entryId, `${what}.entryId`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n status,\n transferId: asString(rec.transferId, `${what}.transferId`),\n tokenId: asString(rec.tokenId, `${what}.tokenId`),\n assets: (parseAssets(rec.assets, `${what}.assets`) ?? []).map((a) => ({ ...a })),\n senderPubkey: asString(rec.senderPubkey, `${what}.senderPubkey`),\n ...(rec.memo !== undefined && rec.memo !== null\n ? { memo: asString(rec.memo, `${what}.memo`) }\n : {}),\n createdAt,\n ...(rec.getUrl !== undefined && rec.getUrl !== null\n ? { getUrl: asString(rec.getUrl, `${what}.getUrl`) }\n : {}),\n ...(rec.blobCollected === true ? { blobCollected: true } : {}),\n };\n}\n\n/** `GET /v1/mailbox?since=` → {@link MailboxPage} (§16). */\nexport function parseMailboxPage(body: unknown): MailboxPage {\n const rec = asRecord(body, 'mailbox response');\n if (typeof rec.more !== 'boolean') throw protocolError('mailbox response .more is not a boolean');\n return {\n readPointer: parseCounter(rec.readPointer, 'mailbox response .readPointer'),\n syncEpoch: parseCounter(rec.syncEpoch, 'mailbox response .syncEpoch'),\n more: rec.more,\n entries: asArray(rec.entries, 'mailbox response .entries').map((raw, i) =>\n parseMailboxEntry(raw, `entries[${i}]`)\n ),\n };\n}\n\n/** `POST /v1/mailbox/claim` → {@link MailboxClaimResult} (§16). */\nexport function parseClaimResult(body: unknown): MailboxClaimResult {\n const rec = asRecord(body, 'mailbox claim response');\n return {\n claimed: asArray(rec.claimed, 'claim response .claimed').map((raw, i) =>\n asString(raw, `claimed[${i}]`)\n ),\n alreadyClaimed: asArray(rec.alreadyClaimed, 'claim response .alreadyClaimed').map((raw, i) => {\n const a = asRecord(raw, `alreadyClaimed[${i}]`);\n if (typeof a.intoInventory !== 'boolean') {\n throw protocolError(`alreadyClaimed[${i}].intoInventory is not a boolean`);\n }\n return { entryId: asString(a.entryId, `alreadyClaimed[${i}].entryId`), intoInventory: a.intoInventory };\n }),\n failed: asArray(rec.failed, 'claim response .failed').map((raw, i) => {\n const f = asRecord(raw, `failed[${i}]`);\n return { entryId: asString(f.entryId, `failed[${i}].entryId`), code: asString(f.code, `failed[${i}].code`) };\n }),\n };\n}\n\n/** `POST /v1/mailbox/reject` → rejected entry ids (§16). */\nexport function parseRejectResult(body: unknown): string[] {\n const rec = asRecord(body, 'mailbox reject response');\n return asArray(rec.rejected, 'reject response .rejected').map((raw, i) => asString(raw, `rejected[${i}]`));\n}\n\n// ── payment requests (§10/§16) ────────────────────────────────────────────────\n\n/** One §16 payment request → {@link PaymentRequestRecord} (create/respond/list). */\nexport function parsePaymentRequestRecord(raw: unknown, what: string): PaymentRequestRecord {\n const rec = asRecord(raw, what);\n const status = asString(rec.status, `${what}.status`);\n if (status !== 'open' && status !== 'paid' && status !== 'declined' && status !== 'expired') {\n throw protocolError(`${what}.status is not a known payment-request status`);\n }\n const createdAt = Date.parse(asString(rec.createdAt, `${what}.createdAt`));\n if (Number.isNaN(createdAt)) throw protocolError(`${what}.createdAt is not a timestamp`);\n let expiresAt: number | undefined;\n if (rec.expiresAt !== undefined && rec.expiresAt !== null) {\n expiresAt = Date.parse(asString(rec.expiresAt, `${what}.expiresAt`));\n if (Number.isNaN(expiresAt)) throw protocolError(`${what}.expiresAt is not a timestamp`);\n }\n if (rec.transferId !== null && typeof rec.transferId !== 'string') {\n throw protocolError(`${what}.transferId is neither a string nor null`);\n }\n return {\n id: asString(rec.id, `${what}.id`),\n seq: parseCounter(rec.seq, `${what}.seq`),\n fromPubkey: asString(rec.fromPubkey, `${what}.fromPubkey`),\n toPubkey: asString(rec.toPubkey, `${what}.toPubkey`),\n assets: (parseAssets(rec.assets, `${what}.assets`) ?? []).map((a) => ({ ...a })),\n ...(rec.memo !== undefined && rec.memo !== null\n ? { memo: asString(rec.memo, `${what}.memo`) }\n : {}),\n status,\n transferId: rec.transferId,\n createdAt,\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n };\n}\n\n/**\n * `GET /v1/payment-requests?role=` → {@link PaymentRequestsPage} (§16). The\n * cursor family is role-bound: incoming pages carry the `?since=` seq counter,\n * outgoing pages an opaque keyset string (null once drained) — a page whose\n * cursor does not match its role is a protocol violation.\n */\nexport function parsePaymentRequestsPage(\n body: unknown,\n role: 'incoming' | 'outgoing'\n): PaymentRequestsPage {\n const rec = asRecord(body, 'payment-requests response');\n if (typeof rec.more !== 'boolean') throw protocolError('payment-requests response .more is not a boolean');\n const requests = asArray(rec.requests, 'payment-requests response .requests').map((raw, i) =>\n parsePaymentRequestRecord(raw, `requests[${i}]`)\n );\n const syncEpoch = parseCounter(rec.syncEpoch, 'payment-requests response .syncEpoch');\n if (role === 'incoming') {\n return {\n role,\n requests,\n more: rec.more,\n cursor: parseCounter(rec.cursor, 'payment-requests response .cursor'),\n syncEpoch,\n };\n }\n if (rec.cursor !== null && typeof rec.cursor !== 'string') {\n throw protocolError('payment-requests response .cursor is neither a keyset string nor null');\n }\n return { role, requests, more: rec.more, cursor: rec.cursor, syncEpoch };\n}\n\n// ── history (§10/§16) ─────────────────────────────────────────────────────────\n\nfunction parseHistoryRecord(raw: unknown, what: string): HistoryWireRecord {\n const rec = asRecord(raw, what);\n return {\n dedupKey: asString(rec.dedupKey, `${what}.dedupKey`),\n id: asString(rec.id, `${what}.id`),\n type: asString(rec.type, `${what}.type`),\n ts: asString(rec.ts, `${what}.ts`),\n assets: asArray(rec.assets, `${what}.assets`).map((a, i) => {\n const asset = asRecord(a, `${what}.assets[${i}]`);\n // Amounts stay decimal strings on this surface — display data (§10).\n parseAmount(asset.amount, `${what}.assets[${i}].amount`);\n return {\n coinId: asString(asset.coinId, `${what}.assets[${i}].coinId`),\n amount: asString(asset.amount, `${what}.assets[${i}].amount`),\n };\n }),\n ...(typeof rec.transferId === 'string' ? { transferId: rec.transferId } : {}),\n ...(typeof rec.tokenId === 'string' ? { tokenId: rec.tokenId } : {}),\n ...(typeof rec.counterpartyPubkey === 'string' ? { counterpartyPubkey: rec.counterpartyPubkey } : {}),\n ...(typeof rec.memo === 'string' ? { memo: rec.memo } : {}),\n ...(typeof rec.counterpartyNametag === 'string' ? { counterpartyNametag: rec.counterpartyNametag } : {}),\n };\n}\n\n/** `GET /v1/history` → {@link HistoryPage} (§16). */\nexport function parseHistoryPage(body: unknown): HistoryPage {\n const rec = asRecord(body, 'history response');\n if (typeof rec.more !== 'boolean') throw protocolError('history response .more is not a boolean');\n if (rec.cursor !== null && typeof rec.cursor !== 'string') {\n throw protocolError('history response .cursor is not a string or null');\n }\n return {\n records: asArray(rec.records, 'history response .records').map((raw, i) =>\n parseHistoryRecord(raw, `records[${i}]`)\n ),\n more: rec.more,\n cursor: rec.cursor,\n syncEpoch: parseCounter(rec.syncEpoch, 'history response .syncEpoch'),\n };\n}\n\n/** Auth responses (§4). */\nexport function parseAuthChallenge(body: unknown): { nonce: string; challenge: string } {\n const rec = asRecord(body, 'auth/challenge response');\n return {\n nonce: asString(rec.nonce, 'auth/challenge response .nonce'),\n challenge: asString(rec.challenge, 'auth/challenge response .challenge'),\n };\n}\n\nexport function parseAuthTokens(body: unknown): { jwt: string; refreshToken: string } {\n const rec = asRecord(body, 'auth token response');\n return {\n jwt: asString(rec.jwt, 'auth token response .jwt'),\n refreshToken: asString(rec.refreshToken, 'auth token response .refreshToken'),\n };\n}\n\n/** `POST /v1/ws-ticket` (§9). */\nexport function parseWsTicket(body: unknown): string {\n const rec = asRecord(body, 'ws-ticket response');\n return asString(rec.ticket, 'ws-ticket response .ticket');\n}\n\n/** WS wake frame (§9): `{ type:'wake', stream, syncEpoch }`. */\nexport function parseWakeFrame(raw: string): { stream: 'inventory' | 'mailbox' | 'payment_requests'; syncEpoch: bigint } | null {\n let body: unknown;\n try {\n body = JSON.parse(raw);\n } catch {\n return null;\n }\n if (typeof body !== 'object' || body === null) return null;\n const rec = body as Record<string, unknown>;\n if (rec.type !== 'wake') return null;\n const stream = rec.stream;\n if (stream !== 'inventory' && stream !== 'mailbox' && stream !== 'payment_requests') return null;\n try {\n return { stream, syncEpoch: parseCounter(rec.syncEpoch, 'wake .syncEpoch') };\n } catch {\n return null;\n }\n}\n","/**\n * wallet-api/client.ts — `WalletApiClient` (sdk-changes S1).\n *\n * A small typed client for the wallet-api backend: challenge→sign→JWT with a\n * rotating refresh token, typed REST for the §16 endpoints, and the WS wake\n * channel (ticket flow — §9). Injected into providers (DI; no singletons).\n *\n * Cross-cutting rules (S1):\n * - **Challenge template verification** — the spend key never signs text that\n * fails `verifyChallengeTemplate` (prefix + own pubkey + plausible\n * timestamps). See ./challenge.ts.\n * - **Credential hygiene** — the refresh token lives only in the injected\n * {@link KeyValueStore} (never a URL, never logged). A rotation-reuse\n * revocation or refresh expiry falls back to a fresh challenge→sign cycle —\n * silent, since the wallet key is available at unlock. Non-loopback base\n * URLs MUST be `https:` (ARCHITECTURE §4 transport rule), enforced at\n * construction.\n * - **Amounts are decimal strings end-to-end**, parsed with `BigInt` (§11) —\n * see ./codec.ts.\n *\n * The client also keeps the NORMATIVE LOCAL COPY of open intents (E.3): the\n * server is the primary, but intents are the one server table not\n * re-derivable from blobs — after a server restore (`syncEpoch` change,\n * ARCHITECTURE §5.4) the client re-PUTs its locally-known open intents\n * (idempotent) before anything resumes. `putIntent` persists locally before\n * the server PUT; the local copy survives a failed PUT as the restore\n * backstop.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\n\nimport { signMessage } from '../core/crypto';\nimport { WalletApiError } from './errors';\nimport { verifyChallengeTemplate } from './challenge';\nimport {\n parseApplyResult,\n parseAuthChallenge,\n parseAuthTokens,\n parseBalances,\n parseBlobUrls,\n parseClaimResult,\n parseDepositResult,\n parseHistoryPage,\n parseIntents,\n parseInventoryPage,\n parseMailboxPage,\n parsePaymentRequestRecord,\n parsePaymentRequestsPage,\n parseRejectResult,\n parseUploadUrls,\n parseWakeFrame,\n parseWsTicket,\n} from './codec';\nimport type {\n ApplyDeltaRequest,\n BlobUrlEntry,\n CoinBalance,\n CreatePaymentRequestInput,\n FetchLike,\n FetchResponseLike,\n HistoryPage,\n HistoryWireRecord,\n IntentRecord,\n InventoryPage,\n KeyValueStore,\n ListPaymentRequestsParams,\n MailboxClaimResult,\n MailboxDepositRequest,\n MailboxPage,\n PaymentRequestRecord,\n PaymentRequestsPage,\n RespondPaymentRequestInput,\n UploadUrlEntry,\n UploadUrlRequest,\n WakeCallback,\n WakeSocketHandle,\n WalletApiClientConfig,\n WalletApiIdentity,\n WebSocketFactoryLike,\n} from './types';\n\ninterface LocalIntent {\n payload: string;\n status: 'open' | 'completed' | 'aborted';\n createdAt: number;\n /**\n * #516: set when the abort decision could NOT land on the server (dead\n * backend at send-failure cleanup). {@link WalletApiClient.resyncOpenIntents}\n * replays it (PUT + abort) so the server row converges to aborted instead of\n * resume re-executing a send the user watched fail.\n */\n abortPending?: boolean;\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname.endsWith('.localhost') ||\n /^127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(hostname) ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\n/** Enforce the §4 transport rule: non-loopback MUST be https. */\nfunction assertSecureBaseUrl(baseUrl: string): URL {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new WalletApiError(`Invalid wallet-api base URL: ${baseUrl}`, 'CONFIG');\n }\n if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopbackHost(url.hostname))) {\n throw new WalletApiError(\n 'wallet-api base URL must be https (plain http is allowed only on loopback — ARCHITECTURE §4)',\n 'CONFIG'\n );\n }\n return url;\n}\n\n/** Platform-neutral bytes→base64 (browser btoa + Node ≥16 global btoa). */\nfunction bytesToBase64(bytes: Uint8Array): string {\n let bin = '';\n for (const b of bytes) bin += String.fromCharCode(b);\n return btoa(bin);\n}\n\nfunction defaultWebSocketFactory(url: string): import('./types').WebSocketLike {\n const Ctor = (globalThis as { WebSocket?: new (url: string) => import('./types').WebSocketLike }).WebSocket;\n if (!Ctor) {\n throw new WalletApiError(\n 'No global WebSocket available — inject webSocketFactory (e.g. the \"ws\" package on Node < 22)',\n 'CONFIG'\n );\n }\n return new Ctor(url);\n}\n\nexport class WalletApiClient {\n /** Network name (also the blob-key prefix `<network>/t/<sha256>` — §5.2). */\n readonly network: string;\n\n private readonly baseUrl: string;\n private readonly deviceId: string;\n private readonly storage: KeyValueStore;\n private readonly fetchFn: FetchLike;\n private readonly wsFactory: WebSocketFactoryLike;\n private readonly now: () => number;\n\n private identity: WalletApiIdentity | null = null;\n private jwt: string | null = null;\n /** Serializes concurrent re-auth attempts. */\n private authInFlight: Promise<void> | null = null;\n\n constructor(config: WalletApiClientConfig) {\n const url = assertSecureBaseUrl(config.baseUrl);\n this.baseUrl = url.toString().replace(/\\/$/, '');\n if (!config.network) throw new WalletApiError('network is required', 'CONFIG');\n this.network = config.network;\n this.deviceId = config.deviceId;\n this.storage = config.storage;\n if (!config.fetchFn && !(globalThis as { fetch?: FetchLike }).fetch) {\n throw new WalletApiError('No fetch available — inject fetchFn', 'CONFIG');\n }\n // Resolve the global lazily THROUGH globalThis so the implementation keeps\n // its original receiver (browser fetch throws \"Illegal invocation\" when\n // called detached).\n this.fetchFn =\n config.fetchFn ?? ((u, init) => (globalThis as unknown as { fetch: FetchLike }).fetch(u, init));\n this.wsFactory = config.webSocketFactory ?? defaultWebSocketFactory;\n this.now = config.now ?? (() => Date.now());\n }\n\n /** Bind the wallet identity this client authenticates as. Resets the session. */\n setIdentity(identity: WalletApiIdentity): void {\n this.identity = identity;\n this.jwt = null;\n }\n\n // ── storage keys (scoped per network + pubkey + device) ────────────────────\n\n private requireIdentity(): WalletApiIdentity {\n if (!this.identity) throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n return this.identity;\n }\n\n private scopedKey(kind: string): string {\n const { chainPubkey } = this.requireIdentity();\n return `wallet-api:${kind}:${this.network}:${chainPubkey}`;\n }\n\n private refreshTokenKey(): string {\n return `${this.scopedKey('refresh')}:${this.deviceId}`;\n }\n\n // ── auth (ARCHITECTURE §4) ──────────────────────────────────────────────────\n\n /**\n * Establish a session: try the stored refresh token first (rotating), fall\n * back to a fresh challenge→sign→verify cycle. Safe to call repeatedly.\n */\n async signIn(): Promise<void> {\n if (this.authInFlight) return this.authInFlight;\n this.authInFlight = this.signInInner().finally(() => {\n this.authInFlight = null;\n });\n return this.authInFlight;\n }\n\n private async signInInner(): Promise<void> {\n if (await this.tryRefresh()) return;\n await this.challengeSignIn();\n }\n\n /**\n * `POST /v1/auth/refresh` with the stored token; rotates on success. Any\n * 4xx (expired, revoked, rotation-reuse revocation) clears the stored token\n * and reports `false` — the caller falls back to a challenge cycle.\n */\n private async tryRefresh(): Promise<boolean> {\n const stored = await this.storage.get(this.refreshTokenKey());\n if (!stored) return false;\n const res = await this.rawFetch('POST', '/v1/auth/refresh', { refreshToken: stored });\n if (res.status === 200) {\n const tokens = parseAuthTokens(await this.readJson(res, 'auth/refresh'));\n this.jwt = tokens.jwt;\n await this.storage.set(this.refreshTokenKey(), tokens.refreshToken);\n return true;\n }\n // Stale/revoked/reused token — discard it; a fresh challenge cycle follows.\n await this.storage.remove(this.refreshTokenKey());\n return false;\n }\n\n /** The challenge→verify cycle (§4 steps 1–3) with template verification (S1). */\n private async challengeSignIn(): Promise<void> {\n const identity = this.requireIdentity();\n const challengeRes = await this.rawFetch('POST', '/v1/auth/challenge', {\n pubkey: identity.chainPubkey,\n });\n if (challengeRes.status !== 200) throw await this.toError(challengeRes, 'auth/challenge');\n const { nonce, challenge } = parseAuthChallenge(await this.readJson(challengeRes, 'auth/challenge'));\n\n // The spend key never signs unverified server-chosen text (S1).\n verifyChallengeTemplate(challenge, {\n pubkey: identity.chainPubkey,\n nonce,\n network: this.network,\n nowMs: this.now(),\n });\n\n const signature = signMessage(identity.privateKey, challenge);\n const verifyRes = await this.rawFetch('POST', '/v1/auth/verify', {\n nonce,\n signature,\n deviceId: this.deviceId,\n });\n if (verifyRes.status !== 200) throw await this.toError(verifyRes, 'auth/verify');\n const tokens = parseAuthTokens(await this.readJson(verifyRes, 'auth/verify'));\n this.jwt = tokens.jwt;\n await this.storage.set(this.refreshTokenKey(), tokens.refreshToken);\n }\n\n /** Revoke the session server-side and drop all local credentials. */\n async logout(): Promise<void> {\n if (this.jwt) {\n try {\n await this.rawFetch('POST', '/v1/auth/logout', {}, this.jwt);\n } finally {\n this.jwt = null;\n }\n }\n await this.storage.remove(this.refreshTokenKey());\n }\n\n // ── request core ────────────────────────────────────────────────────────────\n\n private async rawFetch(\n method: string,\n path: string,\n body?: unknown,\n jwt?: string\n ): Promise<FetchResponseLike> {\n const headers: Record<string, string> = {};\n if (body !== undefined) headers['content-type'] = 'application/json';\n if (jwt) headers['authorization'] = `Bearer ${jwt}`;\n try {\n return await this.fetchFn(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n } catch (err) {\n throw new WalletApiError(`wallet-api request failed: ${method} ${path}`, 'NETWORK', undefined, err);\n }\n }\n\n private async readJson(res: FetchResponseLike, what: string): Promise<unknown> {\n const text = await res.text();\n if (text === '') return null;\n try {\n return JSON.parse(text);\n } catch (err) {\n throw new WalletApiError(`${what}: response is not JSON`, 'PROTOCOL', res.status, err);\n }\n }\n\n private async toError(res: FetchResponseLike, what: string): Promise<WalletApiError> {\n let message = `${what} failed with status ${res.status}`;\n try {\n const body = (await this.readJson(res, what)) as { error?: { code?: string; message?: string } } | null;\n if (body?.error?.message) message = `${what}: ${body.error.code ?? ''} ${body.error.message}`.trim();\n } catch {\n // keep the generic message\n }\n return WalletApiError.fromStatus(res.status, message);\n }\n\n /**\n * Authenticated JSON request. A 401 triggers one silent re-auth\n * (refresh → challenge fallback) and one retry.\n */\n private async requestJson(method: string, path: string, body?: unknown): Promise<unknown> {\n if (!this.jwt) await this.signIn();\n let res = await this.rawFetch(method, path, body, this.jwt!);\n if (res.status === 401) {\n this.jwt = null;\n await this.signIn();\n res = await this.rawFetch(method, path, body, this.jwt!);\n }\n if (res.status === 204) return null;\n if (!res.ok) throw await this.toError(res, `${method} ${path}`);\n return this.readJson(res, `${method} ${path}`);\n }\n\n // ── inventory / blobs (§16) ─────────────────────────────────────────────────\n\n /** `GET /v1/inventory?since=` — one page; the caller loops while `more`. */\n async listInventory(since?: bigint): Promise<InventoryPage> {\n const query = since !== undefined ? `?since=${since.toString()}` : '';\n const page = parseInventoryPage(await this.requestJson('GET', `/v1/inventory${query}`));\n await this.noteSyncEpoch(page.syncEpoch);\n return page;\n }\n\n /** `GET /v1/balances` — active rows only. */\n async getBalances(): Promise<CoinBalance[]> {\n return parseBalances(await this.requestJson('GET', '/v1/balances'));\n }\n\n /** `POST /v1/tokens/blob-urls` — owner's active *or tombstoned* rows (§5.3 recovery). */\n async getBlobUrls(tokenIds: string[]): Promise<BlobUrlEntry[]> {\n return parseBlobUrls(await this.requestJson('POST', '/v1/tokens/blob-urls', { tokenIds }));\n }\n\n /** `POST /v1/tokens/upload-urls` — checksum/length-bound presigned PUTs (§5.2). */\n async getUploadUrls(blobs: UploadUrlRequest[]): Promise<UploadUrlEntry[]> {\n return parseUploadUrls(await this.requestJson('POST', '/v1/tokens/upload-urls', { blobs }));\n }\n\n /**\n * `POST /v1/inventory/apply` (§5.3). Also marks the local intent copy\n * completed — the server completes the intent in the same transaction (§16).\n */\n async applyInventoryDelta(req: ApplyDeltaRequest): Promise<bigint> {\n const cursor = parseApplyResult(\n await this.requestJson('POST', '/v1/inventory/apply', {\n transferId: req.transferId,\n spent: req.spent,\n added: req.added,\n ...(req.externalDelivery !== undefined ? { externalDelivery: req.externalDelivery } : {}),\n })\n );\n await this.setLocalIntentStatus(req.transferId, 'completed');\n return cursor;\n }\n\n /** Download blob bytes from a signed GET URL (the URL itself is the credential). */\n async fetchBlob(getUrl: string): Promise<Uint8Array> {\n let res: FetchResponseLike;\n try {\n res = await this.fetchFn(getUrl, { method: 'GET' });\n } catch (err) {\n throw new WalletApiError('blob download failed', 'NETWORK', undefined, err);\n }\n if (!res.ok) throw WalletApiError.fromStatus(res.status, `blob download failed with status ${res.status}`);\n return new Uint8Array(await res.arrayBuffer());\n }\n\n /**\n * Upload blob bytes to a signed PUT URL. A `412 Precondition Failed` means\n * the identical blob already exists (content addressing, `If-None-Match: *`\n * — §5.2) and is treated as success.\n *\n * The §5.2 presign binds `x-amz-checksum-sha256` and `if-none-match` (plus\n * `content-length`) as SIGNED HEADERS — a real S3 endpoint rejects the\n * SigV4 signature unless the uploader sends them verbatim. (Caught by the\n * phase-2 harness on first contact with real MinIO — the in-process fake\n * had only validated the body, not the signed headers.)\n */\n async uploadBlob(putUrl: string, bytes: Uint8Array): Promise<void> {\n let res: FetchResponseLike;\n try {\n res = await this.fetchFn(putUrl, {\n method: 'PUT',\n headers: {\n 'content-type': 'application/octet-stream',\n 'x-amz-checksum-sha256': bytesToBase64(sha256(bytes)),\n 'if-none-match': '*',\n },\n body: bytes,\n });\n } catch (err) {\n throw new WalletApiError('blob upload failed', 'NETWORK', undefined, err);\n }\n if (res.ok || res.status === 412) return;\n throw WalletApiError.fromStatus(res.status, `blob upload failed with status ${res.status}`);\n }\n\n // ── mailbox (§6/§16) ────────────────────────────────────────────────────────\n\n /**\n * `POST /v1/mailbox` — deposit an already-uploaded finished blob to the\n * recipient's mailbox. Idempotent by content-derived `entry_id` (§6): an\n * existing entry in any status returns `200` with its id, provided the\n * request's recipient and key match the stored entry (a mismatch is `409`).\n */\n async depositMailbox(req: MailboxDepositRequest): Promise<string> {\n return parseDepositResult(\n await this.requestJson('POST', '/v1/mailbox', {\n recipientPubkey: req.recipientPubkey,\n key: req.key,\n transferId: req.transferId,\n stateHash: req.stateHash,\n tokenId: req.tokenId,\n ...(req.memo !== undefined ? { memo: req.memo } : {}),\n })\n );\n }\n\n /**\n * `GET /v1/mailbox?since=<seq>` — entries of EVERY status are listable for\n * any client-chosen `since`; a claimed entry carries a working `getUrl`\n * while its blob is within retention, `blobCollected: true` afterwards (§6).\n */\n async listMailbox(since?: bigint): Promise<MailboxPage> {\n const query = since !== undefined ? `?since=${since.toString()}` : '';\n const page = parseMailboxPage(await this.requestJson('GET', `/v1/mailbox${query}`));\n await this.noteSyncEpoch(page.syncEpoch);\n return page;\n }\n\n /**\n * `POST /v1/mailbox/claim` — addressee-only, idempotent ownership handoff\n * (§6). `intoInventory:false` is the delivery-only claim: the entry resolves\n * and the pointer advances with ZERO inventory writes.\n */\n async claimMailbox(entryIds: string[], intoInventory: boolean): Promise<MailboxClaimResult> {\n return parseClaimResult(\n await this.requestJson('POST', '/v1/mailbox/claim', { entryIds, intoInventory })\n );\n }\n\n /**\n * `POST /v1/mailbox/reject` — addressee-only; terminal for DISCOVERY only:\n * the entry counts toward read-pointer contiguity but remains claimable and\n * its blob is retained (§6 — reject is never a destruction path).\n */\n async rejectMailbox(entryIds: string[]): Promise<string[]> {\n return parseRejectResult(await this.requestJson('POST', '/v1/mailbox/reject', { entryIds }));\n }\n\n // ── history (§10/§16) ───────────────────────────────────────────────────────\n\n /**\n * `POST /v1/history` — client-asserted records, deduped by `dedupKey` (the\n * server never writes history rows — §10). `memo`/`counterpartyNametag`\n * MUST already be S6 envelopes.\n */\n async postHistoryRecords(records: HistoryWireRecord[]): Promise<void> {\n await this.requestJson('POST', '/v1/history', { records });\n }\n\n /** `GET /v1/history?before=&limit=` — newest-first keyset pages (§10). */\n async listHistory(options: { before?: string; limit?: number } = {}): Promise<HistoryPage> {\n const params = new URLSearchParams();\n if (options.before !== undefined) params.set('before', options.before);\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n const qs = params.toString();\n return parseHistoryPage(await this.requestJson('GET', `/v1/history${qs ? `?${qs}` : ''}`));\n }\n\n // ── payment requests (§10/§16) ──────────────────────────────────────────────\n\n /**\n * `POST /v1/payment-requests` — create a request addressed to a payer (the\n * payer's account is auto-provisioned even if they never authenticated —\n * §4/§9; the per-payer open cap → 429, §5.5). `memo` MUST already be an S6\n * `enc1.` envelope (§8.3) — only the requester's wallet key decrypts it.\n */\n async createPaymentRequest(input: CreatePaymentRequestInput): Promise<PaymentRequestRecord> {\n return parsePaymentRequestRecord(\n await this.requestJson('POST', '/v1/payment-requests', {\n toPubkey: input.toPubkey,\n // Amounts are decimal strings in every JSON body (§11).\n assets: input.assets.map((a) => ({ coinId: a.coinId, amount: a.amount.toString() })),\n ...(input.memo !== undefined ? { memo: input.memo } : {}),\n ...(input.expiresAt !== undefined ? { expiresAt: new Date(input.expiresAt).toISOString() } : {}),\n }),\n 'payment-request create response'\n );\n }\n\n /**\n * `GET /v1/payment-requests?role=…` (§16): `'incoming'` is the payer's\n * gap-free `?since=<seq>` stream (cursor = bigint, the standard §16 since\n * contract); `'outgoing'` is the requester's newest-first\n * `?before=<opaque keyset>` backfill (cursor = string | null). The two\n * cursor families never mix — the server 422s a mismatched parameter, and\n * the parameter types make it unrepresentable here.\n */\n async listPaymentRequests<R extends 'incoming' | 'outgoing'>(\n params: Extract<ListPaymentRequestsParams, { role: R }>\n ): Promise<Extract<PaymentRequestsPage, { role: R }>> {\n // Widened to the plain union — generic Extract<> members don't narrow.\n const p: ListPaymentRequestsParams = params;\n const query = new URLSearchParams();\n query.set('role', p.role);\n if (p.status !== undefined) query.set('status', p.status);\n if (p.role === 'incoming') {\n if (p.since !== undefined) query.set('since', p.since.toString());\n } else if (p.before !== undefined) {\n query.set('before', p.before);\n }\n const page = parsePaymentRequestsPage(\n await this.requestJson('GET', `/v1/payment-requests?${query.toString()}`),\n p.role\n );\n await this.noteSyncEpoch(page.syncEpoch);\n // The codec already discriminated the page by role; Extract<> re-states\n // the role-bound cursor family to the caller.\n return page as Extract<PaymentRequestsPage, { role: R }>;\n }\n\n /**\n * `POST /v1/payment-requests/{id}/respond` — addressee-only (§10, non-\n * addressee → 403); only an `open` request may be responded to (else 409).\n * `paid` REQUIRES and links the fulfilling send's transferId, `declined`\n * carries none — the pairing is enforced by {@link RespondPaymentRequestInput}.\n */\n async respondPaymentRequest(id: string, response: RespondPaymentRequestInput): Promise<PaymentRequestRecord> {\n return parsePaymentRequestRecord(\n await this.requestJson('POST', `/v1/payment-requests/${id}/respond`, response),\n 'payment-request respond response'\n );\n }\n\n // ── intents (E.3) ───────────────────────────────────────────────────────────\n\n private intentsKey(): string {\n return this.scopedKey('intents');\n }\n\n private async readLocalIntents(): Promise<Record<string, LocalIntent>> {\n const raw = await this.storage.get(this.intentsKey());\n if (!raw) return {};\n try {\n return JSON.parse(raw) as Record<string, LocalIntent>;\n } catch {\n return {};\n }\n }\n\n private async writeLocalIntents(intents: Record<string, LocalIntent>): Promise<void> {\n await this.storage.set(this.intentsKey(), JSON.stringify(intents));\n }\n\n private async setLocalIntentStatus(\n transferId: string,\n status: LocalIntent['status'],\n opts: { abortPending?: boolean } = {}\n ): Promise<void> {\n const intents = await this.readLocalIntents();\n const intent = intents[transferId];\n if (!intent) return;\n if (intent.status === 'completed') return; // completed never reverts (§16)\n const abortPending = opts.abortPending === true;\n if (intent.status === status && (intent.abortPending === true) === abortPending) return;\n intent.status = status;\n if (abortPending) intent.abortPending = true;\n else delete intent.abortPending;\n await this.writeLocalIntents(intents);\n }\n\n /**\n * Persist a (client-encrypted — S6) intent payload: LOCAL copy first, then\n * `PUT /v1/intents/{transferId}` and await the server ack (E.3 — the engine\n * MUST NOT be called before this resolves). A failed server PUT throws, but\n * the local copy stays as the restore backstop and is re-PUT by\n * {@link resyncOpenIntents}.\n */\n async putIntent(transferId: string, payloadEnvelope: string): Promise<void> {\n const intents = await this.readLocalIntents();\n if (!intents[transferId]) {\n intents[transferId] = { payload: payloadEnvelope, status: 'open', createdAt: this.now() };\n await this.writeLocalIntents(intents);\n }\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: payloadEnvelope });\n }\n\n /** `GET /v1/intents?status=` — server-side intent list. */\n async listIntents(status: 'open' | 'aborted'): Promise<IntentRecord[]> {\n return parseIntents(await this.requestJson('GET', `/v1/intents?status=${status}`));\n }\n\n /**\n * `POST /v1/intents/{id}/abort` — soft, recoverable (§16).\n *\n * #516: the LOCAL copy flips to 'aborted' even when the server abort cannot\n * land (dead backend) — leaving it 'open' would make {@link resyncOpenIntents}\n * re-PUT it and the next sign-in's `resumeOpenIntents` RE-EXECUTE a send the\n * user watched fail (and possibly retried manually): a double-pay hazard.\n * An unlanded abort is marked pending and replayed by\n * {@link resyncOpenIntents} (PUT + abort) so the server converges too.\n */\n async abortIntent(transferId: string): Promise<void> {\n try {\n await this.requestJson('POST', `/v1/intents/${transferId}/abort`);\n } catch (err) {\n await this.setLocalIntentStatus(transferId, 'aborted', { abortPending: true });\n throw err;\n }\n await this.setLocalIntentStatus(transferId, 'aborted');\n }\n\n /** `POST /v1/intents/{id}/complete` — the uniform client-side close (E.3). */\n async completeIntent(transferId: string): Promise<void> {\n await this.requestJson('POST', `/v1/intents/${transferId}/complete`);\n await this.setLocalIntentStatus(transferId, 'completed');\n }\n\n /** The locally-known open intents (the E.3 restore backstop). */\n async listLocalOpenIntents(): Promise<IntentRecord[]> {\n const intents = await this.readLocalIntents();\n return Object.entries(intents)\n .filter(([, i]) => i.status === 'open')\n .map(([transferId, i]) => ({ transferId, payload: i.payload, status: 'open' as const, createdAt: i.createdAt }));\n }\n\n /**\n * Re-PUT every locally-known open intent (idempotent — the server PUT is\n * write-once while open/completed). Called after a `syncEpoch` change\n * (server restore — §5.4): intents are the one server table not\n * re-derivable from blobs.\n *\n * #516: also replays locally-aborted intents whose abort never landed on\n * the server (`abortPending`) — PUT (no-op while open/completed; (re)creates\n * the row after a restore or a failed original PUT) then abort, so the\n * server row lands as 'aborted' instead of an 'open' intent that resume\n * would re-execute. Completed-wins is preserved server-side (§16).\n */\n async resyncOpenIntents(): Promise<void> {\n const intents = await this.readLocalIntents();\n for (const [transferId, intent] of Object.entries(intents)) {\n if (intent.status === 'open') {\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: intent.payload });\n } else if (intent.status === 'aborted' && intent.abortPending === true) {\n await this.requestJson('PUT', `/v1/intents/${transferId}`, { payload: intent.payload });\n await this.requestJson('POST', `/v1/intents/${transferId}/abort`);\n await this.setLocalIntentStatus(transferId, 'aborted'); // clears abortPending\n }\n }\n }\n\n // ── syncEpoch (§5.4/§9) ─────────────────────────────────────────────────────\n\n private syncEpochKey(): string {\n return this.scopedKey('syncEpoch');\n }\n\n /**\n * Track the server `syncEpoch` carried by cursor-bearing responses and\n * wakes. On a change (server restore), re-PUT locally-known open intents\n * before anything resumes (E.3). Storage-provider cursor invalidation is\n * the provider's own duty (S2) — it observes the same epoch values.\n */\n private async noteSyncEpoch(epoch: bigint): Promise<void> {\n const stored = await this.storage.get(this.syncEpochKey());\n if (stored === epoch.toString()) return;\n await this.storage.set(this.syncEpochKey(), epoch.toString());\n if (stored !== null) {\n await this.resyncOpenIntents();\n }\n }\n\n // ── realtime (§9) ───────────────────────────────────────────────────────────\n\n /**\n * Open the wake channel: `POST /v1/ws-ticket` (JWT-authed, single-use,\n * short TTL) then `GET /v1/ws?ticket=…` — the JWT never appears in a URL.\n * Wakes are nudges only; correctness comes from the `?since=` cursors.\n */\n async connectWakeSocket(onWake: WakeCallback): Promise<WakeSocketHandle> {\n const ticket = parseWsTicket(await this.requestJson('POST', '/v1/ws-ticket'));\n const wsBase = this.baseUrl.replace(/^http/, 'ws');\n const ws = this.wsFactory(`${wsBase}/v1/ws?ticket=${encodeURIComponent(ticket)}`);\n\n await new Promise<void>((resolve, reject) => {\n ws.onopen = () => resolve();\n ws.onerror = (err) => reject(new WalletApiError('wake socket failed to connect', 'NETWORK', undefined, err));\n ws.onclose = () => reject(new WalletApiError('wake socket closed before connecting', 'NETWORK'));\n });\n\n ws.onerror = null;\n ws.onclose = null;\n ws.onmessage = (ev) => {\n const frame = parseWakeFrame(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (!frame) return;\n void this.noteSyncEpoch(frame.syncEpoch);\n onWake(frame);\n };\n\n return { close: () => ws.close() };\n }\n}\n","/**\n * WalletApiTokenStorageProvider — the lazy, thin-wallet storage provider over\n * the wallet-api backend (sdk-changes S2; ARCHITECTURE §5/§16).\n *\n * Platform-neutral (browser + Node) over the injected {@link WalletApiClient}.\n * The wallet renders balances from the server's value-indexed inventory view;\n * blobs are fetched on demand only to spend (signed GET URLs). Key behaviors:\n *\n * - **Tombstone-aware delta sync** (§5.1): `?since=` deltas include\n * `status:'removed'` rows — the only way a stale device learns about\n * spends/handoffs; the provider applies them (drops the entries from its\n * active view) and loops while `more`.\n * - **Paginated full pull** is finished with an immediate\n * `?since=<page-1 cursor>` closing delta, whose tombstones repair any flips\n * that happened between pages (§5.1).\n * - **`syncEpoch` change** (server restore — §5.4): discard all persisted\n * cursors, full pull, then re-PUT locally-known open intents (E.3, via the\n * client) before anything resumes.\n * - **Write-behind with empty-import protection** (§5.1 client guards): a\n * removal is pushed only after a successful inventory load and only for a\n * confirmed on-chain spend (a `_tombstones` entry) — a fresh device or a\n * failed load can never appear to \"empty\" the wallet, and a merely-absent\n * token is never removed.\n * - **`recoverRemoved()`** (§5.3 recovery): tombstones the client cannot match\n * to a known spend are re-fetched via blob-urls (which work for own\n * tombstoned rows), re-verified locally, and re-added (reactivation). A\n * server `409` is an evidenced tombstone — \"actually spent\" — and is kept.\n * - **Blob upload** via upload-urls with client-side sha256; a `412` from the\n * content-addressed store means the blob already exists = success (§5.2).\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport type {\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n InventoryItem,\n InventoryView,\n LoadResult,\n RecoverRemovedResult,\n SaveResult,\n SyncResult,\n TokenStorageProvider,\n TxfStorageDataBase,\n} from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport {\n TOKEN_BLOB_VERSION,\n decodeTokenBlob,\n unwrapTokenBlobBytes,\n} from '../../../token-engine/token-blob';\nimport type { FullIdentity, ProviderStatus } from '../../../types';\nimport { WalletApiClient, WalletApiError } from '../../../wallet-api';\nimport type { KeyValueStore } from '../../../wallet-api';\n\nexport interface WalletApiTokenStorageConfig {\n /** The authenticated wallet-api client (S1). DI — never a singleton. */\n client: WalletApiClient;\n /** Persists the inventory cursor, syncEpoch and own-spend set per identity. */\n stateStore: KeyValueStore;\n /**\n * Optional local token verification used by `recoverRemoved()` before\n * re-adding a tombstoned blob (S2: \"re-verifies locally\"). Wire the engine's\n * `verify` here at composition; the default accepts any blob that decodes\n * and matches its tokenId.\n */\n verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nfunction newTransferId(): string {\n return (globalThis as { crypto: { randomUUID(): string } }).crypto.randomUUID();\n}\n\n/** A stored v2 token entry (the UI token record with a hex CBOR blob). */\nfunction isV2BlobEntry(entry: unknown): entry is { sdkData: string } {\n if (typeof entry !== 'object' || entry === null || 'genesis' in entry) return false;\n const sdkData = (entry as { sdkData?: unknown }).sdkData;\n return (\n typeof sdkData === 'string' &&\n sdkData.length >= 2 &&\n sdkData.length % 2 === 0 &&\n /^[0-9a-f]+$/i.test(sdkData)\n );\n}\n\nexport class WalletApiTokenStorageProvider implements TokenStorageProvider<TxfStorageDataBase> {\n readonly id = 'wallet-api-token-storage';\n readonly name = 'Wallet API Token Storage';\n readonly type = 'cloud' as const;\n /** Custody lives in the wallet-api backend — composing this provider without\n * the wallet-api client is illegal (fail-closed, S7/#515). */\n readonly requiresWalletApi = true;\n\n private readonly client: WalletApiClient;\n private readonly stateStore: KeyValueStore;\n private readonly verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n\n private status: ProviderStatus = 'disconnected';\n private identity: FullIdentity | null = null;\n\n /** Local mirror of the inventory view — active rows and tombstones. */\n private readonly view = new Map<string, InventoryItem>();\n /**\n * Empty-import protection (§5.1): no removal is ever pushed before this\n * flips on the first successful inventory load. Doubles as the per-session\n * first-sync flag (#521): while false, `syncInventory` full-pulls even with\n * a warm persisted cursor — the view Map is process-lifetime, so a reloaded\n * instance (tab refresh) must rebuild it before deltas can resume.\n */\n private hadSuccessfulLoad = false;\n\n constructor(config: WalletApiTokenStorageConfig) {\n this.client = config.client;\n this.stateStore = config.stateStore;\n this.verifyToken = config.verifyToken;\n }\n\n // ── provider lifecycle ──────────────────────────────────────────────────────\n\n setIdentity(identity: FullIdentity): void {\n this.identity = identity;\n this.client.setIdentity({ privateKey: identity.privateKey, chainPubkey: identity.chainPubkey });\n this.view.clear();\n this.hadSuccessfulLoad = false;\n }\n\n async initialize(): Promise<boolean> {\n this.status = 'connected';\n return true;\n }\n\n async shutdown(): Promise<void> {\n this.status = 'disconnected';\n }\n\n async connect(): Promise<void> {\n await this.initialize();\n }\n\n async disconnect(): Promise<void> {\n this.status = 'disconnected';\n }\n\n isConnected(): boolean {\n return this.status === 'connected';\n }\n\n getStatus(): ProviderStatus {\n return this.status;\n }\n\n // ── persisted sync state (per network + identity) ───────────────────────────\n\n private stateKey(kind: string): string {\n if (!this.identity) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n return `wallet-api-storage:${kind}:${this.client.network}:${this.identity.chainPubkey}`;\n }\n\n private async readCursor(): Promise<bigint | null> {\n const raw = await this.stateStore.get(this.stateKey('cursor'));\n return raw !== null && /^[0-9]+$/.test(raw) ? BigInt(raw) : null;\n }\n\n private async readSyncEpoch(): Promise<bigint | null> {\n const raw = await this.stateStore.get(this.stateKey('syncEpoch'));\n return raw !== null && /^[0-9]+$/.test(raw) ? BigInt(raw) : null;\n }\n\n private async persistSyncState(cursor: bigint, syncEpoch: bigint): Promise<void> {\n await this.stateStore.set(this.stateKey('cursor'), cursor.toString());\n await this.stateStore.set(this.stateKey('syncEpoch'), syncEpoch.toString());\n }\n\n /** TokenIds this provider itself spent — `recoverRemoved()` skips these. */\n private async readKnownSpends(): Promise<Set<string>> {\n const raw = await this.stateStore.get(this.stateKey('knownSpends'));\n if (!raw) return new Set();\n try {\n return new Set(JSON.parse(raw) as string[]);\n } catch {\n return new Set();\n }\n }\n\n private async addKnownSpends(tokenIds: string[]): Promise<void> {\n if (tokenIds.length === 0) return;\n const known = await this.readKnownSpends();\n for (const id of tokenIds) known.add(id);\n await this.stateStore.set(this.stateKey('knownSpends'), JSON.stringify([...known]));\n }\n\n // ── inventory sync (§5.1) ───────────────────────────────────────────────────\n\n private applyItems(items: InventoryItem[]): void {\n for (const item of items) {\n // Tombstones drop the entry from the active view but stay recorded —\n // recoverRemoved() needs them (§5.3 recovery).\n this.view.set(item.tokenId, item);\n }\n }\n\n /** Full pull + the §5.1 closing delta; replaces the whole local view. */\n private async fullPull(): Promise<void> {\n this.view.clear();\n let page = await this.client.listInventory(undefined);\n const firstPageCursor = page.cursor;\n this.applyItems(page.items);\n while (page.more) {\n page = await this.client.listInventory(page.cursor);\n this.applyItems(page.items);\n }\n // Closing delta from the page-1 cursor: its tombstones repair any flips\n // that happened between pages (§5.1 — a paginated full pull spans snapshots).\n let delta = await this.client.listInventory(firstPageCursor);\n this.applyItems(delta.items);\n while (delta.more) {\n delta = await this.client.listInventory(delta.cursor);\n this.applyItems(delta.items);\n }\n await this.persistSyncState(delta.cursor, delta.syncEpoch);\n }\n\n /**\n * Delta loop from the persisted cursor. Returns `true` when the server's\n * `syncEpoch` no longer matches the persisted one — the cursors are invalid\n * (server restore, §5.4) and the caller must resync from scratch.\n */\n private async deltaLoop(since: bigint): Promise<boolean> {\n const knownEpoch = await this.readSyncEpoch();\n let page = await this.client.listInventory(since);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) return true;\n this.applyItems(page.items);\n while (page.more) {\n page = await this.client.listInventory(page.cursor);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) return true;\n this.applyItems(page.items);\n }\n await this.persistSyncState(page.cursor, page.syncEpoch);\n return false;\n }\n\n /** §5.4: discard cursors → full pull → re-PUT locally-known open intents. */\n private async handleSyncEpochChange(): Promise<void> {\n await this.stateStore.remove(this.stateKey('cursor'));\n await this.stateStore.remove(this.stateKey('syncEpoch'));\n await this.fullPull();\n await this.client.resyncOpenIntents();\n }\n\n /** Converge the local view with the server (delta when possible). */\n private async syncInventory(): Promise<void> {\n const cursor = await this.readCursor();\n // #521: the cursor is durable but the view Map is not — a delta from a\n // warm cursor into an empty view renders an empty wallet after a reload.\n // The FIRST sync of each instance/identity session (the flag setIdentity\n // resets) is therefore always a full pull; deltas resume afterwards.\n if (cursor === null || !this.hadSuccessfulLoad) {\n await this.fullPull();\n } else if (await this.deltaLoop(cursor)) {\n await this.handleSyncEpochChange();\n }\n this.hadSuccessfulLoad = true;\n }\n\n // ── lazy inventory port (S2) ────────────────────────────────────────────────\n\n /**\n * Without `since`: converge with the server, then return the full active\n * view (`more:false`) — a fresh device renders balances with zero blob\n * downloads. With `since`: a true server delta page (tombstones included),\n * also applied to the local view.\n */\n async listInventory(since?: bigint): Promise<InventoryView> {\n if (since === undefined) {\n await this.syncInventory();\n return this.snapshotView();\n }\n const knownEpoch = await this.readSyncEpoch();\n const page = await this.client.listInventory(since);\n if (knownEpoch !== null && page.syncEpoch !== knownEpoch) {\n // The caller's cursor predates a server restore — resync and hand back\n // the converged snapshot instead of a meaningless delta (§5.4).\n await this.handleSyncEpochChange();\n this.hadSuccessfulLoad = true;\n return this.snapshotView();\n }\n this.applyItems(page.items);\n if (!page.more) {\n const stored = await this.readCursor();\n if (stored === null || page.cursor > stored) {\n await this.persistSyncState(page.cursor, page.syncEpoch);\n }\n }\n return page;\n }\n\n private async snapshotView(): Promise<InventoryView> {\n const items = [...this.view.values()].filter((i) => i.status === 'active');\n const cursor = (await this.readCursor()) ?? 0n;\n const syncEpoch = (await this.readSyncEpoch()) ?? 0n;\n return { cursor, syncEpoch, more: false, items };\n }\n\n /** Fetch + decode one blob on demand via a short-lived signed GET (§5.1). */\n async getToken(tokenId: string): Promise<TokenBlob> {\n const urls = await this.client.getBlobUrls([tokenId]);\n const entry = urls.find((u) => u.tokenId === tokenId);\n if (!entry) {\n throw new WalletApiError(`No blob URL for token ${tokenId} (not owned here)`, 'NOT_FOUND');\n }\n const bytes = await this.client.fetchBlob(entry.getUrl);\n const blob = this.wrapWireBlob(tokenId, bytes);\n if (blob.tokenId !== tokenId) {\n throw new WalletApiError(\n `Blob tokenId mismatch: requested ${tokenId}, blob carries ${blob.tokenId}`,\n 'PROTOCOL'\n );\n }\n return blob;\n }\n\n /**\n * The §16 wire serves RAW token bytes (§5.2/§8.2 — the sphere 39051\n * envelope never crosses the API): re-wrap them for the engine-facing\n * {@link TokenBlob} surface. Envelope bytes (older rows, fake-world seeds)\n * still decode — their embedded tokenId is then checked by the caller. The\n * `network` of a raw wrap is not recoverable without the engine; consumers\n * derive it from the decoded token (`engine.decodeToken` re-wraps), so it\n * is never read from this value.\n */\n private wrapWireBlob(tokenId: string, bytes: Uint8Array): TokenBlob {\n try {\n return decodeTokenBlob(bytes);\n } catch {\n return { v: TOKEN_BLOB_VERSION, network: 0, tokenId, token: bytes };\n }\n }\n\n /**\n * Record a spend (§5.3) — idempotent by `transferId` server-side. MUST be\n * called after the mailbox deposit of the same `transferId` (the backend\n * evidence-checks removals against it). Refreshes the local view from the\n * resulting delta.\n */\n async applyDelta(\n transferId: string,\n spent: string[],\n added: ApplyDeltaAdded[],\n opts?: ApplyDeltaOptions\n ): Promise<void> {\n await this.client.applyInventoryDelta({\n transferId,\n spent,\n added,\n ...(opts?.externalDelivery !== undefined ? { externalDelivery: opts.externalDelivery } : {}),\n });\n await this.addKnownSpends(spent);\n await this.syncInventory();\n }\n\n // ── recovery (§5.3, sdk-changes S2) ─────────────────────────────────────────\n\n /**\n * Re-add tombstoned tokens this client cannot match to a known spend (a\n * wiped view — stolen JWT or a buggy client). Blobs are retained server-side\n * and blob-urls works for own tombstoned rows; each candidate is re-fetched,\n * locally verified, and re-added (reactivation). A `409` is the server's\n * verdict that the tombstone is an evidenced spend — kept as spent.\n */\n async recoverRemoved(): Promise<RecoverRemovedResult> {\n await this.syncInventory();\n const knownSpends = await this.readKnownSpends();\n const result: RecoverRemovedResult = { recovered: [], spent: [], skipped: [] };\n\n const candidates = [...this.view.values()].filter(\n (i) => i.status === 'removed' && !knownSpends.has(i.tokenId)\n );\n for (const item of candidates) {\n const blobBytes = await this.fetchRemovedBlob(item.tokenId);\n if (blobBytes === null || !(await this.locallyValid(item.tokenId, blobBytes))) {\n result.skipped.push(item.tokenId);\n continue;\n }\n // Content-addressed key of the exact bytes we verified (§5.2).\n const key = `${this.client.network}/t/${bytesToHex(sha256(blobBytes))}`;\n try {\n await this.client.applyInventoryDelta({\n transferId: newTransferId(),\n spent: [],\n added: [{ tokenId: item.tokenId, key }],\n });\n result.recovered.push(item.tokenId);\n } catch (err) {\n if (err instanceof WalletApiError && err.code === 'CONFLICT') {\n // Evidenced tombstone (or the token moved on) — actually spent (§5.3).\n result.spent.push(item.tokenId);\n await this.addKnownSpends([item.tokenId]);\n } else {\n throw err;\n }\n }\n }\n\n await this.syncInventory();\n return result;\n }\n\n private async fetchRemovedBlob(tokenId: string): Promise<Uint8Array | null> {\n const urls = await this.client.getBlobUrls([tokenId]);\n const entry = urls.find((u) => u.tokenId === tokenId);\n if (!entry) return null;\n try {\n return await this.client.fetchBlob(entry.getUrl);\n } catch {\n return null;\n }\n }\n\n private async locallyValid(tokenId: string, bytes: Uint8Array): Promise<boolean> {\n const blob = this.wrapWireBlob(tokenId, bytes);\n if (blob.tokenId !== tokenId) return false;\n if (this.verifyToken) return this.verifyToken(blob);\n return true;\n }\n\n // ── whole-blob surface (thin: the hot path is the lazy port above) ──────────\n\n /**\n * Thin view: converges with the server and reports tombstones, but carries\n * NO token entries — blobs are fetched on demand via `getToken()`\n * (sdk-changes S2: `load()` no longer eagerly pulls every blob).\n */\n async load(): Promise<LoadResult<TxfStorageDataBase>> {\n try {\n await this.syncInventory();\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'inventory sync failed',\n source: 'remote',\n timestamp: Date.now(),\n };\n }\n const data: TxfStorageDataBase = {\n _meta: {\n version: 1,\n address: this.identity?.l1Address ?? '',\n formatVersion: '2.0',\n updatedAt: Date.now(),\n },\n _tombstones: [...this.view.values()]\n .filter((i) => i.status === 'removed')\n .map((i) => ({ tokenId: i.tokenId, stateHash: '', timestamp: Date.now() })),\n };\n return { success: true, data, source: 'remote', timestamp: Date.now() };\n }\n\n /**\n * Write-behind push of a whole-blob snapshot:\n * - unknown tokens are uploaded (content-addressed, 412 = already present)\n * and added via one idempotent apply;\n * - removals are pushed ONLY for confirmed spends (`_tombstones` entries)\n * and ONLY after a successful inventory load (empty-import protection,\n * §5.1) — a merely-absent token is never removed.\n * A failed sync fails the save: pushing against an unknown server view\n * could only do harm.\n */\n async save(data: TxfStorageDataBase): Promise<SaveResult> {\n try {\n await this.syncInventory();\n const tombstoneIds = new Set((data._tombstones ?? []).map((t) => t.tokenId));\n await this.pushAdditions(data, tombstoneIds);\n await this.pushRemovals(tombstoneIds);\n await this.syncInventory();\n return { success: true, timestamp: Date.now() };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'wallet-api save failed',\n timestamp: Date.now(),\n };\n }\n }\n\n private async pushAdditions(data: TxfStorageDataBase, excluded: Set<string>): Promise<void> {\n const toAdd: { tokenId: string; bytes: Uint8Array }[] = [];\n for (const [key, value] of Object.entries(data)) {\n if (!/^_[0-9a-f]{4,}$/i.test(key) || !isV2BlobEntry(value)) continue;\n const tokenId = key.slice(1);\n // Skip tokens the server already knows: an equal-state re-push of an\n // active row is a lineage 409 (§5.3), and tombstoned tokens re-enter\n // via recoverRemoved(), never via write-behind.\n if (excluded.has(tokenId) || this.view.has(tokenId)) continue;\n // §5.2/§8.2: the wire carries the RAW token bytes — the stored sdkData\n // is the sphere envelope; unwrap it at the wallet-api boundary.\n toAdd.push({ tokenId, bytes: unwrapTokenBlobBytes(hexToBytes(value.sdkData)) });\n }\n if (toAdd.length === 0) return;\n\n const urls = await this.client.getUploadUrls(\n toAdd.map((t) => ({ sha256: bytesToHex(sha256(t.bytes)), size: t.bytes.length }))\n );\n const added: ApplyDeltaAdded[] = [];\n for (const token of toAdd) {\n const sha = bytesToHex(sha256(token.bytes));\n const url = urls.find((u) => u.sha256 === sha);\n if (!url) {\n throw new WalletApiError(`upload-urls response missing sha256 ${sha}`, 'PROTOCOL');\n }\n await this.client.uploadBlob(url.putUrl, token.bytes); // 412 = already present = success (§5.2)\n added.push({ tokenId: token.tokenId, key: url.key });\n }\n await this.client.applyInventoryDelta({ transferId: newTransferId(), spent: [], added });\n }\n\n private async pushRemovals(tombstoneIds: Set<string>): Promise<void> {\n // Empty-import protection (§5.1): never push a removal before the first\n // successful inventory load; a spent token is removed only against a\n // confirmed on-chain spend (a tombstone), never mere absence.\n if (!this.hadSuccessfulLoad || tombstoneIds.size === 0) return;\n const spent = [...tombstoneIds].filter((id) => this.view.get(id)?.status === 'active');\n if (spent.length === 0) return;\n await this.client.applyInventoryDelta({ transferId: newTransferId(), spent, added: [] });\n await this.addKnownSpends(spent);\n }\n\n async sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>> {\n const result = await this.save(localData);\n return {\n success: result.success,\n merged: localData,\n added: 0,\n removed: 0,\n conflicts: 0,\n error: result.error,\n };\n }\n}\n\nexport function createWalletApiTokenStorageProvider(\n config: WalletApiTokenStorageConfig\n): WalletApiTokenStorageProvider {\n return new WalletApiTokenStorageProvider(config);\n}\n","/**\n * transport/delivery-provider.ts — the `DeliveryProvider` port (sdk-changes S7,\n * covenant §3.1-6).\n *\n * The seam that keeps the delivery rail swappable. In Unicity, a transfer —\n * after certification — is just a file handoff, so the port is deliberately\n * tiny: hand a finished token blob to a recipient, pull incoming deliveries,\n * acknowledge them. `WalletApiMailboxProvider`\n * (impl/shared/wallet-api/WalletApiMailboxProvider.ts) is the reference\n * implementation; anything that can move a file can implement it (the port\n * shape must not preclude the old Nostr transport or a future federated\n * transport — neither is a deliverable here).\n *\n * Normative shapes (sdk-changes S7):\n * - `DeliveryReceipt = { deliveryId }`\n * - `IncomingDelivery = { deliveryId, transferId?, senderPubkey?, memo?,\n * fetchBlob(), cursor }`\n * - `deliveryId` is the **content-derived** entry id —\n * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — NEVER a server-assigned\n * row id or seq (covenant §3.1-4; the contract suite asserts it). It is\n * computed client-side ({@link computeDeliveryId}) and must equal the\n * backend's `entry_id` (ARCHITECTURE §6).\n * - **Custody is a composition-time property, not a per-call flag**:\n * implementations take `custody: 'inventory' | 'external'` at construction\n * and every ack sends the corresponding `intoInventory` — delivery-only\n * safety must never depend on remembering an option at a call site.\n * - Implementations MUST keep a **persistent `(tokenId, stateHash)` seen-set**\n * for incoming deliveries: the recipient-side replay guard is part of the\n * port contract, not a server promise (the recipient never trusts the\n * backend — ARCHITECTURE §8.2). `deliveryId` is the canonical hash encoding\n * of exactly that pair, so a persistent deliveryId set satisfies this.\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\n\n\n// =============================================================================\n// Normative shapes (sdk-changes S7)\n// =============================================================================\n\n/** Receipt for a delivered blob. `deliveryId` is content-derived — see module doc. */\nexport interface DeliveryReceipt {\n deliveryId: string;\n}\n\n/** Options for {@link DeliveryProvider.deliver}. */\nexport interface DeliverOptions {\n /**\n * The send's transferId (the E.3 intent id / realization seed). Recorded\n * with the delivery so the recipient can group multi-token payments and the\n * backend can evidence-check the sender's removals (ARCHITECTURE §5.3/§6).\n */\n transferId: string;\n /** Optional human memo. Implementations encrypt it client-side (S6). */\n memo?: string;\n}\n\n/** One incoming delivery pulled from the feed. */\nexport interface IncomingDelivery {\n /** Content-derived id — `hex(SHA-256(tokenId bytes ‖ stateHash bytes))`. */\n deliveryId: string;\n /** The sender's transferId, when the transport carries it. */\n transferId?: string;\n /** The sender's pubkey, when the transport carries it. */\n senderPubkey?: string;\n /** Decrypted memo (S6), when present and decryptable. */\n memo?: string;\n /** Fetch the finished token blob bytes (the encoded TokenBlob). */\n fetchBlob(): Promise<Uint8Array>;\n /** Transport-local resume cursor (opaque to callers). */\n cursor: string;\n}\n\nexport type DeliveryDisposition = 'claimed' | 'rejected';\n\n/**\n * Custody mode (composition-time): `'inventory'` — acknowledged deliveries\n * enter the wallet-api inventory (the full wallet-api preset); `'external'` —\n * the app's own storage keeps custody and acks perform ZERO inventory writes\n * (the delivery-only preset, ARCHITECTURE §6 \"delivery-only claim\").\n */\nexport type DeliveryCustody = 'inventory' | 'external';\n\n// =============================================================================\n// The port\n// =============================================================================\n\nexport interface DeliveryProvider {\n /** Composition-time custody property — never a per-call flag (S7). */\n readonly custody: DeliveryCustody;\n\n /**\n * Bind the wallet identity (optional — implementations that authenticate or\n * encrypt per-wallet need it; mirrors `TokenStorageProvider.setIdentity`).\n */\n setIdentity?(identity: { privateKey: string; chainPubkey: string }): void;\n\n /**\n * Hand a finished token blob to a recipient. `recipientPubkey` is the\n * recipient's CHAIN pubkey (33-byte compressed secp256k1, hex) — the\n * canonical Unicity identity (ARCHITECTURE §4); transports that address\n * recipients differently resolve it themselves.\n *\n * MUST be idempotent per (token, state): re-delivering the same finished\n * blob — including after the recipient claimed — succeeds and returns the\n * same content-derived `deliveryId` (ARCHITECTURE §6 deposit idempotency).\n */\n deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt>;\n\n /**\n * Pull-based feed of incoming deliveries since the given transport-local\n * cursor (or the provider's persisted cursor when omitted). Yields only\n * deliveries not yet in the persistent seen-set; completes when the feed is\n * drained — callers re-invoke on poll/wake. Feeds the existing\n * transport-agnostic `handleV2Transfer` (sdk-changes S3).\n */\n incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery>;\n\n /**\n * Acknowledge a delivery: `'claimed'` accepts it (with the provider's\n * composition-time custody), `'rejected'` marks it locally-unverifiable —\n * terminal for discovery only (the entry stays claimable server-side and\n * its blob is retained — ARCHITECTURE §6). Both record the delivery in the\n * persistent seen-set.\n */\n ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void>;\n\n /**\n * Optional wake hook: `callback` fires when new deliveries may be available\n * (e.g. a WS nudge — never a correctness dependency, ARCHITECTURE §9).\n * Returns an unsubscribe function.\n */\n onWake?(callback: () => void): () => void;\n\n /**\n * Late-bind the backend-true (tokenId, stateHash) derivation —\n * `ITokenEngine.deliveryKeys`. Compositions are engine-less (the engine is\n * built later); the module that owns both (PaymentsModule) binds this at\n * init. Implementations that derive ids (S7) MUST use it and fail loudly if\n * unbound; transports that don't derive may omit the method.\n */\n bindDeliveryKeys?(derive: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>): void;\n}\n\n// =============================================================================\n// deliveryId derivation (the backend's entry_id formula — ARCHITECTURE §6)\n// =============================================================================\n\nfunction hexToBytes(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * The content-derived delivery id:\n * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — identical to the\n * backend's `entry_id` (ARCHITECTURE §6), computed client-side so no\n * implementation can substitute a server row id (covenant §3.1-4).\n *\n * @param tokenIdHex - genesis-stable 64-hex token id\n * @param stateHashHex - the SDK's per-state hash (DataHash imprint, hex) — the\n * backend's `state_hash` (wallet-api validation/chain.ts); NEVER a plain\n * sha256 over the token bytes (that variant 422s on deposit).\n */\nexport function computeDeliveryId(tokenIdHex: string, stateHashHex: string): string {\n const tokenId = hexToBytes(tokenIdHex);\n const stateHash = hexToBytes(stateHashHex);\n const joined = new Uint8Array(tokenId.length + stateHash.length);\n joined.set(tokenId, 0);\n joined.set(stateHash, tokenId.length);\n return bytesToHex(sha256(joined));\n}\n\n/** Full delivery keys: the engine-derived pair + the composed deliveryId. */\nexport interface DeliveryBlobKeys {\n /** Genesis-stable 64-hex token id (from the TokenBlob envelope). */\n tokenId: string;\n /** Per-state hash: `hex(SHA-256(inner token bytes))` — changes every transfer. */\n stateHash: string;\n /** {@link computeDeliveryId} of the two above. */\n deliveryId: string;\n}\n\n/**\n * Derive (tokenId, stateHash, deliveryId) from encoded TokenBlob bytes.\n * Throws when the bytes are not a decodable TokenBlob.\n */\n/**\n * Compose full delivery keys from an engine-derived (tokenId, stateHash) pair.\n * The pair MUST come from `ITokenEngine.deliveryKeys` (the backend-true SDK\n * derivation) — the port module deliberately cannot decode tokens itself.\n */\nexport function composeDeliveryKeys(keys: { tokenId: string; stateHash: string }): DeliveryBlobKeys {\n return { ...keys, deliveryId: computeDeliveryId(keys.tokenId, keys.stateHash) };\n}\n","/**\n * HMAC: RFC2104 message authentication code.\n * @module\n */\nimport {\n abytes,\n aexists,\n ahash,\n aoutput,\n clean,\n type CHash,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Internal class for HMAC.\n * Accepts any byte key, although RFC 2104 §3 recommends keys at least\n * `HashLen` bytes long.\n */\nexport class _HMAC<T extends Hash<T>> implements Hash<_HMAC<T>> {\n oHash: T;\n iHash: T;\n blockLen: number;\n outputLen: number;\n canXOF = false;\n private finished = false;\n private destroyed = false;\n\n constructor(hash: TArg<CHash>, key: TArg<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 the first block) of the outer hash here,\n // 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: TArg<Uint8Array>): this {\n aexists(this);\n this.iHash.update(buf);\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n const buf = out.subarray(0, this.outputLen);\n // Reuse the first outputLen bytes for the inner digest; the outer hash consumes them before\n // overwriting that same prefix with the final tag, leaving any oversized tail untouched.\n this.iHash.digestInto(buf);\n this.oHash.update(buf);\n this.oHash.digestInto(buf);\n this.destroy();\n }\n digest(): TRet<Uint8Array> {\n const out = new Uint8Array(this.oHash.outputLen);\n this.digestInto(out);\n return out as TRet<Uint8Array>;\n }\n _cloneInto(to?: _HMAC<T>): _HMAC<T> {\n // Create new instance without calling constructor since the key\n // is 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 - authentication key bytes\n * @param message - message bytes to authenticate\n * @returns Authentication tag bytes.\n * @example\n * Compute an RFC 2104 HMAC.\n * ```ts\n * import { hmac } from '@noble/hashes/hmac.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const mac = hmac(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]));\n * ```\n */\ntype HmacFn = {\n (hash: TArg<CHash>, key: TArg<Uint8Array>, message: TArg<Uint8Array>): TRet<Uint8Array>;\n create(hash: TArg<CHash>, key: TArg<Uint8Array>): TRet<_HMAC<any>>;\n};\nexport const hmac: TRet<HmacFn> = /* @__PURE__ */ (() => {\n const hmac_ = ((\n hash: TArg<CHash>,\n key: TArg<Uint8Array>,\n message: TArg<Uint8Array>\n ): TRet<Uint8Array> => new _HMAC<any>(hash, key).update(message).digest()) as TRet<HmacFn>;\n hmac_.create = (hash: TArg<CHash>, key: TArg<Uint8Array>): TRet<_HMAC<any>> =>\n new _HMAC<any>(hash, key) as TRet<_HMAC<any>>;\n return hmac_;\n})();\n","/**\n * HKDF (RFC 5869): extract + expand in one step.\n * See {@link https://soatok.blog/2021/11/17/understanding-hkdf/}.\n * @module\n */\nimport { hmac } from './hmac.ts';\nimport { abytes, ahash, anumber, type CHash, clean, type TArg, type TRet } 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 * Local validation only checks `hash`; `ikm` / `salt` byte validation is delegated to `hmac()`.\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 * @returns Pseudorandom key derived from input keying material.\n * @example\n * Run the HKDF extract step.\n * ```ts\n * import { extract } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * extract(sha256, new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]));\n * ```\n */\nexport function extract(\n hash: TArg<CHash>,\n ikm: TArg<Uint8Array>,\n salt?: TArg<Uint8Array>\n): TRet<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\n// Shared mutable scratch byte for the RFC 5869 block counter `N`.\n// Safe to reuse because `expand()` is synchronous and resets it with `clean(...)` before returning.\nconst HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);\n// Shared RFC 5869 empty string for both `info === undefined` and the first-block `T(0)` input.\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\n * (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 * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM.\n * @returns Output keying material with the requested length.\n * @throws If the requested output length exceeds the HKDF limit\n * for the selected hash. {@link Error}\n * @example\n * Run the HKDF expand step.\n * ```ts\n * import { expand } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * expand(sha256, new Uint8Array(32), new Uint8Array([1, 2, 3]), 16);\n * ```\n */\nexport function expand(\n hash: TArg<CHash>,\n prk: TArg<Uint8Array>,\n info?: TArg<Uint8Array>,\n length: number = 32\n): TRet<Uint8Array> {\n ahash(hash);\n anumber(length, 'length');\n abytes(prk, undefined, 'prk');\n const olen = hash.outputLen;\n // RFC 5869 §2.3: PRK is \"a pseudorandom key of at least HashLen octets\".\n if (prk.length < olen) throw new Error('\"prk\" must be at least HashLen octets');\n // RFC 5869 §2.3 only bounds `L` by `<= 255*HashLen`; `L=0` is valid and yields empty OKM.\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) as TRet<Uint8Array>;\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 bytes\n * @param length - length of output keying material in bytes.\n * RFC 5869 §2.3 allows `0..255*HashLen`, so `0` returns an empty OKM.\n * @returns Output keying material derived from the input key.\n * @throws If the requested output length exceeds the HKDF limit\n * for the selected hash. {@link Error}\n * @example\n * HKDF (RFC 5869): derive keys from an initial input.\n * ```ts\n * import { hkdf } from '@noble/hashes/hkdf.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * import { randomBytes, utf8ToBytes } from '@noble/hashes/utils.js';\n * const inputKey = randomBytes(32);\n * const salt = randomBytes(32);\n * const info = utf8ToBytes('application-key');\n * const okm = hkdf(sha256, inputKey, salt, info, 32);\n * ```\n */\nexport const hkdf = (\n hash: TArg<CHash>,\n ikm: TArg<Uint8Array>,\n salt: TArg<Uint8Array | undefined>,\n info: TArg<Uint8Array | undefined>,\n length: number\n): TRet<Uint8Array> => expand(hash, extract(hash, ikm, salt), info, length);\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */\n\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - Value to inspect.\n * @returns `true` when the value is a Uint8Array view, including Node's `Buffer`.\n * @example\n * Guards a value before treating it as raw key material.\n *\n * ```ts\n * isBytes(new Uint8Array());\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy /\n // cross-realm cases. The fallback still requires a real ArrayBuffer view\n // so plain JSON-deserialized `{ constructor: ... }`\n // spoofing is rejected, and `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is boolean.\n * @param b - Value to validate.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Validates a boolean option before branching on it.\n *\n * ```ts\n * abool(true);\n * ```\n */\nexport function abool(b: boolean): void {\n if (typeof b !== 'boolean') throw new TypeError(`boolean expected, not ${b}`);\n}\n\n/**\n * Asserts something is a non-negative safe integer.\n * @param n - Value to validate.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validates a non-negative length or counter.\n *\n * ```ts\n * anumber(1);\n * ```\n */\nexport function anumber(n: number): void {\n if (typeof n !== 'number') throw new TypeError('number expected, got ' + typeof n);\n if (!Number.isSafeInteger(n) || n < 0)\n throw new RangeError('positive integer expected, got ' + n);\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - Value to validate.\n * @param length - Expected byte length.\n * @param title - Optional label used in error messages.\n * @returns The validated byte array.\n * On Node, `Buffer` is accepted too because it is a Uint8Array view.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument lengths. {@link RangeError}\n * @example\n * Validates a fixed-length nonce or key buffer.\n *\n * ```ts\n * abytes(new Uint8Array([1, 2]), 2);\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Asserts a hash- or MAC-like instance has not been destroyed or finished.\n * @param instance - Stateful instance to validate.\n * @param checkFinished - Whether to reject finished instances.\n * When `false`, only `destroyed` is checked.\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Guards against calling `update()` or `digest()` on a finished hash.\n *\n * ```ts\n * aexists({ destroyed: false, finished: false });\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a properly-sized byte array.\n * @param out - Output buffer to validate.\n * @param instance - Hash-like instance providing `outputLen`.\n * This is the relaxed `digestInto()`-style contract: output must be at least `outputLen`,\n * unlike one-shot cipher helpers elsewhere in the repo that often require exact lengths.\n * @throws On wrong argument types. {@link TypeError}\n * @param onlyAligned - Whether `out` must be 4-byte aligned for zero-allocation word views.\n * @throws On wrong output buffer lengths. {@link RangeError}\n * @throws On wrong output buffer alignment. {@link Error}\n * @example\n * Verifies that a caller-provided output buffer is large enough.\n *\n * ```ts\n * aoutput(new Uint8Array(16), { outputLen: 16 });\n * ```\n */\nexport function aoutput(out: any, instance: any, onlyAligned = false): void {\n abytes(out, undefined, 'output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('digestInto() expects output buffer of length at least ' + min);\n }\n if (onlyAligned && !isAligned32(out)) throw new Error('invalid output, must be aligned');\n}\n\n/** One-shot hash helper with `.create()`. */\nexport type IHash = {\n (data: string | TArg<Uint8Array>): TRet<Uint8Array>;\n /** Input block size in bytes. */\n blockLen: number;\n /** Digest size in bytes. */\n outputLen: number;\n /** Creates a fresh incremental hash instance of the same algorithm. */\n create: any;\n};\n\n/** One-shot MAC helper with `.create()`. */\nexport type CMac<H extends IHash2 = IHash2, A extends any[] = []> = {\n (msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array>;\n /** Input block size in bytes. */\n blockLen: number;\n /** Digest size in bytes. */\n outputLen: number;\n /**\n * Creates a fresh incremental MAC instance of the same algorithm.\n * @param key - MAC key bytes.\n * @param args - Additional constructor arguments, when the MAC wrapper needs them.\n * @returns Fresh incremental MAC instance.\n */\n create(key: TArg<Uint8Array>, ...args: A): H;\n};\n\n/** Generic type encompassing 8/16/32-bit typed arrays, but not 64-bit. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed-array view to Uint8Array.\n * @param arr - Typed-array view to reinterpret.\n * @returns Uint8Array view over the same bytes.\n * @example\n * Views 32-bit words as raw bytes without copying.\n *\n * ```ts\n * u8(new Uint32Array([1]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed-array view to Uint32Array.\n * @param arr - Typed-array view to reinterpret.\n * @returns Uint32Array view over the same bytes. Callers are expected to provide a\n * 4-byte-aligned offset; trailing `1..3` bytes are silently dropped.\n * @example\n * Views a byte buffer as 32-bit words for block processing.\n *\n * ```ts\n * u32(new Uint8Array(4));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place.\n * Warning: JS provides no guarantees.\n * @param arrays - Arrays to wipe.\n * @example\n * Wipes a temporary key buffer after use.\n *\n * ```ts\n * const bytes = new Uint8Array([1]);\n * clean(bytes);\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - Typed-array view to wrap.\n * @returns DataView over the same bytes.\n * @example\n * Creates an endian-aware view for length encoding.\n *\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Whether the current platform is little-endian.\n * Most are; some IBM systems are not.\n */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Reverses byte order of one 32-bit word.\n * @param word - Unsigned 32-bit word to swap.\n * @returns The same word with bytes reversed.\n * @example\n * Swaps a big-endian word into little-endian byte order.\n *\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport const byteSwap = (word: number): number =>\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff);\n\n/**\n * Normalizes one 32-bit word to the little-endian representation expected by cipher cores.\n * @param n - Unsigned 32-bit word to normalize.\n * @returns Little-endian normalized word on big-endian hosts, else the input word unchanged.\n * @example\n * Normalizes a host-endian word before passing it into an ARX/AES core.\n *\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - Uint32Array whose words should be swapped.\n * @returns The same array after in-place byte swapping.\n * @example\n * Swaps every 32-bit word in a word-view buffer.\n *\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport const byteSwap32 = (arr: TArg<Uint32Array>): TRet<Uint32Array> => {\n for (let i = 0; i < arr.length; i++) arr[i] = byteSwap(arr[i]);\n return arr as TRet<Uint32Array>;\n};\n\n/**\n * Normalizes a Uint32Array view to the little-endian representation expected by cipher cores.\n * @param u - Word view to normalize in place.\n * @returns Little-endian normalized word view.\n * @example\n * Normalizes a word-view buffer before block processing.\n *\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion:\n// {@link https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex | caniuse entry}\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 * @param bytes - Bytes to encode.\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Formats ciphertext bytes for logs or test vectors.\n *\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - Hexadecimal string to decode.\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On malformed hexadecimal input. {@link RangeError}\n * @example\n * Parses a hex test vector into bytes.\n *\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array as TRet<Uint8Array>;\n}\n\n// Used in micro\n/**\n * Converts a big-endian hex string into bigint.\n * @param hex - Hexadecimal string without `0x`.\n * @returns Parsed bigint value. The empty string is treated as `0n`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Parses a big-endian field element or counter from hex.\n *\n * ```ts\n * hexToNumber('ff');\n * ```\n */\nexport function hexToNumber(hex: string): bigint {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n return BigInt(hex === '' ? '0' : '0x' + hex); // Big Endian\n}\n\n// Used in ff1\n// BE: Big Endian, LE: Little Endian\n/**\n * Converts big-endian bytes into bigint.\n * @param bytes - Big-endian bytes.\n * @returns Parsed bigint value. Empty input is treated as `0n`.\n * @throws On invalid byte input passed to the internal hex conversion. {@link TypeError}\n * @example\n * Reads a big-endian integer from serialized bytes.\n *\n * ```ts\n * bytesToNumberBE(new Uint8Array([1, 0]));\n * ```\n */\nexport function bytesToNumberBE(bytes: TArg<Uint8Array>): bigint {\n return hexToNumber(bytesToHex(bytes));\n}\n\n// Used in micro, ff1\n/**\n * Converts a number into big-endian bytes of fixed length.\n * @param n - Number to encode.\n * @param len - Output length in bytes.\n * @returns Big-endian bytes padded to `len`.\n * Validation is indirect through `hexToBytes(...)`, so negative values, `len = 0`,\n * and values that do not fit surface through the downstream hex parser instead of a\n * dedicated range guard here.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If the requested output length cannot represent the encoded value. {@link RangeError}\n * @example\n * Encodes a counter as fixed-width big-endian bytes.\n *\n * ```ts\n * numberToBytesBE(1, 2);\n * ```\n */\nexport function numberToBytesBE(n: number | bigint, len: number): TRet<Uint8Array> {\n // Reject coercible non-numeric inputs before string/hex conversion changes behavior.\n if (typeof n === 'number') anumber(n);\n else if (typeof n !== 'bigint') throw new TypeError(`number or bigint expected, got ${typeof n}`);\n anumber(len);\n return hexToBytes(n.toString(16).padStart(len * 2, '0'));\n}\n\n// Global symbols, but ts doesn't see them:\n// {@link https://github.com/microsoft/TypeScript/issues/31535 | TypeScript issue 31535}\ndeclare const TextEncoder: any;\ndeclare const TextDecoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * @param str - String to encode.\n * @returns UTF-8 bytes in a detached fresh Uint8Array copy.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encodes application text before encryption or MACing.\n *\n * ```ts\n * utf8ToBytes('abc'); // new Uint8Array([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)) as TRet<Uint8Array>; // {@link https://bugzil.la/1681809 | Firefox bug 1681809}\n}\n\n/**\n * Converts bytes to string using UTF8 encoding.\n * @param bytes - UTF-8 bytes.\n * @returns Decoded string. Input validation is delegated to `TextDecoder`, and malformed\n * UTF-8 is replacement-decoded instead of rejected.\n * @example\n * Decodes UTF-8 plaintext back into a string.\n *\n * ```ts\n * bytesToUtf8(new Uint8Array([97, 98, 99])); // 'abc'\n * ```\n */\nexport function bytesToUtf8(bytes: TArg<Uint8Array>): string {\n return new TextDecoder().decode(bytes);\n}\n\n/**\n * Checks if two U8A use same underlying buffer and overlaps.\n * This is invalid and can corrupt data.\n * @param a - First byte view.\n * @param b - Second byte view.\n * @returns `true` when the views overlap in memory.\n * @example\n * Detects whether two slices alias the same backing buffer.\n *\n * ```ts\n * overlapBytes(new Uint8Array(4), new Uint8Array(4));\n * ```\n */\nexport function overlapBytes(a: TArg<Uint8Array>, b: TArg<Uint8Array>): boolean {\n // Zero-length views cannot overwrite anything, even if their offset sits inside another range.\n if (!a.byteLength || !b.byteLength) return false;\n return (\n a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy\n a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end\n b.byteOffset < a.byteOffset + a.byteLength // b starts before a end\n );\n}\n\n/**\n * If input and output overlap and input starts before output, we will overwrite end of input before\n * we start processing it, so this is not supported for most ciphers\n * (except chacha/salsa, which were designed for this)\n * @param input - Input bytes.\n * @param output - Output bytes.\n * @throws If the output view would overwrite unread input bytes. {@link Error}\n * @example\n * Rejects an in-place layout that would overwrite unread input bytes.\n *\n * ```ts\n * complexOverlapBytes(new Uint8Array(4), new Uint8Array(4));\n * ```\n */\nexport function complexOverlapBytes(input: TArg<Uint8Array>, output: TArg<Uint8Array>): void {\n // This is very cursed. It works somehow, but I'm completely unsure,\n // reasoning about overlapping aligned windows is very hard.\n if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)\n throw new Error('complex overlap of input and output is not supported');\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - Byte arrays to concatenate.\n * @returns Combined byte array.\n * @throws On wrong argument types inside the byte-array list. {@link TypeError}\n * @example\n * Builds a `nonce || ciphertext` style buffer.\n *\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res as TRet<Uint8Array>;\n}\n\n// Used in ARX only\ntype EmptyObj = {};\n/**\n * Merges user options into defaults.\n * @param defaults - Default option values.\n * @param opts - User-provided overrides.\n * @returns Combined options object.\n * The merge mutates `defaults` in place and returns the same object.\n * @throws If options are missing or not an object. {@link Error}\n * @example\n * Applies user overrides to the default cipher options.\n *\n * ```ts\n * checkOpts({ rounds: 20 }, { rounds: 8 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts: T2\n): T1 & T2 {\n if (opts == null || typeof opts !== 'object') throw new Error('options must be defined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/**\n * Compares two byte arrays in kinda constant time once lengths already match.\n * @param a - First byte array.\n * @param b - Second byte array.\n * @returns `true` when the arrays contain the same bytes. Different lengths still return early.\n * @example\n * Compares an expected authentication tag with the received one.\n *\n * ```ts\n * equalBytes(new Uint8Array([1]), new Uint8Array([1]));\n * ```\n */\nexport function equalBytes(a: TArg<Uint8Array>, b: TArg<Uint8Array>): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];\n return diff === 0;\n}\n\n// TODO: remove\n/** Incremental hash interface used internally. */\nexport interface IHash2 {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by the final digest. */\n outputLen: number;\n /**\n * Absorbs one more chunk into the hash state.\n * @param buf - Data chunk to hash.\n * @returns The same hash instance for chaining.\n */\n update(buf: string | TArg<Uint8Array>): this;\n /**\n * Writes the final digest into a caller-provided buffer.\n * @param buf - Destination buffer for the digest bytes.\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a fresh digest buffer.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /**\n * Resets internal state. Makes Hash instance unusable.\n * Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed\n * by user, they will need to manually call `destroy()` when zeroing is necessary.\n */\n destroy(): void;\n}\n\n/**\n * Wraps a keyed MAC constructor into a one-shot helper with `.create()`.\n * @param keyLen - Valid probe-key length used to read static metadata once.\n * The probe key is only used for `outputLen` / `blockLen`, so callers with several valid key sizes\n * can pass any representative size as long as those values stay fixed.\n * @param macCons - Keyed MAC constructor or factory.\n * @param fromMsg - Optional adapter that derives extra constructor args from the one-shot message.\n * @returns Callable MAC helper with `.create()`.\n */\nexport function wrapMacConstructor<H extends IHash2, A extends any[] = []>(\n keyLen: number,\n macCons: TArg<(key: Uint8Array, ...args: A) => H>,\n fromMsg?: TArg<(msg: Uint8Array) => A>\n): TRet<CMac<H, A>> {\n const mac = macCons as (key: TArg<Uint8Array>, ...args: A) => H;\n const getArgs = (fromMsg || (() => [] as unknown as A)) as (msg: TArg<Uint8Array>) => A;\n const macC: any = (msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array> =>\n mac(key, ...getArgs(msg))\n .update(msg)\n .digest();\n const tmp = mac(new Uint8Array(keyLen), ...getArgs(new Uint8Array(0)));\n macC.outputLen = tmp.outputLen;\n macC.blockLen = tmp.blockLen;\n macC.create = (key: TArg<Uint8Array>, ...args: A) => mac(key, ...args);\n return macC as TRet<CMac<H, A>>;\n}\n\n// This will allow to re-use with composable things like packed & base encoders\n// Also, we probably can make tags composable\n\n/** Sync cipher: takes byte array and returns byte array. */\nexport type Cipher = {\n /**\n * Encrypts plaintext bytes.\n * @param plaintext - Data to encrypt.\n * @returns Ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>): TRet<Uint8Array>;\n /**\n * Decrypts ciphertext bytes.\n * @param ciphertext - Data to decrypt.\n * @returns Plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Async cipher e.g. from built-in WebCrypto. */\nexport type AsyncCipher = {\n /**\n * Encrypts plaintext bytes.\n * @param plaintext - Data to encrypt.\n * @returns Promise resolving to ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>): Promise<TRet<Uint8Array>>;\n /**\n * Decrypts ciphertext bytes.\n * @param ciphertext - Data to decrypt.\n * @returns Promise resolving to plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>): Promise<TRet<Uint8Array>>;\n};\n\n/** Cipher with `output` argument which can optimize by doing 1 less allocation. */\nexport type CipherWithOutput = Cipher & {\n /**\n * Encrypts plaintext bytes into an optional caller-provided buffer.\n * @param plaintext - Data to encrypt.\n * @param output - Optional destination buffer.\n * @returns Ciphertext bytes.\n */\n encrypt(plaintext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array>;\n /**\n * Decrypts ciphertext bytes into an optional caller-provided buffer.\n * @param ciphertext - Data to decrypt.\n * @param output - Optional destination buffer.\n * @returns Plaintext bytes.\n */\n decrypt(ciphertext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/**\n * Params are outside of return type, so it is accessible before calling constructor.\n * If function support multiple nonceLength's, we return the best one.\n */\nexport type CipherParams = {\n /** Cipher block size in bytes. */\n blockSize: number;\n /** Nonce length in bytes when the cipher uses a fixed nonce size. */\n nonceLength?: number;\n /** Authentication-tag length in bytes for AEAD modes. */\n tagLength?: number;\n /** Whether nonce length is variable at runtime. */\n varSizeNonce?: boolean;\n};\n/**\n * ARX AEAD cipher, like salsa or chacha.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param AAD - Optional associated data.\n * @returns Cipher instance with caller-managed output buffers.\n */\nexport type ARXCipher = ((\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n) => CipherWithOutput) & {\n blockSize: number;\n nonceLength: number;\n tagLength: number;\n};\n/**\n * Cipher constructor signature.\n * @param key - Secret key bytes.\n * @param args - Additional constructor arguments, such as nonce or IV.\n * @returns Cipher instance.\n */\nexport type CipherCons<T extends any[]> = (key: TArg<Uint8Array>, ...args: T) => Cipher;\n/**\n * Wraps a cipher: validates args, ensures encrypt() can only be called once.\n * Used internally by the exported cipher constructors.\n * Output-buffer support is inferred from the wrapped `encrypt` / `decrypt`\n * arity (`fn.length === 2`), and tag-bearing constructors are expected to use\n * `args[1]` for optional AAD.\n * @__NO_SIDE_EFFECTS__\n * @param params - Static cipher metadata. See {@link CipherParams}.\n * @param constructor - Cipher constructor.\n * @returns Wrapped constructor with validation.\n */\nexport const wrapCipher = <C extends CipherCons<any>, P extends CipherParams>(\n params: P,\n constructor: C\n): C & P => {\n function wrappedCipher(key: TArg<Uint8Array>, ...args: any[]): TRet<CipherWithOutput> {\n // Validate key\n abytes(key, undefined, 'key');\n\n // Validate nonce if nonceLength is present\n if (params.nonceLength !== undefined) {\n const nonce = args[0];\n abytes(nonce, params.varSizeNonce ? undefined : params.nonceLength, 'nonce');\n }\n\n // Validate AAD if tagLength present\n const tagl = params.tagLength;\n if (tagl && args[1] !== undefined) abytes(args[1], undefined, 'AAD');\n\n const cipher = constructor(key, ...args);\n const checkOutput = (fnLength: number, output?: TArg<Uint8Array>) => {\n if (output !== undefined) {\n if (fnLength !== 2) throw new Error('cipher output not supported');\n abytes(output, undefined, 'output');\n }\n };\n // Create wrapped cipher with validation and single-use encryption\n let called = false;\n const wrCipher = {\n encrypt(data: TArg<Uint8Array>, output?: TArg<Uint8Array>) {\n if (called) throw new Error('cannot encrypt() twice with same key + nonce');\n called = true;\n abytes(data);\n checkOutput(cipher.encrypt.length, output);\n return (cipher as CipherWithOutput).encrypt(data, output);\n },\n decrypt(data: TArg<Uint8Array>, output?: TArg<Uint8Array>) {\n abytes(data);\n if (tagl && data.length < tagl)\n throw new Error('\"ciphertext\" expected length bigger than tagLength=' + tagl);\n checkOutput(cipher.decrypt.length, output);\n return (cipher as CipherWithOutput).decrypt(data, output);\n },\n };\n\n return wrCipher as TRet<CipherWithOutput>;\n }\n\n Object.assign(wrappedCipher, params);\n return wrappedCipher as C & P;\n};\n\n/**\n * Represents a Salsa or ChaCha xor stream.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Optional starting block counter.\n * @returns Output bytes.\n */\nexport type XorStream = (\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n data: TArg<Uint8Array>,\n output?: TArg<Uint8Array>,\n counter?: number\n) => TRet<Uint8Array>;\n\n/**\n * By default, returns u8a of length.\n * When out is available, it checks it for validity and uses it.\n * @param expectedLength - Required output length.\n * @param out - Optional destination buffer.\n * @param onlyAligned - Whether `out` must be 4-byte aligned.\n * @returns Output buffer ready for writing.\n * @throws On wrong argument types. {@link TypeError}\n * @throws If the provided output buffer has the wrong size or alignment. {@link Error}\n * @example\n * Reuses a caller-provided output buffer when lengths match.\n *\n * ```ts\n * getOutput(16, new Uint8Array(16));\n * ```\n */\nexport function getOutput(\n expectedLength: number,\n out?: TArg<Uint8Array>,\n onlyAligned = true\n): TRet<Uint8Array> {\n if (out === undefined) return new Uint8Array(expectedLength) as TRet<Uint8Array>;\n // Keep Buffer/cross-realm Uint8Array support here instead of trusting a shape-compatible object.\n abytes(out, undefined, 'output');\n if (out.length !== expectedLength)\n throw new Error(\n '\"output\" expected Uint8Array of length ' + expectedLength + ', got: ' + out.length\n );\n if (onlyAligned && !isAligned32(out)) throw new Error('invalid output, must be aligned');\n return out as TRet<Uint8Array>;\n}\n\n/**\n * Encodes data and AAD bit lengths into a 16-byte buffer.\n * @param dataLength - Data length in bits.\n * @param aadLength - AAD length in bits.\n * The serialized block is still `aadLength || dataLength`, matching GCM/Poly1305\n * conventions even though the helper parameter order is `(dataLength, aadLength)`.\n * @param isLE - Whether to encode lengths as little-endian.\n * @returns 16-byte length block.\n * @throws On wrong argument types passed to the endian validator. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Builds the length block appended by GCM and Poly1305.\n *\n * ```ts\n * u64Lengths(16, 8, true);\n * ```\n */\nexport function u64Lengths(dataLength: number, aadLength: number, isLE: boolean): TRet<Uint8Array> {\n // Reject coercible non-number lengths like '10' and true before BigInt(...) accepts them.\n anumber(dataLength);\n anumber(aadLength);\n abool(isLE);\n const num = new Uint8Array(16);\n const view = createView(num);\n view.setBigUint64(0, BigInt(aadLength), isLE);\n view.setBigUint64(8, BigInt(dataLength), isLE);\n return num as TRet<Uint8Array>;\n}\n\n/**\n * Checks whether a byte array is aligned to a 4-byte offset.\n * @param bytes - Byte array to inspect.\n * @returns `true` when the view is 4-byte aligned.\n * @example\n * Checks whether a buffer can be safely viewed as Uint32Array.\n *\n * ```ts\n * isAligned32(new Uint8Array(4));\n * ```\n */\nexport function isAligned32(bytes: TArg<Uint8Array>): boolean {\n return bytes.byteOffset % 4 === 0;\n}\n\n/**\n * Copies bytes into a new Uint8Array.\n * @param bytes - Bytes to copy.\n * @returns Copied byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Copies input into an aligned Uint8Array before block processing.\n *\n * ```ts\n * copyBytes(new Uint8Array([1, 2]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Cryptographically secure PRNG.\n * Uses internal OS-level `crypto.getRandomValues`.\n * @param bytesLength - Number of bytes to produce.\n * Validation is delegated to `Uint8Array(bytesLength)` and `getRandomValues`, so\n * non-integers, negative lengths, and oversize requests surface backend/runtime errors.\n * @returns Random byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the runtime does not expose `crypto.getRandomValues`. {@link Error}\n * @example\n * Generates a fresh nonce or key.\n *\n * ```ts\n * randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Validate upfront so fractional / coercible lengths do not silently\n // truncate through Uint8Array().\n anumber(bytesLength);\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n return cr.getRandomValues(new Uint8Array(bytesLength)) as TRet<Uint8Array>;\n}\n\n/**\n * The pseudorandom number generator doesn't wipe current state:\n * instead, it generates new one based on previous state + entropy.\n * Not reseed/rekey, since AES CTR DRBG does rekey on each randomBytes,\n * which is in fact `reseed`, since it changes counter too.\n */\nexport interface PRG {\n /**\n * Mixes fresh entropy into the current generator state.\n * @param seed - Entropy bytes to absorb.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Produces a requested number of pseudorandom bytes.\n * @param bytesLength - Number of bytes to generate.\n * @returns Random byte array.\n */\n randomBytes(bytesLength: number): TRet<Uint8Array>;\n /** Destroys the generator state. */\n clean(): void;\n}\n\n/** Removes the nonce argument from a cipher constructor type. */\nexport type RemoveNonce<T extends (...args: any) => any> = T extends (\n arg0: any,\n arg1: any,\n ...rest: infer R\n) => infer Ret\n ? (key: TArg<Uint8Array>, ...args: R) => Ret\n : never;\n/**\n * Cipher constructor that requires a nonce argument.\n * @param key - Secret key bytes.\n * @param nonce - Nonce bytes.\n * @param args - Additional cipher-specific arguments.\n * @returns Cipher instance.\n */\nexport type CipherWithNonce = ((\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n ...args: any[]\n) => Cipher | AsyncCipher) & {\n nonceLength: number;\n};\n\n/**\n * Uses CSPRNG for nonce, nonce injected in ciphertext.\n * For `encrypt`, a `nonceBytes`-length buffer is fetched from CSPRNG and\n * prepended to encrypted ciphertext. For `decrypt`, first `nonceBytes` of ciphertext\n * are treated as nonce. The wrapper always allocates a fresh `nonce || ciphertext`\n * buffer on encrypt and intentionally does not support caller-provided destination buffers.\n * Too-short decrypt inputs are split into short/empty nonce views and then delegated\n * to the wrapped cipher instead of being rejected here first.\n *\n * NOTE: Under the same key, using random nonces (e.g. `managedNonce`) with AES-GCM and ChaCha\n * should be limited to `2**23` (8M) messages to get a collision chance of\n * `2**-50`. Stretching to `2**32` (4B) messages would raise that chance to\n * `2**-33`, still negligible but creeping up.\n * @param fn - Cipher constructor that expects a nonce.\n * @param randomBytes_ - Random-byte source used for nonce generation.\n * @returns Cipher constructor that prepends the nonce to ciphertext.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On invalid nonce lengths observed at wrapper construction or use. {@link RangeError}\n * @example\n * Prepends a fresh random nonce to every ciphertext.\n *\n * ```ts\n * import { gcm } from '@noble/ciphers/aes.js';\n * import { managedNonce, randomBytes } from '@noble/ciphers/utils.js';\n * const wrapped = managedNonce(gcm);\n * const key = randomBytes(16);\n * const ciphertext = wrapped(key).encrypt(new Uint8Array([1, 2, 3]));\n * wrapped(key).decrypt(ciphertext);\n * ```\n */\nexport function managedNonce<T extends CipherWithNonce>(\n fn: T,\n randomBytes_: typeof randomBytes = randomBytes\n): TRet<RemoveNonce<T>> {\n const { nonceLength } = fn;\n anumber(nonceLength);\n const addNonce = (\n nonce: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n plaintext: TArg<Uint8Array>\n ) => {\n const out = concatBytes(nonce, ciphertext);\n // Wrapped ciphers may alias caller plaintext on encrypt(); never zero\n // caller-owned buffers here.\n if (!overlapBytes(plaintext, ciphertext)) ciphertext.fill(0);\n return out;\n };\n // NOTE: we cannot support DST here, it would be mistake:\n // - we don't know how much dst length cipher requires\n // - nonce may unalign dst and break everything\n // - we create new u8a anyway (concatBytes)\n // - previously we passed all args to cipher, but that was mistake!\n const res = ((key: TArg<Uint8Array>, ...args: any[]): any => ({\n encrypt(plaintext: TArg<Uint8Array>) {\n abytes(plaintext);\n const nonce = randomBytes_(nonceLength);\n const encrypted = fn(key, nonce, ...args).encrypt(plaintext);\n // @ts-ignore\n if (encrypted instanceof Promise)\n return encrypted.then((ct) => addNonce(nonce, ct, plaintext));\n return addNonce(nonce, encrypted, plaintext);\n },\n decrypt(ciphertext: TArg<Uint8Array>) {\n abytes(ciphertext);\n const nonce = ciphertext.subarray(0, nonceLength);\n const decrypted = ciphertext.subarray(nonceLength);\n return fn(key, nonce, ...args).decrypt(decrypted);\n },\n })) as RemoveNonce<T> & { blockSize?: number; tagLength?: number };\n // Auto-nonce wrappers still preserve the wrapped payload geometry.\n if ('blockSize' in fn) res.blockSize = (fn as any).blockSize;\n if ('tagLength' in fn) res.tagLength = (fn as any).tagLength;\n return res as TRet<RemoveNonce<T>>;\n}\n\n/** `Uint8Array.of()` return type helper for TS 5.9. */\nexport type Uint8ArrayBuffer = TRet<Uint8Array>;\n","/**\n * Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.\n\nRFC8439 requires multi-step cipher stream, where\nauthKey starts with counter: 0, actual msg with counter: 1.\n\nFor this, we need a way to re-use nonce / counter:\n\n const counter = new Uint8Array(4);\n chacha(..., counter, ...); // counter is now 1\n chacha(..., counter, ...); // counter is now 2\n\nThis is complicated:\n\n- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB\n- Original papers don't allow mutating counters\n- Counter overflow is undefined [^1]\n- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it\n- Caveat: Cannot be re-used through all cases:\n- * chacha has (counter | nonce)\n- * xchacha has (nonce16 | counter | nonce16)\n- Idea B: separate nonce / counter and provide separate API for counter re-use\n- Caveat: there are different counter sizes depending on an algorithm.\n- salsa & chacha also differ in structures of key & sigma:\n salsa20: s[0] | k(4) | s[1] | nonce(2) | cnt(2) | s[2] | k(4) | s[3]\n chacha: s(4) | k(8) | cnt(1) | nonce(3)\n chacha20orig: s(4) | k(8) | cnt(2) | nonce(2)\n- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`\n- Caveat: we can't re-use counter array\n\nxchacha uses the subkey and remaining 8 byte nonce with ChaCha20 as normal\n(prefixed by 4 NUL bytes, since RFC8439 specifies a 12-byte nonce).\nCounter overflow is undefined; see {@link https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/ | the CFRG thread}.\nCurrent noble policy is strict non-wrap for the shared 32-bit counter path:\nexported ARX ciphers reject initial `0xffffffff` and stop before any implicit\nwrap back to zero.\nSee {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2 | the XChaCha appendix} for the extended-nonce construction.\n\n * @module\n */\nimport {\n type PRG,\n type TArg,\n type TRet,\n type XorStream,\n abool,\n abytes,\n anumber,\n checkOpts,\n clean,\n copyBytes,\n getOutput,\n isAligned32,\n isLE,\n randomBytes,\n swap32IfBE,\n u32,\n} from './utils.ts';\n\n// Replaces `TextEncoder` for ASCII literals, which is enough for sigma constants.\n// Non-ASCII input would not match UTF-8 `TextEncoder` output.\nconst encodeStr = (str: string) => Uint8Array.from(str.split(''), (c) => c.charCodeAt(0));\n// Raw `createCipher(...)` exports consume these native-endian `u32(...)` views directly.\n// Public `wrapCipher(...)` APIs reject non-little-endian platforms before reaching this path.\n// RFC 8439 §2.3 / RFC 7539 §2.3 only define the 256-bit-key constants; this 16-byte sigma is\n// kept for legacy allowShortKeys Salsa/ChaCha variants.\nconst sigma16_32 = /* @__PURE__ */ (() => swap32IfBE(u32(encodeStr('expand 16-byte k'))))();\n// RFC 8439 §2.3 / RFC 7539 §2.3 define words 0-3 as\n// `0x61707865 0x3320646e 0x79622d32 0x6b206574`, i.e. `expand 32-byte k`.\nconst sigma32_32 = /* @__PURE__ */ (() => swap32IfBE(u32(encodeStr('expand 32-byte k'))))();\n\n/**\n * Rotates a 32-bit word left.\n * @param a - Input word.\n * @param b - Rotation count in bits.\n * @returns Rotated 32-bit word.\n * @example\n * Moves the top byte of `0x12345678` into the low byte position.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(a: number, b: number): number {\n return (a << b) | (a >>> (32 - b));\n}\n\n/**\n * ARX core function operating on 32-bit words. Ciphers must use u32 for efficiency.\n * @param sigma - Sigma constants for the selected cipher layout.\n * @param key - Expanded key words.\n * @param nonce - Nonce and counter words prepared for the round function.\n * @param output - Output block written in place.\n * @param counter - Block counter value.\n * @param rounds - Optional round count override.\n */\nexport type CipherCoreFn = (\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n nonce: TArg<Uint32Array>,\n output: TArg<Uint32Array>,\n counter: number,\n rounds?: number\n) => void;\n\n/**\n * Nonce-extension function used by XChaCha and XSalsa.\n * @param sigma - Sigma constants for the selected cipher layout.\n * @param key - Expanded key words.\n * @param input - Input nonce words used for subkey derivation.\n * @param output - Output buffer written with the derived nonce words.\n */\nexport type ExtendNonceFn = (\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n input: TArg<Uint32Array>,\n output: TArg<Uint32Array>\n) => void;\n\n/** ARX cipher options.\n * * `allowShortKeys` for 16-byte keys\n * * `counterLength` in bytes\n * * `counterRight`: right: `nonce|counter`; left: `counter|nonce`\n * */\nexport type CipherOpts = {\n /** Whether 16-byte keys are accepted for legacy Salsa and ChaCha variants. */\n allowShortKeys?: boolean;\n /** Optional nonce-expansion hook used by extended-nonce variants. */\n extendNonceFn?: ExtendNonceFn;\n /** Counter length in bytes inside the nonce/counter layout. */\n counterLength?: number;\n /** Whether the layout is `nonce|counter` instead of `counter|nonce`. */\n counterRight?: boolean;\n /** Number of core rounds to execute. */\n rounds?: number;\n};\n\n// Salsa and Chacha block length is always 512-bit\nconst BLOCK_LEN = 64;\n// RFC 8439 §2.2 / RFC 7539 §2.2: the ChaCha state has 16 32-bit words.\nconst BLOCK_LEN32 = 16;\n\n// Counter policy for the shared public `counter` argument:\n// - RFC/IETF ChaCha20 uses a 32-bit counter.\n// - OpenSSL/Node `chacha20` instead treat the full 16-byte IV as a 128-bit\n// counter state and carry into the next word.\n// - Raw `chacha20orig`, `salsa20`, `xsalsa20`, and `xchacha20` use 64-bit counters in libsodium\n// and libtomcrypt, while some libs (for example libtomcrypt's RFC/IETF path) reject the max\n// boundary instead of carrying.\n// - AEAD wrappers diverge too: libsodium `xchacha20poly1305` uses the IETF payload counter from\n// block 1, while `secretstream_xchacha20poly1305` is a different protocol with rekey/reset.\n// Noble intentionally throws instead of silently picking one wrap model for users. In the default\n// path, even a 32-bit boundary would take 2^32 blocks * 64 bytes = 256 GiB, which is practically\n// unreachable for normal JS callers; advanced users who pass `counter` explicitly can implement\n// whatever wider carry / wrap policy they need on top.\nconst MAX_COUNTER = /* @__PURE__ */ (() => 2 ** 32 - 1)();\nconst U32_EMPTY = /* @__PURE__ */ Uint32Array.of();\nfunction runCipher(\n core: TArg<CipherCoreFn>,\n sigma: TArg<Uint32Array>,\n key: TArg<Uint32Array>,\n nonce: TArg<Uint32Array>,\n data: TArg<Uint8Array>,\n output: TArg<Uint8Array>,\n counter: number,\n rounds: number\n): void {\n const len = data.length;\n const block = new Uint8Array(BLOCK_LEN);\n const b32 = u32(block);\n // Make sure that buffers aligned to 4 bytes\n const isAligned = isLE && isAligned32(data) && isAligned32(output);\n const d32 = isAligned ? u32(data) : U32_EMPTY;\n const o32 = isAligned ? u32(output) : U32_EMPTY;\n // RFC 8439 §2.4.1 / RFC 7539 §2.4.1 allow XORing one keystream block at a time and\n // truncating the final partial block instead of materializing the whole keystream.\n if (!isLE) {\n for (let pos = 0; pos < len; counter++) {\n core(\n sigma as TRet<Uint32Array>,\n key as TRet<Uint32Array>,\n nonce as TRet<Uint32Array>,\n b32,\n counter,\n rounds\n );\n // RFC 8439 §2.4 / RFC 7539 §2.4 serialize keystream words in little-endian order.\n swap32IfBE(b32);\n if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const take = Math.min(BLOCK_LEN, len - pos);\n for (let j = 0, posj; j < take; j++) {\n posj = pos + j;\n output[posj] = data[posj] ^ block[j];\n }\n pos += take;\n }\n return;\n }\n for (let pos = 0; pos < len; counter++) {\n core(\n sigma as TRet<Uint32Array>,\n key as TRet<Uint32Array>,\n nonce as TRet<Uint32Array>,\n b32,\n counter,\n rounds\n );\n // See MAX_COUNTER policy note above: never silently wrap the shared public counter.\n if (counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const take = Math.min(BLOCK_LEN, len - pos);\n // aligned to 4 bytes\n if (isAligned && take === BLOCK_LEN) {\n const pos32 = pos / 4;\n if (pos % 4 !== 0) throw new Error('arx: invalid block position');\n for (let j = 0, posj: number; j < BLOCK_LEN32; j++) {\n posj = pos32 + j;\n o32[posj] = d32[posj] ^ b32[j];\n }\n pos += BLOCK_LEN;\n continue;\n }\n for (let j = 0, posj; j < take; j++) {\n posj = pos + j;\n output[posj] = data[posj] ^ block[j];\n }\n pos += take;\n }\n}\n\n/**\n * Creates an ARX stream cipher from a 32-bit core permutation.\n * Used internally to build the exported Salsa and ChaCha stream ciphers.\n * @param core - Core function that fills one keystream block.\n * @param opts - Cipher layout and nonce-extension options. See {@link CipherOpts}.\n * @returns Stream cipher function over byte arrays.\n * @throws If the core callback, key size, counter, or output sizing is invalid. {@link Error}\n */\nexport function createCipher(core: TArg<CipherCoreFn>, opts: TArg<CipherOpts>): TRet<XorStream> {\n const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts(\n { allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 },\n opts\n );\n if (typeof core !== 'function') throw new Error('core must be a function');\n anumber(counterLength);\n anumber(rounds);\n abool(counterRight);\n abool(allowShortKeys);\n return (\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n data: TArg<Uint8Array>,\n output?: TArg<Uint8Array>,\n counter = 0\n ): TRet<Uint8Array> => {\n abytes(key, undefined, 'key');\n abytes(nonce, undefined, 'nonce');\n abytes(data, undefined, 'data');\n const len = data.length;\n // Raw XorStream APIs return ciphertext/plaintext bytes directly, so caller-provided outputs\n // must match the logical result length exactly instead of returning an oversized workspace.\n output = getOutput(len, output, false);\n anumber(counter);\n // See MAX_COUNTER policy note above: reject advanced explicit-counter requests before any wrap.\n if (counter < 0 || counter >= MAX_COUNTER) throw new Error('arx: counter overflow');\n const toClean = [];\n\n // Key & sigma\n // key=16 -> sigma16, k=key|key\n // key=32 -> sigma32, k=key\n let l = key.length;\n let k: Uint8Array;\n let sigma: Uint32Array;\n if (l === 32) {\n // Copy caller keys too: big-endian normalization, extended-nonce subkey derivation, and\n // final clean(...) all mutate or wipe the temporary buffer in place.\n toClean.push((k = copyBytes(key)));\n sigma = sigma32_32;\n } else if (l === 16 && allowShortKeys) {\n k = new Uint8Array(32);\n k.set(key);\n k.set(key, 16);\n sigma = sigma16_32;\n toClean.push(k);\n } else {\n abytes(key, 32, 'arx key');\n throw new Error('invalid key size');\n // throw new Error(`\"arx key\" expected Uint8Array of length 32, got length=${l}`);\n }\n\n // Nonce\n // salsa20: 8 (8-byte counter)\n // chacha20orig: 8 (8-byte counter)\n // chacha20: 12 (4-byte counter)\n // xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)\n // xchacha20: 24 (16 -> hchacha, 8 -> old nonce)\n // Copy before taking u32(...) views on misaligned inputs, and on big-endian so later\n // swap32IfBE(...) never mutates caller nonce bytes in place.\n if (!isLE || !isAligned32(nonce)) toClean.push((nonce = copyBytes(nonce)));\n\n let k32 = u32(k);\n // hsalsa & hchacha: handle extended nonce\n if (extendNonceFn) {\n if (nonce.length !== 24) throw new Error(`arx: extended nonce must be 24 bytes`);\n const n16 = nonce.subarray(0, 16);\n if (isLE) extendNonceFn(sigma as TRet<Uint32Array>, k32, u32(n16), k32);\n else {\n const sigmaRaw = swap32IfBE(Uint32Array.from(sigma));\n extendNonceFn(sigmaRaw, k32, u32(n16), k32);\n clean(sigmaRaw);\n swap32IfBE(k32);\n }\n nonce = nonce.subarray(16);\n } else if (!isLE) swap32IfBE(k32);\n\n // Handle nonce counter\n const nonceNcLen = 16 - counterLength;\n if (nonceNcLen !== nonce.length)\n throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);\n\n // Normalize 64-bit-nonce layouts to the 12-byte core input: ChaCha/XChaCha prefix 4 zero\n // counter bytes, while Salsa/XSalsa append them after the nonce words.\n if (nonceNcLen !== 12) {\n const nc = new Uint8Array(12);\n nc.set(nonce, counterRight ? 0 : 12 - nonce.length);\n nonce = nc;\n toClean.push(nonce);\n }\n const n32 = swap32IfBE(u32(nonce));\n // Ensure temporary key/nonce copies are wiped even if the remaining\n // runtime guard in runCipher(...) throws on counter overflow.\n try {\n runCipher(core, sigma, k32, n32, data, output, counter, rounds);\n return output as TRet<Uint8Array>;\n } finally {\n clean(...toClean);\n }\n };\n}\n\n/** Internal class which wraps chacha20 or chacha8 to create CSPRNG. */\nexport class _XorStreamPRG implements PRG {\n readonly blockLen: number;\n readonly keyLen: number;\n readonly nonceLen: number;\n private state: TRet<Uint8Array>;\n private buf: TRet<Uint8Array>;\n private key: TRet<Uint8Array>;\n private nonce: TRet<Uint8Array>;\n private pos: number;\n private ctr: number;\n private cipher: TArg<XorStream>;\n constructor(\n cipher: TArg<XorStream>,\n blockLen: number,\n keyLen: number,\n nonceLen: number,\n seed: TArg<Uint8Array>\n ) {\n this.cipher = cipher;\n this.blockLen = blockLen;\n this.keyLen = keyLen;\n this.nonceLen = nonceLen;\n this.state = new Uint8Array(this.keyLen + this.nonceLen) as TRet<Uint8Array>;\n this.reseed(seed);\n this.ctr = 0;\n this.pos = this.blockLen;\n this.buf = new Uint8Array(this.blockLen) as TRet<Uint8Array>;\n // Keep a single key||nonce backing buffer so reseed/addEntropy/clean update the live cipher\n // inputs in place through these subarray views.\n this.key = this.state.subarray(0, this.keyLen) as TRet<Uint8Array>;\n this.nonce = this.state.subarray(this.keyLen) as TRet<Uint8Array>;\n }\n private reseed(seed: TArg<Uint8Array>) {\n abytes(seed);\n if (!seed || seed.length === 0) throw new Error('entropy required');\n // Mix variable-length entropy cyclically across the whole key||nonce state, then restart the\n // keystream so buffered leftovers from the previous state are never reused.\n for (let i = 0; i < seed.length; i++) this.state[i % this.state.length] ^= seed[i];\n this.ctr = 0;\n this.pos = this.blockLen;\n }\n addEntropy(seed: TArg<Uint8Array>): void {\n // Reject empty entropy before re-keying, otherwise a throwing call would still advance state.\n abytes(seed);\n if (seed.length === 0) throw new Error('entropy required');\n // Re-key from the current stream first, then mix external entropy into the fresh key||nonce\n // state through reseed() so stale buffered bytes are discarded.\n this.state.set(this.randomBytes(this.state.length));\n this.reseed(seed);\n }\n randomBytes(len: number): TRet<Uint8Array> {\n anumber(len);\n if (len === 0) return new Uint8Array(0) as TRet<Uint8Array>;\n const avail = this.pos < this.blockLen ? this.blockLen - this.pos : 0;\n const blocks = Math.ceil(Math.max(0, len - avail) / this.blockLen);\n // Preflight overflow so failed reads don't partially consume keystream\n // and leave the PRG repeating blocks.\n if (blocks > 0 && this.ctr > MAX_COUNTER - blocks) throw new Error('arx: counter overflow');\n const out = new Uint8Array(len);\n let outPos = 0;\n // `out` starts zero-filled, and `buf.fill(0)` below does the same for leftovers: XOR-stream\n // ciphers then emit raw keystream bytes directly into those buffers.\n // Serve buffered leftovers first so split reads stay identical to one larger read.\n if (this.pos < this.blockLen) {\n const take = Math.min(len, this.blockLen - this.pos);\n out.set(this.buf.subarray(this.pos, this.pos + take), 0);\n this.pos += take;\n outPos += take;\n if (outPos === len) return out as TRet<Uint8Array>; // fast path\n }\n // Full blocks directly to out\n const full = Math.floor((len - outPos) / this.blockLen);\n if (full > 0) {\n const blockBytes = full * this.blockLen;\n const b = out.subarray(outPos, outPos + blockBytes);\n this.cipher(this.key, this.nonce, b as TRet<Uint8Array>, b as TRet<Uint8Array>, this.ctr);\n this.ctr += full;\n outPos += blockBytes;\n }\n // Save leftovers\n const left = len - outPos;\n if (left > 0) {\n this.buf.fill(0);\n // NOTE: cipher will handle overflow\n this.cipher(\n this.key,\n this.nonce,\n this.buf as TRet<Uint8Array>,\n this.buf as TRet<Uint8Array>,\n this.ctr++\n );\n out.set(this.buf.subarray(0, left), outPos);\n this.pos = left;\n }\n return out as TRet<Uint8Array>;\n }\n // Clone seeds the new instance from this stream, so the source PRG advances too.\n clone(): _XorStreamPRG {\n return new _XorStreamPRG(\n this.cipher,\n this.blockLen,\n this.keyLen,\n this.nonceLen,\n this.randomBytes(this.state.length)\n );\n }\n // Zeroes the current state and leftover buffer, but does not make the instance unusable:\n // Later reads first drain zeros from the cleared buffer and then continue\n // from zero key||nonce state.\n clean(): void {\n this.pos = 0;\n this.ctr = 0;\n this.buf.fill(0);\n this.state.fill(0);\n }\n}\n\n/**\n * PRG constructor backed by an ARX stream cipher.\n * @param seed - Optional seed bytes mixed into the initial state. When omitted, exactly 32\n * random bytes are mixed in by default: larger states keep a zero tail, while smaller states\n * wrap those bytes through `reseed()`'s XOR schedule.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n */\nexport type XorPRG = (seed?: TArg<Uint8Array>) => TRet<_XorStreamPRG>;\n\n/**\n * Creates a PRG constructor from a stream cipher.\n * @param cipher - Stream cipher used to fill output blocks.\n * @param blockLen - Keystream block length in bytes.\n * @param keyLen - Internal key length in bytes.\n * @param nonceLen - Internal nonce length in bytes.\n * @returns PRG factory for seeded concrete `_XorStreamPRG` instances.\n * @example\n * Builds a PRG from XChaCha20 and reads bytes from a randomly seeded instance.\n * ```ts\n * import { xchacha20 } from '@noble/ciphers/chacha.js';\n * import { createPRG } from '@noble/ciphers/_arx.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const init = createPRG(xchacha20, 64, 32, 24);\n * const prg = init(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const createPRG = (\n cipher: TArg<XorStream>,\n blockLen: number,\n keyLen: number,\n nonceLen: number\n): TRet<XorPRG> => {\n return ((seed: TArg<Uint8Array> = randomBytes(32)): TRet<_XorStreamPRG> =>\n new _XorStreamPRG(\n cipher,\n blockLen,\n keyLen,\n nonceLen,\n seed\n ) as TRet<_XorStreamPRG>) as TRet<XorPRG>;\n};\n","/**\n * Poly1305 ({@link https://cr.yp.to/mac/poly1305-20050329.pdf | PDF},\n * {@link https://en.wikipedia.org/wiki/Poly1305 | wiki})\n * is a fast and parallel secret-key message-authentication code suitable for\n * a wide variety of applications. It was standardized in\n * {@link https://www.rfc-editor.org/rfc/rfc8439 | RFC 8439} and is now used in TLS 1.3.\n *\n * Polynomial MACs are not perfect for every situation:\n * they lack Random Key Robustness: the MAC can be forged, and can't be used in PAKE schemes.\n * See {@link https://keymaterial.net/2020/09/07/invisible-salamanders-in-aes-gcm-siv/ | the invisible salamanders attack writeup}.\n * To combat invisible salamanders, `hash(key)` can be included in ciphertext,\n * however, this would violate ciphertext indistinguishability:\n * an attacker would know which key was used - so `HKDF(key, i)`\n * could be used instead.\n *\n * Check out the {@link https://cr.yp.to/mac.html | original website}.\n * Based on public-domain {@link https://github.com/floodyberry/poly1305-donna | poly1305-donna}.\n * @module\n */\n// prettier-ignore\nimport {\n abytes, aexists, aoutput, bytesToHex,\n clean, concatBytes, copyBytes, hexToNumber, numberToBytesBE,\n wrapMacConstructor, type CMac, type IHash2, type TArg, type TRet\n} from './utils.ts';\n\n// Little-endian 2-byte load used by the Poly1305 limb decomposition.\nfunction u8to16(a: TArg<Uint8Array>, i: number) {\n return (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);\n}\n\nfunction bytesToNumberLE(bytes: TArg<Uint8Array>): bigint {\n return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));\n}\n\n/** Small version of `poly1305` without loop unrolling. Unused, provided for auditability. */\nfunction poly1305_small(msg: TArg<Uint8Array>, key: TArg<Uint8Array>): TRet<Uint8Array> {\n abytes(msg);\n abytes(key, 32, 'key');\n const POW_2_130_5 = BigInt(2) ** BigInt(130) - BigInt(5); // 2^130-5\n const POW_2_128_1 = BigInt(2) ** BigInt(128) - BigInt(1); // 2^128-1\n const CLAMP_R = BigInt('0x0ffffffc0ffffffc0ffffffc0fffffff');\n const r = bytesToNumberLE(key.subarray(0, 16)) & CLAMP_R;\n const s = bytesToNumberLE(key.subarray(16));\n // Process by 16 byte chunks\n let acc = BigInt(0);\n for (let i = 0; i < msg.length; i += 16) {\n const m = msg.subarray(i, i + 16);\n // RFC 8439 §2.5.1 / RFC 7539 §2.5.1 append [0x01] to each chunk before multiplying by r.\n const n = bytesToNumberLE(m) | (BigInt(1) << BigInt(8 * m.length));\n acc = ((acc + n) * r) % POW_2_130_5;\n }\n const res = (acc + s) & POW_2_128_1;\n // RFC 8439 §2.5 / RFC 7539 §2.5 serialize the low 128 bits in little-endian order.\n return numberToBytesBE(res, 16).reverse() as TRet<Uint8Array>; // LE\n}\n\n// Can be used to replace `computeTag` in chacha.ts. Unused, provided for auditability.\n// @ts-expect-error\nfunction poly1305_computeTag_small(\n authKey: TArg<Uint8Array>,\n // AEAD trailer must already be the 16-byte length block:\n // 8-byte little-endian AAD length || 8-byte little-endian ciphertext length.\n lengths: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n): TRet<Uint8Array> {\n // RFC 8439 §2.8.1 / RFC 7539 §2.8.1 MAC input is\n // AAD || pad16(AAD) || ciphertext || pad16(ciphertext) || lengths.\n const res = [];\n const updatePadded2 = (msg: TArg<Uint8Array>) => {\n res.push(msg);\n const leftover = msg.length % 16;\n // RFC 8439 §2.8.1 / RFC 7539 §2.8.1: pad16(x) is empty for aligned\n // inputs, else 16-(len%16) zero bytes.\n if (leftover) res.push(new Uint8Array(16).slice(leftover));\n };\n if (AAD) updatePadded2(AAD);\n updatePadded2(ciphertext);\n res.push(lengths);\n return poly1305_small(concatBytes(...res), authKey);\n}\n\n/**\n * Incremental Poly1305 MAC state.\n * Prefer `poly1305()` for one-shot use.\n * @param key - 32-byte Poly1305 one-time key.\n * @example\n * Feeds one chunk into an incremental Poly1305 state with a fresh one-time key.\n *\n * ```ts\n * import { Poly1305 } from '@noble/ciphers/_poly1305.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const mac = new Poly1305(key);\n * mac.update(new Uint8Array([1, 2, 3]));\n * mac.digest();\n * ```\n */\nexport class Poly1305 implements IHash2 {\n readonly blockLen = 16;\n readonly outputLen = 16;\n private buffer = new Uint8Array(16);\n private r = new Uint16Array(10); // Allocating 1 array with .subarray() here is slower than 3\n private h = new Uint16Array(10);\n private pad = new Uint16Array(8);\n private pos = 0;\n protected finished = false;\n protected destroyed = false;\n\n // Can be speed-up using BigUint64Array, at the cost of complexity\n constructor(key: TArg<Uint8Array>) {\n key = copyBytes(abytes(key, 32, 'key'));\n const t0 = u8to16(key, 0);\n const t1 = u8to16(key, 2);\n const t2 = u8to16(key, 4);\n const t3 = u8to16(key, 6);\n const t4 = u8to16(key, 8);\n const t5 = u8to16(key, 10);\n const t6 = u8to16(key, 12);\n const t7 = u8to16(key, 14);\n\n // RFC 8439 §2.5.1 / RFC 7539 §2.5.1 clamp r before multiplication.\n // These masks unpack that clamped value into 13-bit limbs, while pad\n // keeps the raw s half for finalize().\n // {@link https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47 | poly1305-donna reference}\n this.r[0] = t0 & 0x1fff;\n this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;\n this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;\n this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;\n this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;\n this.r[5] = (t4 >>> 1) & 0x1ffe;\n this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;\n this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;\n this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;\n this.r[9] = (t7 >>> 5) & 0x007f;\n for (let i = 0; i < 8; i++) this.pad[i] = u8to16(key, 16 + 2 * i);\n }\n\n private process(data: TArg<Uint8Array>, offset: number, isLast = false) {\n // RFC 8439 §2.5 / §2.5.1 and RFC 7539 §2.5 / §2.5.1 add an extra high\n // bit to every full 16-byte block. The final partial block gets its\n // explicit `1` byte during digestInto(), so `hibit` stays zero there.\n const hibit = isLast ? 0 : 1 << 11;\n const { h, r } = this;\n const r0 = r[0];\n const r1 = r[1];\n const r2 = r[2];\n const r3 = r[3];\n const r4 = r[4];\n const r5 = r[5];\n const r6 = r[6];\n const r7 = r[7];\n const r8 = r[8];\n const r9 = r[9];\n\n const t0 = u8to16(data, offset + 0);\n const t1 = u8to16(data, offset + 2);\n const t2 = u8to16(data, offset + 4);\n const t3 = u8to16(data, offset + 6);\n const t4 = u8to16(data, offset + 8);\n const t5 = u8to16(data, offset + 10);\n const t6 = u8to16(data, offset + 12);\n const t7 = u8to16(data, offset + 14);\n\n let h0 = h[0] + (t0 & 0x1fff);\n let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);\n let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);\n let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);\n let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);\n let h5 = h[5] + ((t4 >>> 1) & 0x1fff);\n let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);\n let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);\n let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);\n let h9 = h[9] + ((t7 >>> 5) | hibit);\n\n let c = 0;\n\n let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);\n c = d0 >>> 13;\n d0 &= 0x1fff;\n d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);\n c += d0 >>> 13;\n d0 &= 0x1fff;\n\n let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);\n c = d1 >>> 13;\n d1 &= 0x1fff;\n d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);\n c += d1 >>> 13;\n d1 &= 0x1fff;\n\n let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);\n c = d2 >>> 13;\n d2 &= 0x1fff;\n d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);\n c += d2 >>> 13;\n d2 &= 0x1fff;\n\n let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);\n c = d3 >>> 13;\n d3 &= 0x1fff;\n d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);\n c += d3 >>> 13;\n d3 &= 0x1fff;\n\n let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;\n c = d4 >>> 13;\n d4 &= 0x1fff;\n d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);\n c += d4 >>> 13;\n d4 &= 0x1fff;\n\n let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;\n c = d5 >>> 13;\n d5 &= 0x1fff;\n d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);\n c += d5 >>> 13;\n d5 &= 0x1fff;\n\n let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;\n c = d6 >>> 13;\n d6 &= 0x1fff;\n d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);\n c += d6 >>> 13;\n d6 &= 0x1fff;\n\n let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;\n c = d7 >>> 13;\n d7 &= 0x1fff;\n d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);\n c += d7 >>> 13;\n d7 &= 0x1fff;\n\n let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;\n c = d8 >>> 13;\n d8 &= 0x1fff;\n d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);\n c += d8 >>> 13;\n d8 &= 0x1fff;\n\n let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;\n c = d9 >>> 13;\n d9 &= 0x1fff;\n d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;\n c += d9 >>> 13;\n d9 &= 0x1fff;\n\n c = ((c << 2) + c) | 0;\n c = (c + d0) | 0;\n d0 = c & 0x1fff;\n c = c >>> 13;\n d1 += c;\n\n h[0] = d0;\n h[1] = d1;\n h[2] = d2;\n h[3] = d3;\n h[4] = d4;\n h[5] = d5;\n h[6] = d6;\n h[7] = d7;\n h[8] = d8;\n h[9] = d9;\n }\n\n private finalize() {\n const { h, pad } = this;\n const g = new Uint16Array(10);\n let c = h[1] >>> 13;\n h[1] &= 0x1fff;\n for (let i = 2; i < 10; i++) {\n h[i] += c;\n c = h[i] >>> 13;\n h[i] &= 0x1fff;\n }\n h[0] += c * 5;\n c = h[0] >>> 13;\n h[0] &= 0x1fff;\n h[1] += c;\n c = h[1] >>> 13;\n h[1] &= 0x1fff;\n h[2] += c;\n\n // RFC 8439 §2.5 / RFC 7539 §2.5 reduce modulo 2^130-5 before repacking\n // to 16-bit words and adding the raw s half.\n g[0] = h[0] + 5;\n c = g[0] >>> 13;\n g[0] &= 0x1fff;\n for (let i = 1; i < 10; i++) {\n g[i] = h[i] + c;\n c = g[i] >>> 13;\n g[i] &= 0x1fff;\n }\n g[9] -= 1 << 13;\n\n let mask = (c ^ 1) - 1;\n for (let i = 0; i < 10; i++) g[i] &= mask;\n mask = ~mask;\n for (let i = 0; i < 10; i++) h[i] = (h[i] & mask) | g[i];\n h[0] = (h[0] | (h[1] << 13)) & 0xffff;\n h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;\n h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;\n h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;\n h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;\n h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;\n h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;\n h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;\n\n let f = h[0] + pad[0];\n h[0] = f & 0xffff;\n for (let i = 1; i < 8; i++) {\n f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;\n h[i] = f & 0xffff;\n }\n clean(g);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n data = copyBytes(data);\n const { buffer, blockLen } = this;\n const len = data.length;\n\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\n if (take === blockLen) {\n for (; blockLen <= len - pos; pos += blockLen) this.process(data, 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(buffer, 0, false);\n this.pos = 0;\n }\n }\n return this;\n }\n destroy(): void {\n // `aexists(this)` guards update/digest paths, so destroy must mark the instance unusable too.\n this.destroyed = true;\n clean(this.h, this.r, this.buffer, this.pad);\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n const { buffer, h } = this;\n let { pos } = this;\n if (pos) {\n // RFC 8439 §2.5 / RFC 7539 §2.5: the final short block appends a\n // single `0x01` byte and zero-fills the remaining bytes before the\n // last multiplication step.\n buffer[pos++] = 1;\n for (; pos < 16; pos++) buffer[pos] = 0;\n this.process(buffer, 0, true);\n }\n this.finalize();\n let opos = 0;\n for (let i = 0; i < 8; i++) {\n out[opos++] = h[i] >>> 0;\n out[opos++] = h[i] >>> 8;\n }\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy out before destroy() zeroes the internal buffer.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n}\n\n/** One-shot keyed hash helper with `.create()`. */\nexport type CHash = CMac<Poly1305>;\n\n/**\n * Poly1305 MAC from RFC 8439.\n * @param msg - Message bytes to authenticate.\n * @param key - 32-byte Poly1305 one-time key.\n * @returns 16-byte authentication tag.\n * @example\n * Authenticates one message with a one-shot Poly1305 call and a fresh key.\n *\n * ```ts\n * import { poly1305 } from '@noble/ciphers/_poly1305.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * poly1305(new Uint8Array(), key);\n * ```\n */\nexport const poly1305: TRet<CHash> = /* @__PURE__ */ wrapMacConstructor(\n 32,\n (key: TArg<Uint8Array>) => new Poly1305(key)\n);\n","/**\n * ChaCha stream cipher, released\n * in 2008. Developed after Salsa20, ChaCha aims to increase diffusion per round.\n * It was standardized in\n * {@link https://www.rfc-editor.org/rfc/rfc8439 | RFC 8439} and\n * is now used in TLS 1.3.\n *\n * {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | XChaCha20}\n * extended-nonce variant is also provided. Similar to XSalsa, it's safe to use with\n * randomly-generated nonces.\n *\n * Check out\n * {@link http://cr.yp.to/chacha/chacha-20080128.pdf | PDF},\n * {@link https://en.wikipedia.org/wiki/Salsa20 | wiki}, and\n * {@link https://cr.yp.to/chacha.html | website}.\n *\n * @module\n */\nimport { type XorPRG, createCipher, createPRG, rotl } from './_arx.ts';\nimport { poly1305 } from './_poly1305.ts';\nimport {\n type ARXCipher,\n type CipherWithOutput,\n type TArg,\n type TRet,\n type XorStream,\n abytes,\n clean,\n equalBytes,\n getOutput,\n swap8IfBE,\n swap32IfBE,\n u64Lengths,\n wrapCipher,\n} from './utils.ts';\n\n/**\n * ChaCha core function. It is implemented twice:\n * 1. Simple loop (chachaCore_small, hchacha_small)\n * 2. Unrolled loop (chachaCore, hchacha) - 4x faster, but larger & harder to read\n * The specific implementation is selected in `createCipher` below.\n */\n\n/** RFC 8439 §2.1 quarter round on words a, b, c, d. */\n// prettier-ignore\nfunction chachaQR(x: TArg<Uint32Array>, a: number, b: number, c: number, d: number) {\n x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 16);\n x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 12);\n x[a] = (x[a] + x[b]) | 0; x[d] = rotl(x[d] ^ x[a], 8);\n x[c] = (x[c] + x[d]) | 0; x[b] = rotl(x[b] ^ x[c], 7);\n}\n\n/** Repeated ChaCha double rounds; callers are expected to pass an even round count. */\nfunction chachaRound(x: TArg<Uint32Array>, rounds = 20) {\n for (let r = 0; r < rounds; r += 2) {\n // RFC 8439 §2.3 / §2.3.1 inner_block: four column rounds, then four diagonal rounds.\n chachaQR(x, 0, 4, 8, 12);\n chachaQR(x, 1, 5, 9, 13);\n chachaQR(x, 2, 6, 10, 14);\n chachaQR(x, 3, 7, 11, 15);\n chachaQR(x, 0, 5, 10, 15);\n chachaQR(x, 1, 6, 11, 12);\n chachaQR(x, 2, 7, 8, 13);\n chachaQR(x, 3, 4, 9, 14);\n }\n}\n\n// Shared scratch for the auditability-only helper below; only the test-only\n// __TESTS.chachaCore_small hook reaches it, so production exports stay reentrant.\nconst ctmp = /* @__PURE__ */ new Uint32Array(16);\n\n/** Small version of chacha without loop unrolling. Unused, provided for auditability. */\n// prettier-ignore\nfunction chacha(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, i: TArg<Uint32Array>, out: TArg<Uint32Array>,\n isHChacha: boolean = true, rounds: number = 20\n): void {\n // `i` is either `[counter, nonce0, nonce1, nonce2]` for the ChaCha block\n // function or the full 128-bit nonce prefix for the HChaCha subkey path.\n // Create initial array using common pattern\n const y = Uint32Array.from([\n s[0], s[1], s[2], s[3], // \"expa\" \"nd 3\" \"2-by\" \"te k\"\n k[0], k[1], k[2], k[3], // Key Key Key Key\n k[4], k[5], k[6], k[7], // Key Key Key Key\n i[0], i[1], i[2], i[3], // Counter Counter Nonce Nonce\n ]);\n const x = ctmp;\n x.set(y);\n chachaRound(x, rounds);\n\n // HChaCha writes words 0..3 and 12..15 after the rounds; the ChaCha\n // block path adds the original state word-by-word.\n if (isHChacha) {\n const xindexes = [0, 1, 2, 3, 12, 13, 14, 15];\n for (let i = 0; i < 8; i++) out[i] = x[xindexes[i]];\n } else {\n for (let i = 0; i < 16; i++) out[i] = (y[i] + x[i]) | 0;\n }\n}\n\n/** Identical to `chachaCore`. Reached only through the test-only `__TESTS` export. */\n// @ts-ignore\nconst chachaCore_small: typeof chachaCore = (s, k, n, out, cnt, rounds) =>\n // Keep the reference wrapper on the same [counter, nonce0, nonce1, nonce2] layout as chacha().\n chacha(s, k, Uint32Array.from([cnt, n[0], n[1], n[2]]), out, false, rounds);\n/** Identical to `hchacha`. Unused. */\n// @ts-ignore\nconst hchacha_small: typeof hchacha = chacha;\n\n/** RFC 8439 §2.3 block core for `state = constants | key | counter | nonce`. */\n// prettier-ignore\nfunction chachaCore(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, n: TArg<Uint32Array>, out: TArg<Uint32Array>, cnt: number, rounds = 20\n): void {\n let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // \"expa\" \"nd 3\" \"2-by\" \"te k\"\n y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key\n y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key\n y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Nonce Nonce Nonce\n // Save state to temporary variables\n let x00 = y00, x01 = y01, x02 = y02, x03 = y03,\n x04 = y04, x05 = y05, x06 = y06, x07 = y07,\n x08 = y08, x09 = y09, x10 = y10, x11 = y11,\n x12 = y12, x13 = y13, x14 = y14, x15 = y15;\n for (let r = 0; r < rounds; r += 2) {\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);\n\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);\n\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 8);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);\n\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);\n\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);\n\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);\n\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);\n\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);\n }\n // RFC 8439 §2.3 / §2.3.1: add the original state words back in state order.\n let oi = 0;\n out[oi++] = (y00 + x00) | 0; out[oi++] = (y01 + x01) | 0;\n out[oi++] = (y02 + x02) | 0; out[oi++] = (y03 + x03) | 0;\n out[oi++] = (y04 + x04) | 0; out[oi++] = (y05 + x05) | 0;\n out[oi++] = (y06 + x06) | 0; out[oi++] = (y07 + x07) | 0;\n out[oi++] = (y08 + x08) | 0; out[oi++] = (y09 + x09) | 0;\n out[oi++] = (y10 + x10) | 0; out[oi++] = (y11 + x11) | 0;\n out[oi++] = (y12 + x12) | 0; out[oi++] = (y13 + x13) | 0;\n out[oi++] = (y14 + x14) | 0; out[oi++] = (y15 + x15) | 0;\n}\n/**\n * hchacha hashes key and nonce into key' and nonce' for xchacha20.\n * Algorithmically identical to `hchacha_small`, but this exported path\n * normalizes word order on big-endian hosts.\n * Need to find a way to merge it with `chachaCore` without 25% performance hit.\n * @param s - Sigma constants as 32-bit words.\n * @param k - Key words.\n * @param i - Nonce-prefix words.\n * @param out - Output buffer for the derived subkey.\n * @example\n * Derives the XChaCha subkey from sigma, key, and nonce-prefix words.\n *\n * ```ts\n * const sigma = new Uint32Array(4);\n * const key = new Uint32Array(8);\n * const nonce = new Uint32Array(4);\n * const out = new Uint32Array(8);\n * hchacha(sigma, key, nonce, out);\n * ```\n */\n// prettier-ignore\nexport function hchacha(\n s: TArg<Uint32Array>, k: TArg<Uint32Array>, i: TArg<Uint32Array>, out: TArg<Uint32Array>\n): void {\n let x00 = swap8IfBE(s[0]), x01 = swap8IfBE(s[1]), x02 = swap8IfBE(s[2]), x03 = swap8IfBE(s[3]),\n x04 = swap8IfBE(k[0]), x05 = swap8IfBE(k[1]), x06 = swap8IfBE(k[2]), x07 = swap8IfBE(k[3]),\n x08 = swap8IfBE(k[4]), x09 = swap8IfBE(k[5]), x10 = swap8IfBE(k[6]), x11 = swap8IfBE(k[7]),\n x12 = swap8IfBE(i[0]), x13 = swap8IfBE(i[1]), x14 = swap8IfBE(i[2]), x15 = swap8IfBE(i[3]);\n for (let r = 0; r < 20; r += 2) {\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 16);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 12);\n x00 = (x00 + x04) | 0; x12 = rotl(x12 ^ x00, 8);\n x08 = (x08 + x12) | 0; x04 = rotl(x04 ^ x08, 7);\n\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 16);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 12);\n x01 = (x01 + x05) | 0; x13 = rotl(x13 ^ x01, 8);\n x09 = (x09 + x13) | 0; x05 = rotl(x05 ^ x09, 7);\n\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 16);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 12);\n x02 = (x02 + x06) | 0; x14 = rotl(x14 ^ x02, 8);\n x10 = (x10 + x14) | 0; x06 = rotl(x06 ^ x10, 7);\n\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 16);\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 12);\n x03 = (x03 + x07) | 0; x15 = rotl(x15 ^ x03, 8)\n x11 = (x11 + x15) | 0; x07 = rotl(x07 ^ x11, 7);\n\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 16);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 12);\n x00 = (x00 + x05) | 0; x15 = rotl(x15 ^ x00, 8);\n x10 = (x10 + x15) | 0; x05 = rotl(x05 ^ x10, 7);\n\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 16);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 12);\n x01 = (x01 + x06) | 0; x12 = rotl(x12 ^ x01, 8);\n x11 = (x11 + x12) | 0; x06 = rotl(x06 ^ x11, 7);\n\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 16);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 12);\n x02 = (x02 + x07) | 0; x13 = rotl(x13 ^ x02, 8);\n x08 = (x08 + x13) | 0; x07 = rotl(x07 ^ x08, 7);\n\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 16)\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 12);\n x03 = (x03 + x04) | 0; x14 = rotl(x14 ^ x03, 8);\n x09 = (x09 + x14) | 0; x04 = rotl(x04 ^ x09, 7);\n }\n // HChaCha derives the subkey from state words 0..3 and 12..15 after 20 rounds.\n let oi = 0;\n out[oi++] = x00; out[oi++] = x01;\n out[oi++] = x02; out[oi++] = x03;\n out[oi++] = x12; out[oi++] = x13;\n out[oi++] = x14; out[oi++] = x15;\n swap32IfBE(out);\n}\n\n/**\n * Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.\n * The nonce/counter layout still reserves 8 counter bytes internally, but the shared public\n * `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`\n * near `MAX_COUNTER` for the full counter-policy rationale.\n * @param key - 16-byte or 32-byte key.\n * @param nonce - 8-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with the original 8-byte-nonce ChaCha variant and a fresh key/nonce.\n *\n * ```ts\n * import { chacha20orig } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(8);\n * chacha20orig(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha20orig: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n allowShortKeys: true,\n});\n/**\n * ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.\n * With smaller nonce, it's not safe to make it random (CSPRNG), due to collision chance.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with the RFC 8439 ChaCha20 stream cipher and a fresh key/nonce.\n *\n * ```ts\n * import { chacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha20(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha20: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n allowShortKeys: false,\n});\n\n/**\n * XChaCha eXtended-nonce ChaCha. With 24-byte nonce, it's safe to make it random (CSPRNG).\n * See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.\n * The nonce/counter layout still reserves 8 counter bytes internally, but the shared public\n * `counter` argument follows noble's strict non-wrapping 32-bit policy. See `src/_arx.ts`\n * near `MAX_COUNTER` for the full counter-policy rationale.\n * @param key - 32-byte key.\n * @param nonce - 24-byte extended nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Encrypts bytes with XChaCha20 using a fresh key and random 24-byte nonce.\n *\n * ```ts\n * import { xchacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(24);\n * xchacha20(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const xchacha20: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 8,\n extendNonceFn: hchacha,\n allowShortKeys: false,\n});\n\n/**\n * Reduced 8-round chacha, described in original paper.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Uses the reduced 8-round variant for non-critical workloads with a fresh key/nonce.\n *\n * ```ts\n * import { chacha8 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha8(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha8: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 8,\n});\n\n/**\n * Reduced 12-round chacha, described in original paper.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param data - Input bytes to xor with the keystream.\n * @param output - Optional destination buffer.\n * @param counter - Initial block counter.\n * @returns Encrypted or decrypted bytes.\n * @example\n * Uses the reduced 12-round variant for non-critical workloads with a fresh key/nonce.\n *\n * ```ts\n * import { chacha12 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * chacha12(key, nonce, new Uint8Array(4));\n * ```\n */\nexport const chacha12: TRet<XorStream> = /* @__PURE__ */ createCipher(chachaCore, {\n counterRight: false,\n counterLength: 4,\n rounds: 12,\n});\n\n// Test-only hooks for keeping the simple/reference core aligned with the unrolled production core.\nexport const __TESTS: {\n chachaCore_small: typeof chachaCore_small;\n chachaCore: typeof chachaCore;\n} = /* @__PURE__ */ Object.freeze({ chachaCore_small, chachaCore });\n\n// RFC 8439 §2.8.1 pad16(x): shared zero block for AAD/ciphertext padding.\nconst ZEROS16 = /* @__PURE__ */ new Uint8Array(16);\n// RFC 8439 §2.8 / §2.8.1: aligned inputs add nothing, otherwise append 16-(len%16) zero bytes.\nconst updatePadded = (h: ReturnType<typeof poly1305.create>, msg: TArg<Uint8Array>) => {\n h.update(msg);\n const leftover = msg.length % 16;\n if (leftover) h.update(ZEROS16.subarray(leftover));\n};\n\n// RFC 8439 §2.6.1 poly1305_key_gen returns `block[0..31]`, so AEAD key\n// generation only needs 32 zero bytes.\nconst ZEROS32 = /* @__PURE__ */ new Uint8Array(32);\nfunction computeTag(\n fn: TArg<XorStream>,\n key: TArg<Uint8Array>,\n nonce: TArg<Uint8Array>,\n ciphertext: TArg<Uint8Array>,\n AAD?: TArg<Uint8Array>\n): TRet<Uint8Array> {\n if (AAD !== undefined) abytes(AAD, undefined, 'AAD');\n // RFC 8439 §2.6 / §2.8: derive the Poly1305 one-time key from counter 0,\n // then MAC AAD || pad16(AAD) || ciphertext || pad16(ciphertext) || len(AAD) || len(ciphertext).\n const authKey = fn(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n ZEROS32 as TRet<Uint8Array>\n );\n const lengths = u64Lengths(ciphertext.length, AAD ? AAD.length : 0, true);\n\n // Methods below can be replaced with\n // return poly1305_computeTag_small(authKey, lengths, ciphertext, AAD)\n const h = poly1305.create(authKey);\n if (AAD) updatePadded(h, AAD);\n updatePadded(h, ciphertext);\n h.update(lengths);\n const res = h.digest();\n clean(authKey, lengths);\n return res;\n}\n\n/**\n * AEAD algorithm from RFC 8439.\n * Salsa20 and chacha (RFC 8439) use poly1305 differently.\n * We could have composed them, but it's hard because of authKey:\n * In salsa20, authKey changes position in salsa stream.\n * In chacha, authKey can't be computed inside computeTag, it modifies the counter.\n */\nexport const _poly1305_aead =\n (xorStream: TArg<XorStream>) =>\n (key: TArg<Uint8Array>, nonce: TArg<Uint8Array>, AAD?: TArg<Uint8Array>): CipherWithOutput => {\n // This borrows caller key/nonce/AAD buffers by reference; mutating them after construction\n // changes future encrypt/decrypt results.\n const tagLength = 16;\n return {\n encrypt(plaintext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array> {\n const plength = plaintext.length;\n output = getOutput(plength + tagLength, output, false);\n output.set(plaintext);\n const oPlain = output.subarray(0, -tagLength);\n // RFC 8439 §2.8: payload encryption starts at counter 1 because counter 0 produced the OTK.\n xorStream(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n oPlain as TRet<Uint8Array>,\n oPlain as TRet<Uint8Array>,\n 1\n );\n const tag = computeTag(xorStream, key, nonce, oPlain, AAD);\n output.set(tag, plength); // append tag\n clean(tag);\n return output as TRet<Uint8Array>;\n },\n decrypt(ciphertext: TArg<Uint8Array>, output?: TArg<Uint8Array>): TRet<Uint8Array> {\n output = getOutput(ciphertext.length - tagLength, output, false);\n const data = ciphertext.subarray(0, -tagLength);\n const passedTag = ciphertext.subarray(-tagLength);\n const tag = computeTag(xorStream, key, nonce, data, AAD);\n // RFC 8439 §2.8 / §4: authenticate ciphertext before decrypting it, and compare tags with\n // the constant-time equalBytes() helper rather than decrypting speculative plaintext first.\n if (!equalBytes(passedTag, tag)) {\n clean(tag);\n throw new Error('invalid tag');\n }\n output.set(ciphertext.subarray(0, -tagLength));\n // Actual decryption\n xorStream(\n key as TRet<Uint8Array>,\n nonce as TRet<Uint8Array>,\n output as TRet<Uint8Array>,\n output as TRet<Uint8Array>,\n 1\n ); // start stream with i=1\n clean(tag);\n return output as TRet<Uint8Array>;\n },\n };\n };\n\n/**\n * ChaCha20-Poly1305 from RFC 8439.\n *\n * Unsafe to use random nonces under the same key, due to collision chance.\n * Prefer XChaCha instead.\n * @param key - 32-byte key.\n * @param nonce - 12-byte nonce.\n * @param AAD - Additional authenticated data.\n * @returns AEAD cipher instance.\n * @example\n * Encrypts and authenticates plaintext with a fresh key and nonce.\n *\n * ```ts\n * import { chacha20poly1305 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(12);\n * const cipher = chacha20poly1305(key, nonce);\n * cipher.encrypt(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport const chacha20poly1305: TRet<ARXCipher> = /* @__PURE__ */ wrapCipher(\n { blockSize: 64, nonceLength: 12, tagLength: 16 },\n /* @__PURE__ */ _poly1305_aead(chacha20)\n);\n/**\n * XChaCha20-Poly1305 extended-nonce chacha.\n *\n * Can be safely used with random nonces (CSPRNG).\n * See {@link https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha | the IRTF draft}.\n * @param key - 32-byte key.\n * @param nonce - 24-byte nonce.\n * @param AAD - Additional authenticated data.\n * @returns AEAD cipher instance.\n * @example\n * Encrypts and authenticates plaintext with a fresh key and random 24-byte nonce.\n *\n * ```ts\n * import { xchacha20poly1305 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const key = randomBytes(32);\n * const nonce = randomBytes(24);\n * const cipher = xchacha20poly1305(key, nonce);\n * cipher.encrypt(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport const xchacha20poly1305: TRet<ARXCipher> = /* @__PURE__ */ wrapCipher(\n { blockSize: 64, nonceLength: 24, tagLength: 16 },\n /* @__PURE__ */ _poly1305_aead(xchacha20)\n);\n\n/**\n * Chacha20 CSPRNG (cryptographically secure pseudorandom number generator).\n * It's best to limit usage to non-production, non-critical cases: for example, test-only.\n * Compatible with libtomcrypt. It does not have a specification, so unclear how secure it is.\n * @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,\n * only 32 random bytes are mixed into the 40-byte state.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n * @example\n * Seeds the test-only ChaCha20 DRBG from fresh entropy.\n *\n * ```ts\n * import { rngChacha20 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const prg = rngChacha20(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const rngChacha20: TRet<XorPRG> = /* @__PURE__ */ createPRG(chacha20orig, 64, 32, 8);\n/**\n * Chacha20/8 CSPRNG (cryptographically secure pseudorandom number generator).\n * It's best to limit usage to non-production, non-critical cases: for example, test-only.\n * Faster than `rngChacha20`.\n * @param seed - Optional seed bytes mixed into the internal `key || nonce` state. When omitted,\n * only 32 random bytes are mixed into the 44-byte state.\n * @returns Seeded concrete `_XorStreamPRG` instance, including `clone()`.\n * @example\n * Seeds the faster test-only ChaCha8 DRBG from fresh entropy.\n *\n * ```ts\n * import { rngChacha8 } from '@noble/ciphers/chacha.js';\n * import { randomBytes } from '@noble/ciphers/utils.js';\n * const seed = randomBytes(32);\n * const prg = rngChacha8(seed);\n * prg.randomBytes(8);\n * ```\n */\nexport const rngChacha8: TRet<XorPRG> = /* @__PURE__ */ createPRG(chacha8, 64, 32, 12);\n","/**\n * core/field-encryption.ts — client-side field encryption (sdk-changes S6,\n * ARCHITECTURE §8.3).\n *\n * Fields the backend stores but never needs to read (mailbox `memo`, history\n * `memo` + `counterparty_nametag`, payment-request `memo`, and the intent\n * payload — E.3) are encrypted client-side so any of the owner's devices can\n * decrypt but the operator cannot.\n *\n * - **Key**: `HKDF-SHA256(ikm = walletPrivKey bytes, info = \"sphere-fieldenc-v1\")`\n * → 32-byte symmetric key. Deterministic per wallet → multi-device by\n * construction (mirrors token-engine/realization.ts — same @noble/hashes\n * HKDF, no salt).\n * - **Cipher/envelope**: XChaCha20-Poly1305 with a random 24-byte nonce; wire\n * form is EXACTLY `\"enc1.\" + base64(nonce ‖ ciphertext)`.\n * - **One mapping rule everywhere** (ARCHITECTURE §8.3): JSON bodies and\n * responses carry the `enc1.` string verbatim; the server validates only\n * prefix + base64 + size cap, stores the exact UTF-8 bytes, and returns them\n * unchanged. {@link assertFieldEnvelopeShape} is that server-side check.\n */\n\nimport { hkdf } from '@noble/hashes/hkdf.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { xchacha20poly1305 } from '@noble/ciphers/chacha.js';\nimport { randomBytes } from '@noble/ciphers/utils.js';\nimport { SphereError } from './errors';\n\n/** HKDF info label — versioned; the same label appears in sdk-changes S6. */\nexport const FIELD_ENCRYPTION_HKDF_INFO = 'sphere-fieldenc-v1';\n\n/** Envelope wire prefix. The version lives in the prefix (`enc1`). */\nexport const FIELD_ENVELOPE_PREFIX = 'enc1.';\n\n/** XChaCha20-Poly1305 nonce length (24 bytes). */\nexport const FIELD_ENVELOPE_NONCE_BYTES = 24;\n\n/** Poly1305 authentication tag length appended to the ciphertext. */\nconst POLY1305_TAG_BYTES = 16;\n\n/**\n * Default envelope size cap in bytes (UTF-8 length of the full `enc1.…`\n * string). Matches the intent-payload cap (≤ 4 KiB — ARCHITECTURE §7/§11).\n */\nexport const FIELD_ENVELOPE_MAX_BYTES = 4096;\n\n// =============================================================================\n// base64 (RFC 4648, with padding) — hand-rolled so the codec is identical in\n// Node and browsers (no Buffer / btoa dependency) and decode is strict.\n// =============================================================================\n\nconst B64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst B64_LOOKUP: Record<string, number> = {};\nfor (let i = 0; i < B64_ALPHABET.length; i++) B64_LOOKUP[B64_ALPHABET[i]] = i;\n\nfunction base64Encode(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i];\n const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;\n const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;\n out += B64_ALPHABET[b0 >> 2];\n out += B64_ALPHABET[((b0 & 0x03) << 4) | (b1 >> 4)];\n out += i + 1 < bytes.length ? B64_ALPHABET[((b1 & 0x0f) << 2) | (b2 >> 6)] : '=';\n out += i + 2 < bytes.length ? B64_ALPHABET[b2 & 0x3f] : '=';\n }\n return out;\n}\n\n/** Strict decode: throws on non-alphabet chars, bad length, or bad padding. */\nfunction base64Decode(s: string): Uint8Array {\n if (s.length % 4 !== 0) {\n throw new SphereError('Invalid field envelope: base64 length not a multiple of 4', 'DECRYPTION_ERROR');\n }\n let padding = 0;\n if (s.endsWith('==')) padding = 2;\n else if (s.endsWith('=')) padding = 1;\n const body = s.slice(0, s.length - padding);\n if (body.includes('=')) {\n throw new SphereError('Invalid field envelope: misplaced base64 padding', 'DECRYPTION_ERROR');\n }\n const out = new Uint8Array((s.length / 4) * 3 - padding);\n let o = 0;\n for (let i = 0; i < body.length; i += 4) {\n const n0 = B64_LOOKUP[body[i]];\n const n1 = B64_LOOKUP[body[i + 1]];\n const n2 = body[i + 2] !== undefined ? B64_LOOKUP[body[i + 2]] : 0;\n const n3 = body[i + 3] !== undefined ? B64_LOOKUP[body[i + 3]] : 0;\n if (n0 === undefined || n1 === undefined || n2 === undefined || n3 === undefined) {\n throw new SphereError('Invalid field envelope: non-base64 character', 'DECRYPTION_ERROR');\n }\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n if (o < out.length) out[o++] = (triple >> 16) & 0xff;\n if (o < out.length) out[o++] = (triple >> 8) & 0xff;\n if (o < out.length) out[o++] = triple & 0xff;\n }\n return out;\n}\n\n// =============================================================================\n// Key derivation\n// =============================================================================\n\nfunction hexToBytesStrict(hex: string): Uint8Array {\n if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) {\n throw new SphereError('Invalid private key hex for field-encryption key derivation', 'INVALID_CONFIG');\n }\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\n/**\n * Derive the wallet's 32-byte field-encryption key (sdk-changes S6):\n * `HKDF-SHA256(ikm = wallet privkey bytes, info = \"sphere-fieldenc-v1\")`.\n *\n * Deterministic per wallet key — every device holding the seed derives the\n * same key, so envelopes round-trip across devices with no key exchange.\n *\n * @param privKeyHex - wallet secp256k1 private key, hex (the HKDF ikm is its raw bytes)\n */\nexport function deriveFieldEncryptionKey(privKeyHex: string): Uint8Array {\n const ikm = hexToBytesStrict(privKeyHex);\n const info = new TextEncoder().encode(FIELD_ENCRYPTION_HKDF_INFO);\n return hkdf(sha256, ikm, undefined, info, 32);\n}\n\n// =============================================================================\n// Envelope encrypt / decrypt\n// =============================================================================\n\n/**\n * Encrypt a plaintext field into the S6 wire envelope:\n * `\"enc1.\" + base64(nonce ‖ ciphertext)` (XChaCha20-Poly1305, random 24-byte nonce).\n */\nexport function encryptField(key: Uint8Array, plaintext: string): string {\n const nonce = randomBytes(FIELD_ENVELOPE_NONCE_BYTES);\n const ciphertext = xchacha20poly1305(key, nonce).encrypt(new TextEncoder().encode(plaintext));\n const packed = new Uint8Array(nonce.length + ciphertext.length);\n packed.set(nonce, 0);\n packed.set(ciphertext, nonce.length);\n return FIELD_ENVELOPE_PREFIX + base64Encode(packed);\n}\n\n/**\n * Decrypt an S6 wire envelope. Validates the prefix and shape, then opens the\n * XChaCha20-Poly1305 box; any tampering (nonce, ciphertext, or tag) throws a\n * `SphereError('DECRYPTION_ERROR')`.\n */\nexport function decryptField(key: Uint8Array, envelope: string): string {\n if (typeof envelope !== 'string' || !envelope.startsWith(FIELD_ENVELOPE_PREFIX)) {\n throw new SphereError('Invalid field envelope: missing \"enc1.\" prefix', 'DECRYPTION_ERROR');\n }\n const packed = base64Decode(envelope.slice(FIELD_ENVELOPE_PREFIX.length));\n if (packed.length < FIELD_ENVELOPE_NONCE_BYTES + POLY1305_TAG_BYTES) {\n throw new SphereError('Invalid field envelope: too short for nonce + tag', 'DECRYPTION_ERROR');\n }\n const nonce = packed.slice(0, FIELD_ENVELOPE_NONCE_BYTES);\n const ciphertext = packed.slice(FIELD_ENVELOPE_NONCE_BYTES);\n try {\n const plain = xchacha20poly1305(key, nonce).decrypt(ciphertext);\n return new TextDecoder().decode(plain);\n } catch (err) {\n throw new SphereError('Field envelope authentication failed (tampered or wrong key)', 'DECRYPTION_ERROR', err);\n }\n}\n\n/**\n * The server-side shape check (ARCHITECTURE §8.3): the value must be a string,\n * carry the `enc1.` prefix, decode as base64, be long enough to hold a nonce +\n * tag, and fit the size cap. The server never sees plaintext — this is the\n * ONLY validation it performs before storing the exact UTF-8 bytes verbatim.\n *\n * Throws `SphereError('VALIDATION_ERROR')` on any violation.\n */\nexport function assertFieldEnvelopeShape(value: unknown, maxBytes: number = FIELD_ENVELOPE_MAX_BYTES): void {\n if (typeof value !== 'string') {\n throw new SphereError('Field envelope must be a string', 'VALIDATION_ERROR');\n }\n if (!value.startsWith(FIELD_ENVELOPE_PREFIX)) {\n throw new SphereError('Field envelope must start with \"enc1.\"', 'VALIDATION_ERROR');\n }\n if (new TextEncoder().encode(value).length > maxBytes) {\n throw new SphereError(`Field envelope exceeds size cap of ${maxBytes} bytes`, 'VALIDATION_ERROR');\n }\n let packed: Uint8Array;\n try {\n packed = base64Decode(value.slice(FIELD_ENVELOPE_PREFIX.length));\n } catch (err) {\n throw new SphereError('Field envelope payload is not valid base64', 'VALIDATION_ERROR', err);\n }\n if (packed.length < FIELD_ENVELOPE_NONCE_BYTES + POLY1305_TAG_BYTES) {\n throw new SphereError('Field envelope payload too short for nonce + tag', 'VALIDATION_ERROR');\n }\n}\n","/**\n * WalletApiMailboxProvider — the reference `DeliveryProvider` implementation\n * over the wallet-api mailbox (sdk-changes S3/S7; ARCHITECTURE §6/§16).\n *\n * Platform-neutral over the injected {@link WalletApiClient}. Key behaviors:\n *\n * - **deliver** = sha256 + upload via upload-urls (a `412` from the\n * content-addressed store means the blob is already present = success,\n * §5.2) → `POST /v1/mailbox` (idempotent by content-derived `entry_id`,\n * §6). The memo is encrypted client-side with the S6 field key before it\n * leaves the device; the deposit's `entryId` is verified to equal the\n * locally computed content-derived id — a backend substituting row ids is a\n * protocol violation (covenant §3.1-4).\n * - **incoming** = `GET /v1/mailbox?since=` paging (`more` loops), yielding\n * claimable entries (pending; claimed/rejected entries are recorded as seen\n * and skipped — they were resolved here or on another of the owner's\n * devices). `fetchBlob` uses the entry's `getUrl` and re-derives\n * (tokenId, stateHash, deliveryId) from the actual bytes — the recipient\n * never trusts the backend (§8.2). `blobCollected` entries throw a typed\n * error.\n * - **ack** → claim with the provider's **composition-time custody**\n * (`'inventory'` → `intoInventory: true` handoff; `'external'` →\n * `intoInventory: false`, ZERO inventory writes — §6 delivery-only claim),\n * or reject (terminal for discovery only — §6).\n * - **Persistent seen-set**: every acked delivery's content-derived id — the\n * canonical hash of its `(tokenId, stateHash)` pair — is persisted via the\n * injected {@link KeyValueStore}; a replayed delivery (server replay, cursor\n * reset, restore) is never yielded again (S7 port contract).\n */\n\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport type {\n DeliverOptions,\n DeliveryCustody,\n DeliveryDisposition,\n DeliveryProvider,\n DeliveryReceipt,\n IncomingDelivery,\n} from '../../../transport/delivery-provider';\nimport { composeDeliveryKeys, computeDeliveryId } from '../../../transport/delivery-provider';\nimport { unwrapTokenBlobBytes } from '../../../token-engine/token-blob';\nimport { deriveFieldEncryptionKey, decryptField, encryptField } from '../../../core/field-encryption';\nimport { WalletApiClient, WalletApiError } from '../../../wallet-api';\nimport type { KeyValueStore, MailboxEntry } from '../../../wallet-api';\nimport { logger } from '../../../core/logger';\n\nexport interface WalletApiMailboxProviderConfig {\n /** The authenticated wallet-api client (S1). DI — never a singleton. */\n client: WalletApiClient;\n /**\n * Composition-time custody (S7 — NEVER a per-call flag): `'inventory'` for\n * the full wallet-api preset (acks hand tokens into the server inventory),\n * `'external'` for the own-storage preset (acks perform zero inventory\n * writes; the app's storage keeps custody).\n */\n custody: DeliveryCustody;\n /** Persists the per-identity seen-set + mailbox cursor. */\n stateStore: KeyValueStore;\n /**\n * The backend-true (tokenId, stateHash) derivation (`ITokenEngine.deliveryKeys`).\n * Optional at construction — compositions are engine-less; `PaymentsModule`\n * late-binds it via `bindDeliveryKeys` at init. The provider never derives\n * these locally (§8.2 / sdk-changes S7) and fails loudly if unbound.\n */\n deliveryKeys?: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nconst TAG = 'WalletApiMailbox';\n\nexport class WalletApiMailboxProvider implements DeliveryProvider {\n readonly custody: DeliveryCustody;\n\n private readonly client: WalletApiClient;\n private readonly stateStore: KeyValueStore;\n private deriveKeysFn: ((blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>) | null = null;\n\n private identity: { privateKey: string; chainPubkey: string } | null = null;\n private fieldKey: Uint8Array | null = null;\n\n constructor(config: WalletApiMailboxProviderConfig) {\n this.client = config.client;\n this.custody = config.custody;\n this.stateStore = config.stateStore;\n this.deriveKeysFn = config.deliveryKeys ?? null;\n }\n\n /** @inheritDoc — wired by PaymentsModule at init (engine-owning seam). */\n bindDeliveryKeys(derive: (blobBytes: Uint8Array) => Promise<{ tokenId: string; stateHash: string }>): void {\n this.deriveKeysFn = derive;\n }\n\n private deriveKeys(blobBytes: Uint8Array): Promise<{ tokenId: string; stateHash: string }> {\n if (this.deriveKeysFn === null) {\n throw new WalletApiError(\n 'deliveryKeys derivation not bound — PaymentsModule binds ITokenEngine.deliveryKeys at init (S7)',\n 'PROTOCOL'\n );\n }\n return this.deriveKeysFn(blobBytes);\n }\n\n /** Bind the wallet identity (derives the S6 field key; binds the client). */\n setIdentity(identity: { privateKey: string; chainPubkey: string }): void {\n this.identity = identity;\n this.fieldKey = deriveFieldEncryptionKey(identity.privateKey);\n this.client.setIdentity(identity);\n }\n\n // ── persisted state (per network + identity) ────────────────────────────────\n\n private stateKey(kind: string): string {\n if (!this.identity) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n return `wallet-api-mailbox:${kind}:${this.client.network}:${this.identity.chainPubkey}`;\n }\n\n /**\n * The persistent seen-set (S7): content-derived delivery ids — i.e. the\n * canonical `SHA-256(tokenId ‖ stateHash)` encoding of the (tokenId,\n * stateHash) pairs this wallet has already resolved.\n */\n private async readSeen(): Promise<Set<string>> {\n const raw = await this.stateStore.get(this.stateKey('seen'));\n if (!raw) return new Set();\n try {\n return new Set(JSON.parse(raw) as string[]);\n } catch {\n return new Set();\n }\n }\n\n private async addSeen(deliveryIds: string[]): Promise<void> {\n if (deliveryIds.length === 0) return;\n const seen = await this.readSeen();\n for (const id of deliveryIds) seen.add(id);\n await this.stateStore.set(this.stateKey('seen'), JSON.stringify([...seen]));\n }\n\n private async readCursorState(): Promise<{ cursor: bigint; syncEpoch: bigint } | null> {\n const raw = await this.stateStore.get(this.stateKey('cursor'));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as { cursor?: string; syncEpoch?: string };\n if (typeof parsed.cursor !== 'string' || typeof parsed.syncEpoch !== 'string') return null;\n return { cursor: BigInt(parsed.cursor), syncEpoch: BigInt(parsed.syncEpoch) };\n } catch {\n return null;\n }\n }\n\n private async persistCursorState(cursor: bigint, syncEpoch: bigint): Promise<void> {\n await this.stateStore.set(\n this.stateKey('cursor'),\n JSON.stringify({ cursor: cursor.toString(), syncEpoch: syncEpoch.toString() })\n );\n }\n\n // ── deliver (S3: upload → deposit) ──────────────────────────────────────────\n\n /**\n * Idempotent per (token, state): the upload is content-addressed (412 =\n * already present = success — §5.2) and the deposit is idempotent by the\n * content-derived entry_id — it succeeds even after the recipient claimed\n * (§6), which is what makes the sender's journal replay safe.\n */\n async deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt> {\n if (!this.fieldKey) {\n throw new WalletApiError('No identity set — call setIdentity() first', 'CONFIG');\n }\n const keys = composeDeliveryKeys(await this.deriveKeys(blob));\n\n // §5.2/§8.2: the wallet-api wire carries the RAW token bytes — the\n // cross-port blob argument is the sphere envelope; unwrap at the boundary\n // (the real backend content-addresses and Token.fromCBOR's exactly these\n // bytes; the 39051 envelope never crosses the §16 API).\n const wire = unwrapTokenBlobBytes(blob);\n\n // Upload the finished blob (checksum-bound presigned PUT — §5.2).\n const sha = bytesToHex(sha256(wire));\n const urls = await this.client.getUploadUrls([{ sha256: sha, size: wire.length }]);\n const upload = urls.find((u) => u.sha256 === sha);\n if (!upload) {\n throw new WalletApiError(`upload-urls response missing sha256 ${sha}`, 'PROTOCOL');\n }\n await this.client.uploadBlob(upload.putUrl, wire); // 412 = already present = success (§5.2)\n\n // Deposit (idempotent by entry_id — §6). The memo is S6-encrypted BEFORE\n // it leaves the device (§8.3): the operator never sees plaintext.\n let entryId: string;\n try {\n entryId = await this.client.depositMailbox({\n recipientPubkey,\n key: upload.key,\n transferId: options.transferId,\n stateHash: keys.stateHash,\n tokenId: keys.tokenId,\n ...(options.memo !== undefined ? { memo: encryptField(this.fieldKey, options.memo) } : {}),\n });\n } catch (err) {\n // §6 CONFLICT = the backend already holds a record of EXACTLY this\n // (tokenId, stateHash) with different bytes. Reachable honestly in one\n // way: a resume re-derives the byte-identical TRANSACTION (E.1), but\n // the aggregator RECOMPUTES inclusion proofs per request (st-sdk#126),\n // so once the tree has advanced the rebuilt token blob's proof bytes —\n // and its content-addressed key — differ from the originally-deposited\n // blob's. The state itself is identical and bound to this recipient by\n // its own predicate (§8.2), so the existing entry IS this delivery:\n // succeed idempotently with the content-derived id (the port contract —\n // deliver is idempotent per (token, state), S7). A foreign-spend\n // conflict can never reach here: the engine's E.2 match-verify raises\n // TransferConflictError before anything is delivered.\n if (err instanceof WalletApiError && err.status === 409) {\n return { deliveryId: keys.deliveryId };\n }\n throw err;\n }\n\n // covenant §3.1-4: the id is CONTENT-DERIVED — computed client-side; a\n // backend returning anything else (e.g. a row id) is a protocol violation.\n if (entryId !== keys.deliveryId) {\n throw new WalletApiError(\n `mailbox deposit returned a non-content-derived entryId (got ${entryId}, expected ${keys.deliveryId})`,\n 'PROTOCOL'\n );\n }\n return { deliveryId: keys.deliveryId };\n }\n\n // ── incoming (S3: mailbox pull) ─────────────────────────────────────────────\n\n /**\n * Pull entries since the given cursor (or the persisted one), loop while\n * `more`, and yield claimable deliveries not yet in the seen-set. The\n * persisted cursor advances to the server's read pointer (§6) — entries at\n * or below it are all resolved; the seen-set (not the cursor) is the replay\n * guard, so a cursor reset is safe.\n */\n async *incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery> {\n const persisted = await this.readCursorState();\n let since: bigint;\n if (sinceCursor !== undefined) {\n since = BigInt(sinceCursor);\n } else {\n since = persisted?.cursor ?? 0n;\n }\n\n const seen = await this.readSeen();\n let page = await this.client.listMailbox(since);\n // §5.4: a syncEpoch change (server restore) invalidates cursor continuity —\n // discard the persisted cursor and re-pull from the start; the seen-set\n // absorbs the replays.\n if (persisted !== null && page.syncEpoch !== persisted.syncEpoch && sinceCursor === undefined) {\n page = await this.client.listMailbox(0n);\n }\n\n for (;;) {\n for (const entry of page.entries) {\n if (seen.has(entry.entryId)) continue; // persistent replay guard (S7)\n if (entry.status !== 'unclaimed') {\n // Resolved here earlier or on another of the owner's devices —\n // record and skip (claimed/rejected filtering for the consumer).\n seen.add(entry.entryId);\n await this.addSeen([entry.entryId]);\n continue;\n }\n yield this.toIncomingDelivery(entry);\n }\n await this.persistCursorState(page.readPointer, page.syncEpoch);\n if (!page.more) break;\n const lastSeq = page.entries.length > 0 ? page.entries[page.entries.length - 1].seq : since;\n page = await this.client.listMailbox(lastSeq);\n }\n }\n\n private toIncomingDelivery(entry: MailboxEntry): IncomingDelivery {\n const client = this.client;\n const fieldKey = this.fieldKey;\n const deriveKeys = (bytes: Uint8Array): Promise<{ tokenId: string; stateHash: string }> => this.deriveKeys(bytes);\n\n // S6: decrypt the memo envelope. NOTE: the S6 key is wallet-scoped (HKDF\n // from the wallet key), so only memos written by THIS wallet's key\n // decrypt (self-sends / multi-device). A counterparty's memo fails\n // authentication and is surfaced as absent rather than as ciphertext.\n let memo: string | undefined;\n if (entry.memo !== undefined && fieldKey) {\n try {\n memo = decryptField(fieldKey, entry.memo);\n } catch {\n logger.debug(TAG, `memo for entry ${entry.entryId.slice(0, 12)}… not decryptable with this wallet's key`);\n }\n }\n\n return {\n deliveryId: entry.entryId,\n transferId: entry.transferId,\n senderPubkey: entry.senderPubkey,\n ...(memo !== undefined ? { memo } : {}),\n cursor: entry.seq.toString(),\n async fetchBlob(): Promise<Uint8Array> {\n if (entry.blobCollected || !entry.getUrl) {\n // §6: only RESOLVED entries lose their blob after retention; a\n // pending entry's blob is always retained — treat this as a server\n // contract violation rather than silently skipping value.\n throw new WalletApiError(\n `mailbox entry ${entry.entryId} has no fetchable blob (blobCollected)`,\n 'PROTOCOL'\n );\n }\n const bytes = await client.fetchBlob(entry.getUrl);\n // §8.2: the recipient never trusts the backend — re-derive the\n // content-derived id from the ACTUAL bytes and match it.\n const keys = composeDeliveryKeys(await deriveKeys(bytes));\n if (keys.deliveryId !== entry.entryId) {\n throw new WalletApiError(\n `mailbox blob does not match its entry id (expected ${entry.entryId}, bytes derive ${keys.deliveryId})`,\n 'PROTOCOL'\n );\n }\n return bytes;\n },\n };\n }\n\n // ── ack (claim / reject) ────────────────────────────────────────────────────\n\n async ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void> {\n if (disposition === 'claimed') {\n // Custody is the provider's COMPOSITION-TIME property (S7): the\n // own-storage preset bakes 'external' in, so every claim here sends\n // intoInventory:false — zero inventory writes (§6 delivery-only claim) —\n // even when ack is called with no thought given to options.\n const intoInventory = this.custody === 'inventory';\n const result = await this.client.claimMailbox([deliveryId], intoInventory);\n const failed = result.failed.find((f) => f.entryId === deliveryId);\n if (failed) {\n // §16: the defensive lineage bucket — unreachable in normal flows.\n throw new WalletApiError(\n `mailbox claim failed for ${deliveryId}: ${failed.code}`,\n 'CONFLICT'\n );\n }\n // claimed / alreadyClaimed (idempotent — possibly via another device).\n } else {\n // §6: terminal for discovery only — the entry stays claimable and its\n // blob retained (retryable after e.g. a trustbase fix).\n await this.client.rejectMailbox([deliveryId]);\n }\n await this.addSeen([deliveryId]); // the persistent (tokenId, stateHash) seen-set (S7)\n }\n\n // ── wake (optional hook — §9: a nudge, never a correctness dependency) ──────\n\n onWake(callback: () => void): () => void {\n let closed = false;\n let close: (() => void) | null = null;\n void this.client\n .connectWakeSocket((wake) => {\n if (wake.stream === 'mailbox') callback();\n })\n .then((handle) => {\n if (closed) handle.close();\n else close = () => handle.close();\n })\n .catch((err) => {\n // Wakes are best-effort; polling remains the correctness path (§9).\n logger.debug(TAG, 'wake socket unavailable (polling continues):', err);\n });\n return () => {\n closed = true;\n close?.();\n };\n }\n}\n\nexport function createWalletApiMailboxProvider(\n config: WalletApiMailboxProviderConfig\n): WalletApiMailboxProvider {\n return new WalletApiMailboxProvider(config);\n}\n","/**\n * impl/shared/wallet-api/composition.ts — port composition (sdk-changes S4/S7,\n * covenant §3.1-6).\n *\n * The Sphere frontend is a VIEW: all I/O sits behind three independently\n * swappable ports — storage (`TokenStorageProvider`), delivery\n * (`DeliveryProvider`) and the engine/aggregator config (`OracleProvider`) —\n * selected at composition time. wallet-api ships as ONE implementation of each\n * port, not as the port:\n *\n * - {@link createSphereProviders} — the composition root: take any platform\n * base bundle (`createBrowserProviders` / `createNodeProviders`) and select\n * each port independently.\n * - {@link createWalletApiProviders} — the FULL wallet-api preset: thin\n * storage (server inventory custody) + mailbox delivery with custody\n * `'inventory'`.\n * - {@link createOwnStorageWalletApiProviders} — the delivery-only preset:\n * the app's own (local) storage keeps custody; the mailbox provider is\n * constructed with custody `'external'`, so every ack sends\n * `intoInventory: false` — zero server inventory writes by construction\n * (ARCHITECTURE §6 delivery-only claim; never a per-call flag).\n *\n * Asset-transport routing (S4): composing a delivery port moves ASSETS to it;\n * messaging, group chat and nametag bindings stay on the Nostr transport in\n * the base bundle. Payment requests ride wallet-api whenever the `walletApi`\n * client these presets return is passed to `Sphere.init` — `PaymentsModule`\n * detects the §16 payment-request capability on it and does not install the\n * Nostr payment-request channel (sdk-changes S4).\n */\n\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../../../storage';\nimport type { TransportProvider } from '../../../transport';\nimport type { DeliveryProvider } from '../../../transport/delivery-provider';\nimport type { OracleProvider } from '../../../oracle';\nimport type { TokenBlob } from '../../../token-engine/types';\nimport { WalletApiClient } from '../../../wallet-api';\nimport type { FetchLike, KeyValueStore, WebSocketFactoryLike } from '../../../wallet-api';\nimport { WalletApiTokenStorageProvider } from './WalletApiTokenStorageProvider';\nimport { WalletApiMailboxProvider } from './WalletApiMailboxProvider';\n\n// =============================================================================\n// Composition root\n// =============================================================================\n\n/** The minimum any platform base bundle provides (browser/node factories do). */\nexport interface SphereBaseProviders {\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n tokenStorage: TokenStorageProvider<TxfStorageDataBase>;\n}\n\n/** Independent port selection (sdk-changes S7): any combination is legal. */\nexport interface SphereProviderPorts {\n /** Storage port override (token inventory + blob custody). */\n storage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Delivery port (assets move to it; messaging stays on the base transport). */\n delivery?: DeliveryProvider;\n /** Engine/aggregator config port override. */\n engine?: OracleProvider;\n}\n\n/**\n * Compose a Sphere provider bundle with each port selected independently\n * (covenant §3.1-6). Unselected ports keep the base bundle's implementation.\n */\nexport function createSphereProviders<B extends SphereBaseProviders>(\n base: B,\n ports: SphereProviderPorts = {}\n): B & { delivery?: DeliveryProvider } {\n return {\n ...base,\n ...(ports.storage ? { tokenStorage: ports.storage } : {}),\n ...(ports.engine ? { oracle: ports.engine } : {}),\n ...(ports.delivery ? { delivery: ports.delivery } : {}),\n };\n}\n\n// =============================================================================\n// wallet-api presets (S4)\n// =============================================================================\n\nexport interface WalletApiCompositionConfig {\n /** Backend base URL — https off-loopback (ARCHITECTURE §4, client-enforced). */\n baseUrl: string;\n /** Network name (e.g. 'testnet2') — required end-to-end (ARCHITECTURE §14). */\n network: string;\n /**\n * Stable per-device label (ARCHITECTURE §4 — one session row per (owner,\n * device); the refresh token is stored under it). Pass a persisted value;\n * when omitted a fresh random label is generated per construction, which\n * still works but starts every run with a challenge sign-in.\n */\n deviceId?: string;\n /** Reuse an existing client (tests / advanced wiring). */\n client?: WalletApiClient;\n /** Refresh-token / cursor / seen-set persistence; defaults to `base.storage`. */\n stateStore?: KeyValueStore;\n /** Injectable fetch (defaults to `globalThis.fetch`). */\n fetchFn?: FetchLike;\n /** Injectable WebSocket factory (defaults to `globalThis.WebSocket`). */\n webSocketFactory?: WebSocketFactoryLike;\n /** Local token verification for `recoverRemoved()` (wire the engine here). */\n verifyToken?: (blob: TokenBlob) => Promise<boolean>;\n}\n\n/** What the wallet-api presets add to the base bundle. */\nexport interface WalletApiProviderExtras {\n delivery: DeliveryProvider;\n /**\n * The S1 client — pass to `Sphere.init({ walletApi })` for the S4 auth\n * lifecycle. NOT optional in spirit (#515): wallet-api CUSTODY artifacts\n * (the thin storage provider, delivery custody `'inventory'`) without this\n * client are an illegal composition — `Sphere.init` /\n * `PaymentsModule.initialize` throw `INVALID_CONFIG` (fail-closed) instead\n * of silently degrading to local-custody semantics.\n */\n walletApi: WalletApiClient;\n}\n\nfunction buildClient(base: SphereBaseProviders, config: WalletApiCompositionConfig): {\n client: WalletApiClient;\n stateStore: KeyValueStore;\n} {\n // StorageProvider satisfies KeyValueStore structurally (get/set/remove);\n // all client/provider keys are self-namespaced per network + pubkey.\n const stateStore: KeyValueStore = config.stateStore ?? base.storage;\n const client =\n config.client ??\n new WalletApiClient({\n baseUrl: config.baseUrl,\n network: config.network,\n deviceId: config.deviceId ?? `sphere-${crypto.randomUUID()}`,\n storage: stateStore,\n fetchFn: config.fetchFn,\n webSocketFactory: config.webSocketFactory,\n });\n return { client, stateStore };\n}\n\n/**\n * The FULL wallet-api preset (S4): wallet-api keeps inventory custody —\n * thin/lazy storage over the server's value index + mailbox delivery with\n * custody `'inventory'` (claims perform the §6 ownership handoff).\n */\nexport function createWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & WalletApiProviderExtras {\n const { client, stateStore } = buildClient(base, config);\n const tokenStorage = new WalletApiTokenStorageProvider({\n client,\n stateStore,\n verifyToken: config.verifyToken,\n });\n const delivery = new WalletApiMailboxProvider({ client, custody: 'inventory', stateStore });\n return { ...base, tokenStorage, delivery, walletApi: client };\n}\n\n/**\n * The OWN-STORAGE preset (S7: own storage + wallet-api delivery — a\n * supported, tested composition): the base bundle's local storage keeps\n * custody; wallet-api is purely the delivery rail. Custody `'external'` is\n * baked in at construction — every ack sends `intoInventory: false`, so a\n * recipient's claim performs ZERO inventory writes even when `ack` is called\n * with no thought given to options (sdk-changes S7). Senders never call\n * apply; their sends close via `intents/{id}/complete` alone (ARCHITECTURE\n * §6 storage-opt-out senders), which the wallet-api client (passed as\n * `walletApi`) provides.\n */\nexport function createOwnStorageWalletApiProviders<B extends SphereBaseProviders>(\n base: B,\n config: WalletApiCompositionConfig\n): B & WalletApiProviderExtras {\n const { client, stateStore } = buildClient(base, config);\n const delivery = new WalletApiMailboxProvider({ client, custody: 'external', stateStore });\n return { ...base, delivery, walletApi: client };\n}\n","/**\n * Browser Download Utilities\n * Functions for downloading wallet backups as files\n */\n\nimport type { Sphere } from '../../core/Sphere';\nimport type { WalletJSON, WalletJSONExportOptions } from '../../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DownloadTextOptions {\n /** Password for encryption */\n password?: string;\n /** Number of addresses to include */\n addressCount?: number;\n /** Custom filename (without extension) */\n filename?: string;\n}\n\nexport interface DownloadJSONOptions extends WalletJSONExportOptions {\n /** Custom filename (without extension) */\n filename?: string;\n /** Pretty print JSON (default: true) */\n pretty?: boolean;\n}\n\n// =============================================================================\n// Core Download Function\n// =============================================================================\n\n/**\n * Download content as a file in the browser\n */\nexport function downloadFile(\n content: string | Blob,\n filename: string,\n mimeType: string = 'text/plain'\n): void {\n const blob = content instanceof Blob\n ? content\n : new Blob([content], { type: mimeType });\n\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n\n link.href = url;\n link.download = filename;\n link.click();\n\n // Clean up the URL after a short delay\n setTimeout(() => URL.revokeObjectURL(url), 100);\n}\n\n/**\n * Download text content as a .txt file\n */\nexport function downloadTextFile(content: string, filename: string): void {\n downloadFile(content, filename, 'text/plain');\n}\n\n/**\n * Download JSON content as a .json file\n */\nexport function downloadJSONFile(content: object | string, filename: string): void {\n const jsonString = typeof content === 'string'\n ? content\n : JSON.stringify(content, null, 2);\n downloadFile(jsonString, filename, 'application/json');\n}\n\n// =============================================================================\n// Wallet Download Functions\n// =============================================================================\n\n/**\n * Download wallet backup as text file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletText(sphere);\n *\n * // Download encrypted backup\n * downloadWalletText(sphere, { password: 'secret' });\n *\n * // Custom filename\n * downloadWalletText(sphere, { filename: 'my-backup' });\n * ```\n */\nexport function downloadWalletText(sphere: Sphere, options: DownloadTextOptions = {}): void {\n const content = sphere.exportToTxt({\n password: options.password,\n addressCount: options.addressCount,\n });\n\n const filename = options.filename\n ? `${options.filename}.txt`\n : `sphere-wallet-${Date.now()}.txt`;\n\n downloadTextFile(content, filename);\n}\n\n/**\n * Download wallet backup as JSON file\n *\n * @example\n * ```ts\n * // Download unencrypted backup\n * downloadWalletJSON(sphere);\n *\n * // Download encrypted backup\n * downloadWalletJSON(sphere, { password: 'secret' });\n *\n * // Include multiple addresses\n * downloadWalletJSON(sphere, { addressCount: 5 });\n * ```\n */\nexport function downloadWalletJSON(sphere: Sphere, options: DownloadJSONOptions = {}): void {\n const json = sphere.exportToJSON({\n password: options.password,\n addressCount: options.addressCount,\n includeMnemonic: options.includeMnemonic,\n });\n\n const filename = options.filename\n ? `${options.filename}.json`\n : `sphere-wallet-${Date.now()}.json`;\n\n const jsonString = options.pretty !== false\n ? JSON.stringify(json, null, 2)\n : JSON.stringify(json);\n\n downloadFile(jsonString, filename, 'application/json');\n}\n\n/**\n * Download pre-built WalletJSON as file\n */\nexport function downloadWalletJSONData(json: WalletJSON, filename?: string): void {\n const name = filename || `sphere-wallet-${Date.now()}.json`;\n downloadJSONFile(json, name.endsWith('.json') ? name : `${name}.json`);\n}\n\n// =============================================================================\n// File Reading Utilities\n// =============================================================================\n\n/**\n * Read a file as text\n */\nexport function readFileAsText(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\n/**\n * Read a file as ArrayBuffer (for binary files like .dat)\n */\nexport function readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsArrayBuffer(file);\n });\n}\n\n/**\n * Read a file as Uint8Array (for binary files like .dat)\n */\nexport async function readFileAsUint8Array(file: File): Promise<Uint8Array> {\n const buffer = await readFileAsArrayBuffer(file);\n return new Uint8Array(buffer);\n}\n","/**\n * Centralized network-config resolution + a startup consistency assertion.\n *\n * Principle: resolve a network's full configuration in ONE place, and refuse to\n * run a network whose configuration is provably broken (e.g. a null or mismatched\n * trust base would silently accept unverified tokens — a funds risk).\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport { getEmbeddedTrustBase } from './trustbase-loader';\nimport { SphereError } from '../../core/errors';\n\n/**\n * Known trust-base networkId per network. The embedded trust base for a network\n * MUST carry this id; a mismatch means the wrong trust base is wired to a network\n * (e.g. the testnet trust base under testnet2), which would verify tokens against\n * the wrong chain. mainnet/dev have no pinned id yet (mainnet is not onboarded).\n */\nconst EXPECTED_NETWORK_ID: Partial<Record<NetworkType, number>> = {\n // v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),\n // so both expect the testnet2 trust base (networkId 4).\n testnet: 4,\n testnet2: 4,\n};\n\nexport interface ResolvedNetworkConfig {\n readonly network: NetworkType;\n readonly config: NetworkConfig;\n readonly trustBase: unknown | null;\n readonly networkId: number | undefined;\n}\n\n/** Resolve a network's full config + embedded trust base in one place. */\nexport function resolveNetworkConfig(network: NetworkType): ResolvedNetworkConfig {\n const config = NETWORKS[network];\n const trustBase = getEmbeddedTrustBase(network);\n const networkId = (trustBase as { networkId?: number } | null)?.networkId;\n return { network, config, trustBase, networkId };\n}\n\n/**\n * Fail loud if a network is unsafe to run. Conservative on purpose: it rejects\n * only provably-broken configs, so currently-valid networks (testnet/testnet2/dev)\n * keep working while mainnet — which has no embedded trust base — is blocked until\n * onboarded (otherwise it would silently accept unverified tokens). The stricter\n * hygiene checks (e.g. no cross-network registry reuse) live in the per-network CI\n * test, not at runtime, so they can't break a valid deployment.\n */\nexport function assertNetworkConsistency(network: NetworkType): void {\n const { trustBase, networkId } = resolveNetworkConfig(network);\n if (trustBase == null) {\n throw new SphereError(\n `Network \"${network}\" has no embedded trust base — refusing to run (tokens would be unverified).`,\n 'INVALID_CONFIG',\n );\n }\n const expected = EXPECTED_NETWORK_ID[network];\n if (expected !== undefined && networkId !== expected) {\n throw new SphereError(\n `Network \"${network}\" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,\n 'INVALID_CONFIG',\n );\n }\n}\n","/**\n * 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';\nimport type { NetworkType } from '../../../constants';\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 network?: NetworkType,\n): Promise<{ keyPair: unknown; ipnsName: string }> {\n const { generateKeyPairFromSeed, peerIdFromPrivateKey } = await loadLibp2pModules();\n\n // Per-network IPNS: bake the network into the HKDF info so the same wallet key derives a\n // DISTINCT IPNS identity per network — prevents a v2 wallet re-pulling v1 tokens from the\n // old (shared) IPNS record. No network → legacy info (backward compatible).\n const info = network ? `${IPNS_HKDF_INFO}:${network}` : IPNS_HKDF_INFO;\n const derivedKey = deriveEd25519KeyMaterial(privateKeyHex, info);\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 network?: NetworkType,\n): Promise<string> {\n const { ipnsName } = await deriveIpnsIdentity(privateKeyHex, network);\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 /** IPNS context captured at save() time — ensures flush writes to the correct\n * IPNS record even if identity changes between save() and flush(). */\n capturedIpnsKeyPair: unknown | null = null;\n capturedIpnsName: string | null = null;\n\n get isEmpty(): boolean {\n return this.txfData === null;\n }\n\n clear(): void {\n this.txfData = null;\n this.capturedIpnsKeyPair = null;\n this.capturedIpnsName = 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 this.capturedIpnsKeyPair = other.capturedIpnsKeyPair;\n this.capturedIpnsName = other.capturedIpnsName;\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 InventoryView,\n ApplyDeltaAdded,\n ApplyDeltaOptions,\n} from '../../../storage';\nimport { WholeBlobInventoryAdapter } from '../../../storage';\nimport type { TokenBlob } from '../../../token-engine/types';\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 /** Stored config for createForAddress() cloning */\n private readonly _config: IpfsStorageConfig | undefined;\n private readonly _statePersistenceCtor: IpfsStatePersistence | undefined;\n\n constructor(\n config?: IpfsStorageConfig,\n statePersistence?: IpfsStatePersistence,\n ) {\n this._config = config;\n this._statePersistenceCtor = statePersistence;\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, this._config?.network);\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 logger.debug('IPFS-Storage', `shutdown: ipnsName=${this.ipnsName?.slice(0, 20)}..., pendingEmpty=${this.pendingBuffer.isEmpty}, capturedIpns=${this.pendingBuffer.capturedIpnsName?.slice(0, 20) ?? 'none'}`);\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 // Capture IPNS context NOW so the flush writes to the correct IPNS record\n // even if identity changes between save() and flush() (address switch).\n this.pendingBuffer.txfData = data;\n this.pendingBuffer.capturedIpnsKeyPair = this.ipnsKeyPair;\n this.pendingBuffer.capturedIpnsName = this.ipnsName;\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, overrideIpns?: { keyPair: unknown; name: string }): Promise<SaveResult> {\n const ipnsKeyPair = overrideIpns?.keyPair ?? this.ipnsKeyPair;\n const ipnsName = overrideIpns?.name ?? this.ipnsName;\n\n const metaAddr = (data as unknown as TxfStorageDataBase)?._meta?.address;\n logger.debug('IPFS-Storage', `_doSave: ipnsName=${ipnsName?.slice(0, 20)}..., override=${!!overrideIpns}, meta.address=${metaAddr?.slice(0, 20) ?? 'none'}`);\n\n if (!ipnsKeyPair || !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: 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 ipnsKeyPair,\n cid,\n newSeq,\n this.ipnsLifetimeMs,\n );\n\n // Publish to all gateways\n const publishResult = await this.httpClient.publishIpns(\n 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(ipnsName, {\n cid,\n sequence: newSeq,\n gateway: 'local',\n });\n this.cache.setContent(cid, updatedData as unknown as TxfStorageDataBase);\n this.cache.markIpnsFresh(ipnsName);\n\n // Persist state\n await this.statePersistence.save(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 // Use captured IPNS context from save() time — prevents writing to wrong\n // IPNS record if identity changed between save() and flush().\n const overrideIpns = active.capturedIpnsKeyPair && active.capturedIpnsName\n ? { keyPair: active.capturedIpnsKeyPair, name: active.capturedIpnsName }\n : undefined;\n const result = await this._doSave(baseData, overrideIpns);\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 // ── Lazy inventory port (sdk-changes S2) — default whole-blob adapter ──────\n\n /** Default lazy-port adapter over this store (sdk-changes S2). */\n private readonly inventoryAdapter = new WholeBlobInventoryAdapter<TData>(this);\n\n listInventory(since?: bigint): Promise<InventoryView> {\n return this.inventoryAdapter.listInventory(since);\n }\n\n getToken(tokenId: string): Promise<TokenBlob> {\n return this.inventoryAdapter.getToken(tokenId);\n }\n\n applyDelta(transferId: string, spent: string[], added: ApplyDeltaAdded[], opts?: ApplyDeltaOptions): Promise<void> {\n return this.inventoryAdapter.applyDelta(transferId, spent, added, opts);\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 * Create an independent instance for a different address.\n * Shares the same gateway/timeout config but has fresh IPNS state.\n */\n createForAddress(): IpfsStorageProvider<TData> {\n return new IpfsStorageProvider<TData>(this._config, this._statePersistenceCtor);\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 * Per-network cache keys. The registry cache MUST be namespaced by remoteUrl:\n * testnet and testnet2 ship DIFFERENT coinIds for the same symbol, so a single\n * global key let a stale testnet snapshot be served for a testnet2 request —\n * wrong/missing icons AND self-mint resolving the wrong-network coinId. Keying\n * by remoteUrl isolates the snapshots so a network can never read another's.\n */\n private cacheKeys(): { data: string; ts: string } {\n const base = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE;\n const baseTs = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS;\n // No remoteUrl configured (local-only usage): keep the legacy bare key.\n if (!this.remoteUrl) return { data: base, ts: baseTs };\n // Namespace by remoteUrl so testnet and testnet2 snapshots never collide.\n return { data: `${base}:${this.remoteUrl}`, ts: `${baseTs}:${this.remoteUrl}` };\n }\n\n /**\n * Load definitions from StorageProvider cache.\n * Only applies if cache exists and is fresh (within refreshIntervalMs).\n */\n private async loadFromCache(): Promise<boolean> {\n if (!this.storage) return false;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n const [cached, cachedTs] = await Promise.all([\n this.storage.get(dataKey),\n this.storage.get(tsKey),\n ]);\n\n if (!cached || !cachedTs) return false;\n\n const ts = parseInt(cachedTs, 10);\n if (isNaN(ts)) return false;\n\n // Check freshness\n const age = Date.now() - ts;\n if (age > this.refreshIntervalMs) return false;\n\n // Don't overwrite data from a more recent remote fetch\n if (this.lastRefreshAt > ts) return false;\n\n const data: unknown = JSON.parse(cached);\n if (!this.isValidDefinitionsArray(data)) return false;\n\n this.applyDefinitions(data as TokenDefinition[]);\n this.lastRefreshAt = ts;\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Save definitions to StorageProvider cache.\n */\n private async saveToCache(definitions: TokenDefinition[]): Promise<void> {\n if (!this.storage) return;\n\n try {\n const { data: dataKey, ts: tsKey } = this.cacheKeys();\n await Promise.all([\n this.storage.set(dataKey, JSON.stringify(definitions)),\n this.storage.set(tsKey, String(Date.now())),\n ]);\n } catch {\n // Cache save failure is non-critical\n }\n }\n\n // ===========================================================================\n // Remote Refresh\n // ===========================================================================\n\n /**\n * Apply an array of token definitions to the internal maps.\n * Clears existing data before applying.\n */\n private applyDefinitions(definitions: TokenDefinition[]): void {\n this.definitionsById.clear();\n this.definitionsBySymbol.clear();\n this.definitionsByName.clear();\n\n for (const def of definitions) {\n const idLower = def.id.toLowerCase();\n this.definitionsById.set(idLower, def);\n\n if (def.symbol) {\n this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);\n }\n\n this.definitionsByName.set(def.name.toLowerCase(), def);\n }\n }\n\n /**\n * Validate that data is an array of objects with 'id' field\n */\n private isValidDefinitionsArray(data: unknown): boolean {\n return Array.isArray(data) && data.every((item) => item && typeof item === 'object' && 'id' in item);\n }\n\n /**\n * Fetch token definitions from the remote URL and update the registry.\n * On success, also persists to StorageProvider cache.\n * Returns true on success, false on failure. On failure, existing data is preserved.\n * Concurrent calls are deduplicated — only one fetch runs at a time.\n */\n async refreshFromRemote(): Promise<boolean> {\n if (!this.remoteUrl) {\n return false;\n }\n\n // Deduplicate concurrent calls\n if (this.refreshPromise) {\n return this.refreshPromise;\n }\n\n this.refreshPromise = this.doRefresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async doRefresh(): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n let response: Response;\n try {\n response = await fetch(this.remoteUrl!, {\n headers: { Accept: 'application/json' },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n logger.warn('TokenRegistry', `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);\n return false;\n }\n\n const data: unknown = await response.json();\n\n if (!this.isValidDefinitionsArray(data)) {\n logger.warn('TokenRegistry', 'Remote data is not a valid token definitions array');\n return false;\n }\n\n const definitions = data as TokenDefinition[];\n this.applyDefinitions(definitions);\n this.lastRefreshAt = Date.now();\n\n // Persist to cache (fire-and-forget)\n this.saveToCache(definitions);\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn('TokenRegistry', `Remote refresh failed: ${message}`);\n return false;\n }\n }\n\n /**\n * Start periodic auto-refresh from the remote URL.\n * Does an immediate fetch, then repeats at the configured interval.\n */\n startAutoRefresh(intervalMs?: number): void {\n this.stopAutoRefresh();\n\n if (intervalMs !== undefined) {\n this.refreshIntervalMs = intervalMs;\n }\n\n // Immediate first fetch (fire-and-forget)\n this.refreshFromRemote();\n\n this.refreshTimer = setInterval(() => {\n this.refreshFromRemote();\n }, this.refreshIntervalMs);\n }\n\n /**\n * Stop periodic auto-refresh\n */\n stopAutoRefresh(): void {\n if (this.refreshTimer !== null) {\n clearInterval(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n\n /**\n * Timestamp of the last successful remote refresh (0 if never refreshed)\n */\n getLastRefreshAt(): number {\n return this.lastRefreshAt;\n }\n\n // ===========================================================================\n // Lookup Methods\n // ===========================================================================\n\n /**\n * Get token definition by hex coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined if not found\n */\n getDefinition(coinId: string): TokenDefinition | undefined {\n if (!coinId) return undefined;\n return this.definitionsById.get(coinId.toLowerCase());\n }\n\n /**\n * Get token definition by symbol (e.g., \"UCT\", \"BTC\")\n * @param symbol - Token symbol (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionBySymbol(symbol: string): TokenDefinition | undefined {\n if (!symbol) return undefined;\n return this.definitionsBySymbol.get(symbol.toUpperCase());\n }\n\n /**\n * Get token definition by name (e.g., \"bitcoin\", \"ethereum\")\n * @param name - Token name (case-insensitive)\n * @returns Token definition or undefined if not found\n */\n getDefinitionByName(name: string): TokenDefinition | undefined {\n if (!name) return undefined;\n return this.definitionsByName.get(name.toLowerCase());\n }\n\n /**\n * Get token symbol for a coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol (e.g., \"UCT\") or truncated ID if not found\n */\n getSymbol(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.symbol) {\n return def.symbol;\n }\n // Fallback: return first 6 chars of ID uppercased\n return coinId.slice(0, 6).toUpperCase();\n }\n\n /**\n * Get token name for a coin ID\n * @param coinId - 64-character hex string\n * @returns Name (e.g., \"Bitcoin\") or coin ID if not found\n */\n getName(coinId: string): string {\n const def = this.getDefinition(coinId);\n if (def?.name) {\n // Capitalize first letter\n return def.name.charAt(0).toUpperCase() + def.name.slice(1);\n }\n return coinId;\n }\n\n /**\n * Get decimal places for a coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0 if not found\n */\n getDecimals(coinId: string): number {\n const def = this.getDefinition(coinId);\n return def?.decimals ?? 0;\n }\n\n /**\n * Get icon URL for a coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG format over SVG\n * @returns Icon URL or null if not found\n */\n getIconUrl(coinId: string, preferPng = true): string | null {\n const def = this.getDefinition(coinId);\n if (!def?.icons || def.icons.length === 0) {\n return null;\n }\n\n if (preferPng) {\n const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes('.png'));\n if (pngIcon) return pngIcon.url;\n }\n\n return def.icons[0].url;\n }\n\n /**\n * Check if a coin ID is known in the registry\n * @param coinId - 64-character hex string\n * @returns true if the coin is in the registry\n */\n isKnown(coinId: string): boolean {\n return this.definitionsById.has(coinId.toLowerCase());\n }\n\n /**\n * Get all token definitions\n * @returns Array of all token definitions\n */\n getAllDefinitions(): TokenDefinition[] {\n return Array.from(this.definitionsById.values());\n }\n\n /**\n * Get all fungible token definitions\n * @returns Array of fungible token definitions\n */\n getFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'fungible');\n }\n\n /**\n * Get all non-fungible token definitions\n * @returns Array of non-fungible token definitions\n */\n getNonFungibleTokens(): TokenDefinition[] {\n return this.getAllDefinitions().filter((def) => def.assetKind === 'non-fungible');\n }\n\n /**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdBySymbol(symbol: string): string | undefined {\n const def = this.getDefinitionBySymbol(symbol);\n return def?.id;\n }\n\n /**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID hex string or undefined if not found\n */\n getCoinIdByName(name: string): string | undefined {\n const def = this.getDefinitionByName(name);\n return def?.id;\n }\n}\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Get token definition by coin ID\n * @param coinId - 64-character hex string\n * @returns Token definition or undefined\n */\nexport function getTokenDefinition(coinId: string): TokenDefinition | undefined {\n return TokenRegistry.getInstance().getDefinition(coinId);\n}\n\n/**\n * Get token symbol by coin ID\n * @param coinId - 64-character hex string\n * @returns Symbol or truncated ID\n */\nexport function getTokenSymbol(coinId: string): string {\n return TokenRegistry.getInstance().getSymbol(coinId);\n}\n\n/**\n * Get token name by coin ID\n * @param coinId - 64-character hex string\n * @returns Name or coin ID\n */\nexport function getTokenName(coinId: string): string {\n return TokenRegistry.getInstance().getName(coinId);\n}\n\n/**\n * Get token decimals by coin ID\n * @param coinId - 64-character hex string\n * @returns Decimals or 0\n */\nexport function getTokenDecimals(coinId: string): number {\n return TokenRegistry.getInstance().getDecimals(coinId);\n}\n\n/**\n * Get token icon URL by coin ID\n * @param coinId - 64-character hex string\n * @param preferPng - Prefer PNG over SVG\n * @returns Icon URL or null\n */\nexport function getTokenIconUrl(coinId: string, preferPng = true): string | null {\n return TokenRegistry.getInstance().getIconUrl(coinId, preferPng);\n}\n\n/**\n * Check if coin ID is in registry\n * @param coinId - 64-character hex string\n * @returns true if known\n */\nexport function isKnownToken(coinId: string): boolean {\n return TokenRegistry.getInstance().isKnown(coinId);\n}\n\n/**\n * Get coin ID by symbol\n * @param symbol - Token symbol (e.g., \"UCT\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdBySymbol(symbol: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdBySymbol(symbol);\n}\n\n/**\n * Get coin ID by name\n * @param name - Token name (e.g., \"bitcoin\")\n * @returns Coin ID or undefined\n */\nexport function getCoinIdByName(name: string): string | undefined {\n return TokenRegistry.getInstance().getCoinIdByName(name);\n}\n\n/**\n * Normalize a coin identifier to its canonical hash coinId.\n *\n * Accepts both symbolic names (\"BTC\", \"ETH\") and hash coinIds (64-char hex).\n * If the input is a short symbol, resolves it via the TokenRegistry.\n * Returns the input unchanged if it's already a hash coinId or unknown.\n *\n * @public\n *\n * @remarks\n * Heuristic: inputs matching `length <= 20 && /^[A-Za-z0-9]+$/` are treated\n * as symbolic names and looked up in the registry. Long inputs, hyphenated\n * inputs (\"TOKEN-123\"), or dotted inputs (\"BTC.CASH\") pass through unchanged.\n *\n * **Stability:** This function depends on `TokenRegistry` content. Adding a\n * new symbol that shadows a previously-unknown short alphanumeric coinId\n * changes normalization semantics retroactively. Consumers building stable\n * keys against this output should be aware that registry growth is a\n * non-breaking change *for unknown inputs* but a breaking change *for inputs\n * that newly resolve*.\n *\n * @param coinId - A symbolic name or hash coinId.\n * @returns The canonical hash coinId, or the original string if not resolvable.\n */\nexport function normalizeCoinId(coinId: string): string {\n // Short alphanumeric strings are likely symbolic names (BTC, ETH, USDU, etc.)\n if (coinId.length <= 20 && /^[A-Za-z0-9]+$/.test(coinId)) {\n const resolved = getCoinIdBySymbol(coinId);\n if (resolved) return resolved;\n }\n return coinId;\n}\n\n/**\n * Compare two coin identifiers for equality, normalizing both sides.\n *\n * Handles mixed-format comparisons: \"BTC\" vs hash coinId, or two hash coinIds.\n * Both values are normalized to hash coinIds via the TokenRegistry before comparison.\n *\n * @public\n *\n * @remarks\n * Reflexive byte-equality short-circuit comes first; otherwise both sides\n * are normalized via {@link normalizeCoinId} and compared. Inherits the\n * registry-dependence caveat from `normalizeCoinId`. Two distinct symbols\n * that both fail to resolve will compare unequal even if they're semantic\n * aliases — register them as aliases in `TokenRegistry` for matching.\n *\n * @param a - First coin identifier (symbol or hash coinId).\n * @param b - Second coin identifier (symbol or hash coinId).\n * @returns true if both resolve to the same canonical coinId.\n */\nexport function coinIdsMatch(a: string, b: string): boolean {\n if (a === b) return true;\n return normalizeCoinId(a) === normalizeCoinId(b);\n}\n","/**\n * Configuration Resolvers\n * Utility functions for resolving provider configurations with extend/override pattern\n */\n\nimport { NETWORKS, type NetworkType, type NetworkConfig } from '../../constants';\nimport type {\n BaseTransportConfig,\n BaseOracleConfig,\n BasePriceConfig,\n BaseMarketConfig,\n 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,\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":["Buffer","isLE","bytesToHex","hexToBytes","DB_NAME","DB_VERSION","connectionSeq","db","Buffer","Buffer","success","message","senderNametag","hexToBytes","bytesToHex","hexToBytes","hexToBytes","bytesToHex","isBytes","anumber","abytes","aexists","aoutput","clean","createView","abytes","isLE","anumber","createView","anumber","abytes","clean","abytes","clean","aexists","aoutput","abytes","clean","bytesToHex","hexToBytes","data","saveResult","createBrowserWebSocket","netConfig","Buffer"]}