@unicitylabs/sphere-sdk 0.2.3 → 0.3.0

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":["../../core/bech32.ts","../../l1/addressToScriptHash.ts","../../l1/network.ts","../../l1/index.ts","../../core/crypto.ts","../../l1/crypto.ts","../../l1/address.ts","../../l1/tx.ts","../../l1/vesting.ts","../../l1/vestingState.ts","../../l1/addressHelpers.ts","../../modules/payments/L1PaymentsModule.ts","../../modules/payments/TokenSplitCalculator.ts","../../modules/payments/TokenSplitExecutor.ts","../../modules/payments/NametagMinter.ts","../../constants.ts","../../types/txf.ts","../../serialization/txf-serializer.ts","../../registry/token-registry.testnet.json","../../registry/TokenRegistry.ts","../../modules/payments/InstantSplitExecutor.ts","../../modules/payments/InstantSplitProcessor.ts","../../types/instant-split.ts","../../modules/payments/PaymentsModule.ts","../../modules/payments/TokenRecoveryService.ts","../../modules/communications/CommunicationsModule.ts","../../core/encryption.ts","../../core/scan.ts","../../serialization/wallet-text.ts","../../core/utils.ts","../../serialization/wallet-dat.ts","../../core/Sphere.ts","../../core/currency.ts","../../core/index.ts"],"sourcesContent":["/**\n * Bech32 Encoding/Decoding\n * BIP-173 implementation for address encoding\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Bech32 character set from BIP-173 */\nexport const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\n/** Generator polynomial for checksum */\nconst GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];\n\n// =============================================================================\n// Bit Conversion\n// =============================================================================\n\n/**\n * Convert between bit arrays (8→5 or 5→8)\n */\nexport function convertBits(\n data: number[],\n fromBits: number,\n toBits: number,\n pad: boolean\n): number[] | null {\n let acc = 0;\n let bits = 0;\n const ret: number[] = [];\n const maxv = (1 << toBits) - 1;\n\n for (let i = 0; i < data.length; i++) {\n const value = data[i];\n if (value < 0 || value >> fromBits !== 0) return null;\n acc = (acc << fromBits) | value;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n ret.push((acc >> bits) & maxv);\n }\n }\n\n if (pad) {\n if (bits > 0) {\n ret.push((acc << (toBits - bits)) & maxv);\n }\n } else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) {\n return null;\n }\n\n return ret;\n}\n\n// =============================================================================\n// Internal Functions\n// =============================================================================\n\n/**\n * Expand HRP for checksum calculation\n */\nfunction hrpExpand(hrp: string): number[] {\n const ret: number[] = [];\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);\n ret.push(0);\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);\n return ret;\n}\n\n/**\n * Calculate polymod checksum\n */\nfunction bech32Polymod(values: number[]): number {\n let chk = 1;\n for (let p = 0; p < values.length; p++) {\n const top = chk >> 25;\n chk = ((chk & 0x1ffffff) << 5) ^ values[p];\n for (let i = 0; i < 5; i++) {\n if ((top >> i) & 1) chk ^= GENERATOR[i];\n }\n }\n return chk;\n}\n\n/**\n * Create checksum for bech32\n */\nfunction bech32Checksum(hrp: string, data: number[]): number[] {\n const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);\n const mod = bech32Polymod(values) ^ 1;\n\n const ret: number[] = [];\n for (let p = 0; p < 6; p++) {\n ret.push((mod >> (5 * (5 - p))) & 31);\n }\n return ret;\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Encode data to bech32 address\n *\n * @example\n * ```ts\n * const address = encodeBech32('alpha', 1, pubkeyHash);\n * // 'alpha1qw...'\n * ```\n */\nexport function encodeBech32(\n hrp: string,\n version: number,\n program: Uint8Array\n): string {\n if (version < 0 || version > 16) {\n throw new Error('Invalid witness version');\n }\n\n const converted = convertBits(Array.from(program), 8, 5, true);\n if (!converted) {\n throw new Error('Failed to convert bits');\n }\n\n const data = [version].concat(converted);\n const checksum = bech32Checksum(hrp, data);\n const combined = data.concat(checksum);\n\n let out = hrp + '1';\n for (let i = 0; i < combined.length; i++) {\n out += CHARSET[combined[i]];\n }\n\n return out;\n}\n\n/**\n * Decode bech32 address\n *\n * @example\n * ```ts\n * const result = decodeBech32('alpha1qw...');\n * // { hrp: 'alpha', witnessVersion: 1, data: Uint8Array }\n * ```\n */\nexport function decodeBech32(\n addr: string\n): { hrp: string; witnessVersion: number; data: Uint8Array } | null {\n addr = addr.toLowerCase();\n\n const pos = addr.lastIndexOf('1');\n if (pos < 1) return null;\n\n const hrp = addr.substring(0, pos);\n const dataStr = addr.substring(pos + 1);\n\n const data: number[] = [];\n for (let i = 0; i < dataStr.length; i++) {\n const val = CHARSET.indexOf(dataStr[i]);\n if (val === -1) return null;\n data.push(val);\n }\n\n // Validate checksum\n const checksum = bech32Checksum(hrp, data.slice(0, -6));\n for (let i = 0; i < 6; i++) {\n if (checksum[i] !== data[data.length - 6 + i]) {\n return null;\n }\n }\n\n const version = data[0];\n const program = convertBits(data.slice(1, -6), 5, 8, false);\n if (!program) return null;\n\n return {\n hrp,\n witnessVersion: version,\n data: Uint8Array.from(program),\n };\n}\n\n/**\n * Create address from public key hash\n *\n * @example\n * ```ts\n * const address = createAddress('alpha', pubkeyHash);\n * // 'alpha1...'\n * ```\n */\nexport function createAddress(hrp: string, pubkeyHash: Uint8Array | string): string {\n const hashBytes = typeof pubkeyHash === 'string'\n ? Uint8Array.from(Buffer.from(pubkeyHash, 'hex'))\n : pubkeyHash;\n\n return encodeBech32(hrp, 1, hashBytes);\n}\n\n/**\n * Validate bech32 address\n */\nexport function isValidBech32(addr: string): boolean {\n return decodeBech32(addr) !== null;\n}\n\n/**\n * Get HRP from address\n */\nexport function getAddressHrp(addr: string): string | null {\n const result = decodeBech32(addr);\n return result?.hrp ?? null;\n}\n\n// =============================================================================\n// Aliases for L1 SDK compatibility\n// =============================================================================\n\n/**\n * Alias for encodeBech32 (L1 SDK compatibility)\n */\nexport const createBech32 = encodeBech32;\n","import { decodeBech32 } from \"../core/bech32\";\nimport CryptoJS from \"crypto-js\";\n\n/** Convert bytes to hex */\nfunction bytesToHex(buf: Uint8Array) {\n return Array.from(buf)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Convert \"alpha1xxxx\" Bech32 → Electrum script hash\n * Required for:\n * - blockchain.scripthash.get_history\n * - blockchain.scripthash.listunspent\n */\nexport function addressToScriptHash(address: string): string {\n const decoded = decodeBech32(address);\n if (!decoded) throw new Error(\"Invalid bech32 address: \" + address);\n\n // witness program always starts with OP_0 + PUSH20 (for P2WPKH)\n const scriptHex = \"0014\" + bytesToHex(decoded.data);\n\n // SHA256\n const sha = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(scriptHex)).toString();\n\n // Electrum requires reversed byte order\n return sha.match(/../g)!.reverse().join(\"\");\n}\n","// L1 Network - Fulcrum WebSocket client\n\nimport { addressToScriptHash } from './addressToScriptHash';\nimport type { UTXO } from './types';\n\nconst DEFAULT_ENDPOINT = 'wss://fulcrum.unicity.network:50004';\n\ninterface PendingRequest {\n resolve: (result: unknown) => void;\n reject: (err: unknown) => void;\n}\n\nexport interface BlockHeader {\n height: number;\n hex: string;\n [key: string]: unknown;\n}\n\ninterface BalanceResult {\n confirmed: number;\n unconfirmed: number;\n}\n\nlet ws: WebSocket | null = null;\nlet isConnected = false;\nlet isConnecting = false;\nlet requestId = 0;\nlet intentionalClose = false;\nlet reconnectAttempts = 0;\nlet isBlockSubscribed = false;\nlet lastBlockHeader: BlockHeader | null = null;\n\n// Store timeout IDs for pending requests\ninterface PendingRequestWithTimeout extends PendingRequest {\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\nconst pending: Record<number, PendingRequestWithTimeout> = {};\nconst blockSubscribers: ((header: BlockHeader) => void)[] = [];\n\n// Connection state callbacks with cleanup support\ninterface ConnectionCallback {\n resolve: () => void;\n reject: (err: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\nconst connectionCallbacks: ConnectionCallback[] = [];\n\n// Reconnect configuration\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst BASE_DELAY = 2000;\nconst MAX_DELAY = 60000; // 1 minute\n\n// Timeout configuration\nconst RPC_TIMEOUT = 30000; // 30 seconds\nconst CONNECTION_TIMEOUT = 30000; // 30 seconds\n\n// ----------------------------------------\n// CONNECTION STATE\n// ----------------------------------------\nexport function isWebSocketConnected(): boolean {\n return isConnected && ws !== null && ws.readyState === WebSocket.OPEN;\n}\n\nexport function waitForConnection(): Promise<void> {\n if (isWebSocketConnected()) {\n return Promise.resolve();\n }\n\n return new Promise((resolve, reject) => {\n const callback: ConnectionCallback = {\n resolve: () => {\n if (callback.timeoutId) clearTimeout(callback.timeoutId);\n resolve();\n },\n reject: (err: Error) => {\n if (callback.timeoutId) clearTimeout(callback.timeoutId);\n reject(err);\n },\n };\n\n callback.timeoutId = setTimeout(() => {\n // Remove from callbacks array\n const idx = connectionCallbacks.indexOf(callback);\n if (idx > -1) connectionCallbacks.splice(idx, 1);\n reject(new Error('Connection timeout'));\n }, CONNECTION_TIMEOUT);\n\n connectionCallbacks.push(callback);\n });\n}\n\n// ----------------------------------------\n// SINGLETON CONNECT — prevents double connect\n// ----------------------------------------\nexport function connect(endpoint: string = DEFAULT_ENDPOINT): Promise<void> {\n if (isConnected) {\n return Promise.resolve();\n }\n\n if (isConnecting) {\n return waitForConnection();\n }\n\n isConnecting = true;\n\n return new Promise((resolve, reject) => {\n let hasResolved = false;\n\n try {\n ws = new WebSocket(endpoint);\n } catch (err) {\n console.error('[L1] WebSocket constructor threw exception:', err);\n isConnecting = false;\n reject(err);\n return;\n }\n\n ws.onopen = () => {\n isConnected = true;\n isConnecting = false;\n reconnectAttempts = 0; // Reset reconnect counter on successful connection\n hasResolved = true;\n resolve();\n\n // Notify all waiting callbacks (clear their timeouts first)\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n cb.resolve();\n });\n connectionCallbacks.length = 0;\n };\n\n ws.onclose = () => {\n isConnected = false;\n isBlockSubscribed = false; // Reset block subscription on disconnect\n\n // Reject all pending requests and clear their timeouts\n Object.values(pending).forEach((req) => {\n if (req.timeoutId) clearTimeout(req.timeoutId);\n req.reject(new Error('WebSocket connection closed'));\n });\n Object.keys(pending).forEach((key) => delete pending[Number(key)]);\n\n // Don't reconnect if this was an intentional close\n if (intentionalClose) {\n intentionalClose = false;\n isConnecting = false;\n reconnectAttempts = 0;\n\n // Reject if we haven't resolved yet\n if (!hasResolved) {\n hasResolved = true;\n reject(new Error('WebSocket connection closed intentionally'));\n }\n return;\n }\n\n // Check if we've exceeded max reconnect attempts\n if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n console.error('[L1] Max reconnect attempts reached. Giving up.');\n isConnecting = false;\n\n // Reject all waiting callbacks\n const error = new Error('Max reconnect attempts reached');\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n cb.reject(error);\n });\n connectionCallbacks.length = 0;\n\n // Reject if we haven't resolved yet\n if (!hasResolved) {\n hasResolved = true;\n reject(error);\n }\n return;\n }\n\n // Calculate exponential backoff delay\n const delay = Math.min(BASE_DELAY * Math.pow(2, reconnectAttempts), MAX_DELAY);\n\n reconnectAttempts++;\n console.warn(\n `[L1] WebSocket closed unexpectedly. Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`\n );\n\n // Keep isConnecting true so callers know reconnection is in progress\n // The resolve/reject will happen when reconnection succeeds or fails\n setTimeout(() => {\n connect(endpoint)\n .then(() => {\n if (!hasResolved) {\n hasResolved = true;\n resolve();\n }\n })\n .catch((err) => {\n if (!hasResolved) {\n hasResolved = true;\n reject(err);\n }\n });\n }, delay);\n };\n\n ws.onerror = (err: Event) => {\n console.error('[L1] WebSocket error:', err);\n // Note: Browser WebSocket errors don't provide detailed error info for security reasons\n // The actual connection error details are only visible in browser DevTools Network tab\n // Error alone doesn't mean connection failed - onclose will be called\n };\n\n ws.onmessage = (msg) => handleMessage(msg);\n });\n}\n\nfunction handleMessage(event: MessageEvent) {\n const data = JSON.parse(event.data);\n\n if (data.id && pending[data.id]) {\n const request = pending[data.id];\n delete pending[data.id];\n if (data.error) {\n request.reject(data.error);\n } else {\n request.resolve(data.result);\n }\n }\n\n if (data.method === 'blockchain.headers.subscribe') {\n const header = data.params[0] as BlockHeader;\n lastBlockHeader = header; // Cache for late subscribers\n blockSubscribers.forEach((cb) => cb(header));\n }\n}\n\n// ----------------------------------------\n// SAFE RPC - Auto-connects and waits if needed\n// ----------------------------------------\nexport async function rpc(method: string, params: unknown[] = []): Promise<unknown> {\n // Auto-connect if not connected\n if (!isConnected && !isConnecting) {\n await connect();\n }\n\n // Wait for connection if connecting\n if (!isWebSocketConnected()) {\n await waitForConnection();\n }\n\n return new Promise((resolve, reject) => {\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n return reject(new Error('WebSocket not connected (OPEN)'));\n }\n\n const id = ++requestId;\n\n // Set up timeout for this request\n const timeoutId = setTimeout(() => {\n if (pending[id]) {\n delete pending[id];\n reject(new Error(`RPC timeout: ${method}`));\n }\n }, RPC_TIMEOUT);\n\n pending[id] = {\n resolve: (result) => {\n clearTimeout(timeoutId);\n resolve(result);\n },\n reject: (err) => {\n clearTimeout(timeoutId);\n reject(err);\n },\n timeoutId,\n };\n\n ws.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }));\n });\n}\n\n// ----------------------------------------\n// API METHODS\n// ----------------------------------------\n\nexport async function getUtxo(address: string) {\n const scripthash = addressToScriptHash(address);\n\n const result = await rpc('blockchain.scripthash.listunspent', [scripthash]);\n\n if (!Array.isArray(result)) {\n console.warn('listunspent returned non-array:', result);\n return [];\n }\n\n return result.map((u: UTXO) => ({\n tx_hash: u.tx_hash,\n tx_pos: u.tx_pos,\n value: u.value,\n height: u.height,\n address,\n }));\n}\n\nexport async function getBalance(address: string) {\n const scriptHash = addressToScriptHash(address);\n const result = (await rpc('blockchain.scripthash.get_balance', [scriptHash])) as BalanceResult;\n\n const confirmed = result.confirmed || 0;\n const unconfirmed = result.unconfirmed || 0;\n\n const totalSats = confirmed + unconfirmed;\n\n // Convert sats → ALPHA\n const alpha = totalSats / 100_000_000;\n\n return alpha;\n}\n\nexport async function broadcast(rawHex: string) {\n return await rpc('blockchain.transaction.broadcast', [rawHex]);\n}\n\nexport async function subscribeBlocks(cb: (header: BlockHeader) => void): Promise<() => void> {\n // Auto-connect if not connected (same as rpc())\n if (!isConnected && !isConnecting) {\n await connect();\n }\n\n // Wait for connection to be established\n if (!isWebSocketConnected()) {\n await waitForConnection();\n }\n\n blockSubscribers.push(cb);\n\n // Only send RPC subscription if not already subscribed\n // This prevents duplicate server-side subscriptions\n if (!isBlockSubscribed) {\n isBlockSubscribed = true;\n const header = (await rpc('blockchain.headers.subscribe', [])) as BlockHeader;\n if (header) {\n lastBlockHeader = header;\n // Notify ALL current subscribers with the initial header\n blockSubscribers.forEach((subscriber) => subscriber(header));\n }\n } else if (lastBlockHeader) {\n // For late subscribers, immediately notify with cached header\n cb(lastBlockHeader);\n }\n\n // Return unsubscribe function\n return () => {\n const index = blockSubscribers.indexOf(cb);\n if (index > -1) {\n blockSubscribers.splice(index, 1);\n }\n };\n}\n\nexport interface TransactionHistoryItem {\n tx_hash: string;\n height: number;\n fee?: number;\n}\n\nexport interface TransactionDetail {\n txid: string;\n version: number;\n locktime: number;\n vin: Array<{\n txid: string;\n vout: number;\n scriptSig?: {\n hex: string;\n };\n sequence: number;\n }>;\n vout: Array<{\n value: number;\n n: number;\n scriptPubKey: {\n hex: string;\n type: string;\n addresses?: string[];\n address?: string;\n };\n }>;\n blockhash?: string;\n confirmations?: number;\n time?: number;\n blocktime?: number;\n}\n\nexport async function getTransactionHistory(address: string): Promise<TransactionHistoryItem[]> {\n const scriptHash = addressToScriptHash(address);\n const result = await rpc('blockchain.scripthash.get_history', [scriptHash]);\n\n if (!Array.isArray(result)) {\n console.warn('get_history returned non-array:', result);\n return [];\n }\n\n return result as TransactionHistoryItem[];\n}\n\nexport async function getTransaction(txid: string) {\n return await rpc('blockchain.transaction.get', [txid, true]);\n}\n\nexport async function getBlockHeader(height: number) {\n return await rpc('blockchain.block.header', [height, height]);\n}\n\nexport async function getCurrentBlockHeight(): Promise<number> {\n try {\n const header = (await rpc('blockchain.headers.subscribe', [])) as BlockHeader;\n return header?.height || 0;\n } catch (err) {\n console.error('Error getting current block height:', err);\n return 0;\n }\n}\n\nexport function disconnect() {\n if (ws) {\n intentionalClose = true;\n ws.close();\n ws = null;\n }\n isConnected = false;\n isConnecting = false;\n reconnectAttempts = 0;\n isBlockSubscribed = false;\n\n // Clear all pending request timeouts\n Object.values(pending).forEach((req) => {\n if (req.timeoutId) clearTimeout(req.timeoutId);\n });\n Object.keys(pending).forEach((key) => delete pending[Number(key)]);\n\n // Clear connection callback timeouts\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n });\n connectionCallbacks.length = 0;\n}\n","/**\n * L1 SDK - ALPHA blockchain operations\n */\n\n// Types\nexport * from './types';\n\n// Bech32 encoding (from core)\nexport {\n createBech32,\n encodeBech32,\n decodeBech32,\n convertBits,\n CHARSET,\n} from '../core/bech32';\n\n// Address utilities\nexport { addressToScriptHash } from './addressToScriptHash';\n\n// Crypto utilities (from core)\nexport {\n computeHash160,\n hash160,\n hash160ToBytes,\n publicKeyToAddress,\n privateKeyToAddressInfo,\n generateAddressInfo,\n ec,\n} from '../core/crypto';\n\n// Encryption and WIF\nexport {\n encrypt,\n decrypt,\n encryptWallet,\n decryptWallet,\n generatePrivateKey,\n hexToWIF,\n} from './crypto';\n\n// Address derivation\nexport {\n deriveChildKeyBIP32,\n deriveKeyAtPath,\n generateMasterKeyFromSeed,\n generateHDAddressBIP32,\n generateAddressFromMasterKey,\n deriveChildKey,\n generateHDAddress,\n} from './address';\n\n// Network (Fulcrum WebSocket)\nexport {\n connect,\n disconnect,\n rpc,\n isWebSocketConnected,\n waitForConnection,\n getUtxo,\n getBalance,\n broadcast,\n subscribeBlocks,\n getTransactionHistory,\n getTransaction,\n getBlockHeader,\n getCurrentBlockHeight,\n} from './network';\nexport type { BlockHeader, TransactionHistoryItem, TransactionDetail } from './network';\n\n// Transaction building\nexport {\n createScriptPubKey,\n buildSegWitTransaction,\n createAndSignTransaction,\n collectUtxosForAmount,\n createTransactionPlan,\n sendAlpha,\n} from './tx';\n\n// Vesting classification\nexport { vestingClassifier, VESTING_THRESHOLD } from './vesting';\nexport { vestingState } from './vestingState';\n\n// Address helpers\nexport { WalletAddressHelper } from './addressHelpers';\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';\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 Error('Invalid master key generated');\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 Error('Invalid key: IL >= curve order');\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 Error('Invalid key: child key is zero');\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 Error('Invalid mnemonic phrase');\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 Error('Invalid mnemonic phrase');\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// Re-export elliptic instance for advanced use cases\nexport { ec };\n","import CryptoJS from \"crypto-js\";\n\nconst SALT = \"alpha_wallet_salt\";\nconst PBKDF2_ITERATIONS = 100000;\n\nexport function encrypt(text: string, password: string): string {\n return CryptoJS.AES.encrypt(text, password).toString();\n}\n\nexport function decrypt(encrypted: string, password: string): string {\n const bytes = CryptoJS.AES.decrypt(encrypted, password);\n return bytes.toString(CryptoJS.enc.Utf8);\n}\n\nexport function generatePrivateKey(): string {\n return CryptoJS.lib.WordArray.random(32).toString();\n}\n\n/**\n * Encrypt wallet master key with password using PBKDF2 + AES\n */\nexport function encryptWallet(\n masterPrivateKey: string,\n password: string\n): string {\n const passwordKey = CryptoJS.PBKDF2(password, SALT, {\n keySize: 256 / 32,\n iterations: PBKDF2_ITERATIONS,\n }).toString();\n\n const encrypted = CryptoJS.AES.encrypt(\n masterPrivateKey,\n passwordKey\n ).toString();\n\n return encrypted;\n}\n\n/**\n * Decrypt wallet master key with password\n */\nexport function decryptWallet(\n encryptedData: string,\n password: string\n): string {\n const passwordKey = CryptoJS.PBKDF2(password, SALT, {\n keySize: 256 / 32,\n iterations: PBKDF2_ITERATIONS,\n }).toString();\n\n const decrypted = CryptoJS.AES.decrypt(encryptedData, passwordKey);\n return decrypted.toString(CryptoJS.enc.Utf8);\n}\n\n/**\n * Convert hex private key to WIF format\n */\nexport function hexToWIF(hexKey: string): string {\n // Alpha mainnet version byte is 0x80\n const versionByte = \"80\";\n const extendedKey = versionByte + hexKey;\n\n // Calculate checksum\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(extendedKey)).toString();\n const hash2 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(hash1)).toString();\n const checksum = hash2.substring(0, 8);\n\n // Combine and encode\n const finalHex = extendedKey + checksum;\n\n // Convert to Base58\n return base58Encode(finalHex);\n}\n\n/**\n * Base58 encoding\n */\nfunction base58Encode(hex: string): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\n // Convert hex to big integer\n let num = BigInt(\"0x\" + hex);\n let encoded = \"\";\n\n while (num > 0n) {\n const remainder = Number(num % 58n);\n num = num / 58n;\n encoded = ALPHABET[remainder] + encoded;\n }\n\n // Add leading 1s for leading 0s in hex\n for (let i = 0; i < hex.length && hex.substring(i, i + 2) === \"00\"; i += 2) {\n encoded = \"1\" + encoded;\n }\n\n return encoded;\n}\n","/**\n * L1 Address Derivation\n *\n * Uses core crypto functions for standard BIP32 derivation,\n * plus legacy functions for backward compatibility with old wallets.\n */\n\nimport CryptoJS from 'crypto-js';\nimport {\n deriveChildKey as coreDeriveChildKey,\n deriveKeyAtPath as coreDeriveKeyAtPath,\n generateMasterKey,\n generateAddressInfo,\n ec,\n type AddressInfo,\n} from '../core/crypto';\n\n// Re-export core functions with L1 naming conventions\nexport { ec };\n\n/**\n * Standard BIP32 child key derivation\n * Re-export from core with L1 naming convention\n */\nexport const deriveChildKeyBIP32 = coreDeriveChildKey;\n\n/**\n * Derive key at a full BIP44 path\n * Re-export from core\n */\nexport const deriveKeyAtPath = coreDeriveKeyAtPath;\n\n/**\n * Generate master key and chain code from seed (BIP32 standard)\n * Wrapper around core function with L1 return type naming\n */\nexport function generateMasterKeyFromSeed(seedHex: string): {\n masterPrivateKey: string;\n masterChainCode: string;\n} {\n const result = generateMasterKey(seedHex);\n return {\n masterPrivateKey: result.privateKey,\n masterChainCode: result.chainCode,\n };\n}\n\n/**\n * Generate HD address using standard BIP32\n * Standard path: m/44'/0'/0'/0/{index} (external chain, non-hardened)\n * For change addresses, use isChange = true to get m/44'/0'/0'/1/{index}\n */\nexport function generateHDAddressBIP32(\n masterPriv: string,\n chainCode: string,\n index: number,\n basePath: string = \"m/44'/0'/0'\",\n isChange: boolean = false\n): AddressInfo {\n // Chain: 0 = external (receiving), 1 = internal (change)\n const chain = isChange ? 1 : 0;\n const fullPath = `${basePath}/${chain}/${index}`;\n\n const derived = coreDeriveKeyAtPath(masterPriv, chainCode, fullPath);\n\n return generateAddressInfo(derived.privateKey, index, fullPath);\n}\n\n// ============================================\n// Original index.html compatible derivation\n// ============================================\n\n/**\n * Generate address from master private key using HMAC-SHA512 derivation\n * This matches exactly the original index.html implementation\n * NOTE: This is NON-STANDARD derivation for legacy wallet compatibility\n *\n * @param masterPrivateKey - 32-byte hex private key (64 chars)\n * @param index - Address index\n */\nexport function generateAddressFromMasterKey(\n masterPrivateKey: string,\n index: number\n): AddressInfo {\n const derivationPath = `m/44'/0'/${index}'`;\n\n // HMAC-SHA512 with path as key (matching index.html exactly)\n const hmacInput = CryptoJS.enc.Hex.parse(masterPrivateKey);\n const hmacKey = CryptoJS.enc.Utf8.parse(derivationPath);\n const hmacOutput = CryptoJS.HmacSHA512(hmacInput, hmacKey).toString();\n\n // Use left 32 bytes for private key\n const childPrivateKey = hmacOutput.substring(0, 64);\n\n return generateAddressInfo(childPrivateKey, index, derivationPath);\n}\n\n// ============================================\n// Legacy functions for backward compatibility\n// ============================================\n\n/**\n * @deprecated Use deriveChildKeyBIP32 for new wallets\n * Legacy HMAC-SHA512 derivation (non-standard)\n * Kept for backward compatibility with old wallets\n */\nexport function deriveChildKey(\n masterPriv: string,\n chainCode: string,\n index: number\n) {\n const data = masterPriv + index.toString(16).padStart(8, '0');\n\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(data),\n CryptoJS.enc.Hex.parse(chainCode)\n ).toString();\n\n return {\n privateKey: I.substring(0, 64),\n nextChainCode: I.substring(64),\n };\n}\n\n/**\n * @deprecated Use generateHDAddressBIP32 for new wallets\n * Legacy HD address generation (non-standard derivation)\n */\nexport function generateHDAddress(\n masterPriv: string,\n chainCode: string,\n index: number\n): AddressInfo {\n const child = deriveChildKey(masterPriv, chainCode, index);\n const path = `m/44'/0'/0'/${index}`;\n\n return generateAddressInfo(child.privateKey, index, path);\n}\n","/**\n * Transaction handling - Strict copy of index.html logic\n */\nimport { getUtxo, broadcast } from \"./network\";\nimport { decodeBech32 } from \"../core/bech32\";\nimport CryptoJS from \"crypto-js\";\nimport elliptic from \"elliptic\";\nimport type { Wallet, TransactionPlan, Transaction, UTXO } from \"./types\";\nimport { vestingState } from \"./vestingState\";\nimport { WalletAddressHelper } from \"./addressHelpers\";\n\nconst ec = new elliptic.ec(\"secp256k1\");\n\n// Constants\nconst FEE = 10_000; // sats per transaction\nconst DUST = 546; // dust threshold\nconst SAT = 100_000_000; // sats in 1 ALPHA\n\n/**\n * Create scriptPubKey for address (P2WPKH for bech32)\n * Exact copy from index.html\n */\nexport function createScriptPubKey(address: string): string {\n if (!address || typeof address !== \"string\") {\n throw new Error(\"Invalid address: must be a string\");\n }\n\n const decoded = decodeBech32(address);\n if (!decoded) {\n throw new Error(\"Invalid bech32 address: \" + address);\n }\n\n // Convert data array to hex string\n const dataHex = Array.from(decoded.data)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n // P2WPKH scriptPubKey: OP_0 <20-byte-key-hash>\n return \"0014\" + dataHex;\n}\n\n/**\n * Create signature hash for SegWit (BIP143)\n * Exact copy from index.html createSignatureHash()\n */\nfunction createSignatureHash(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n publicKey: string\n): string {\n let preimage = \"\";\n\n // 1. nVersion (4 bytes, little-endian)\n preimage += \"02000000\";\n\n // 2. hashPrevouts (32 bytes)\n const txidBytes = txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n const voutBytes = (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n const prevouts = txidBytes + voutBytes;\n const hashPrevouts = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(prevouts))).toString();\n preimage += hashPrevouts;\n\n // 3. hashSequence (32 bytes)\n const sequence = \"feffffff\";\n const hashSequence = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(sequence))).toString();\n preimage += hashSequence;\n\n // 4. outpoint (36 bytes)\n preimage += txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n preimage += (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // 5. scriptCode for P2WPKH (includes length prefix)\n const pubKeyHash = CryptoJS.RIPEMD160(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(publicKey))).toString();\n const scriptCode = \"1976a914\" + pubKeyHash + \"88ac\";\n preimage += scriptCode;\n\n // 6. amount (8 bytes, little-endian)\n const amountHex = txPlan.input.value.toString(16).padStart(16, \"0\");\n preimage += amountHex.match(/../g)!.reverse().join(\"\");\n\n // 7. nSequence (4 bytes, little-endian)\n preimage += sequence;\n\n // 8. hashOutputs (32 bytes)\n let outputs = \"\";\n for (const output of txPlan.outputs) {\n const outAmountHex = output.value.toString(16).padStart(16, \"0\");\n outputs += outAmountHex.match(/../g)!.reverse().join(\"\");\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (scriptPubKey.length / 2).toString(16).padStart(2, \"0\");\n outputs += scriptLength;\n outputs += scriptPubKey;\n }\n const hashOutputs = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(outputs))).toString();\n preimage += hashOutputs;\n\n // 9. nLocktime (4 bytes, little-endian)\n preimage += \"00000000\";\n\n // 10. sighash type (4 bytes, little-endian)\n preimage += \"01000000\"; // SIGHASH_ALL\n\n // Double SHA256\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(preimage));\n const hash2 = CryptoJS.SHA256(hash1);\n return hash2.toString();\n}\n\n/**\n * Create witness data for the transaction\n * Exact copy from index.html createWitnessData()\n */\nfunction createWitnessData(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n keyPair: elliptic.ec.KeyPair,\n publicKey: string\n): string {\n // Create signature hash for witness\n const sigHash = createSignatureHash(txPlan, publicKey);\n\n // Sign the hash\n const signature = keyPair.sign(sigHash);\n\n // Ensure low-S canonical signature (BIP62)\n const halfOrder = ec.curve.n!.shrn(1);\n if (signature.s.cmp(halfOrder) > 0) {\n signature.s = ec.curve.n!.sub(signature.s);\n }\n\n const derSig = signature.toDER(\"hex\") + \"01\"; // SIGHASH_ALL\n\n // Build witness\n let witness = \"\";\n witness += \"02\"; // 2 stack items\n\n // Signature\n const sigLen = (derSig.length / 2).toString(16).padStart(2, \"0\");\n witness += sigLen;\n witness += derSig;\n\n // Public key\n const pubKeyLen = (publicKey.length / 2).toString(16).padStart(2, \"0\");\n witness += pubKeyLen;\n witness += publicKey;\n\n return witness;\n}\n\n/**\n * Build a proper SegWit transaction\n * Exact copy from index.html buildSegWitTransaction()\n */\nexport function buildSegWitTransaction(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n keyPair: elliptic.ec.KeyPair,\n publicKey: string\n): { hex: string; txid: string } {\n let txHex = \"\";\n\n // Version (4 bytes, little-endian)\n txHex += \"02000000\"; // version 2\n\n // Marker and flag for SegWit\n txHex += \"00\"; // marker\n txHex += \"01\"; // flag\n\n // Number of inputs (varint)\n txHex += \"01\"; // 1 input\n\n // Input - Previous tx hash (32 bytes, reversed for little-endian)\n const prevTxHash = txPlan.input.tx_hash;\n const reversedHash = prevTxHash.match(/../g)!.reverse().join(\"\");\n txHex += reversedHash;\n\n // Previous output index (4 bytes, little-endian)\n const vout = txPlan.input.tx_pos;\n txHex += (\"00000000\" + vout.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // Script length (varint) - 0 for witness transactions\n txHex += \"00\";\n\n // Sequence (4 bytes)\n txHex += \"feffffff\";\n\n // Number of outputs (varint)\n const outputCount = txPlan.outputs.length;\n txHex += (\"0\" + outputCount.toString(16)).slice(-2);\n\n // Outputs\n for (const output of txPlan.outputs) {\n // Amount (8 bytes, little-endian)\n const amountHex = output.value.toString(16).padStart(16, \"0\");\n txHex += amountHex.match(/../g)!.reverse().join(\"\");\n\n // Script pubkey\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (scriptPubKey.length / 2).toString(16).padStart(2, \"0\");\n txHex += scriptLength;\n txHex += scriptPubKey;\n }\n\n // Witness data\n const witnessData = createWitnessData(txPlan, keyPair, publicKey);\n txHex += witnessData;\n\n // Locktime (4 bytes)\n txHex += \"00000000\";\n\n // Calculate transaction ID (double SHA256 of tx without witness data)\n let txForId = \"\";\n\n // Version (4 bytes)\n txForId += \"02000000\";\n\n // Input count (1 byte)\n txForId += \"01\";\n\n // Input: txid (32 bytes reversed) + vout (4 bytes)\n const inputTxidBytes = txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n txForId += inputTxidBytes;\n txForId += (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // Script sig (empty for P2WPKH)\n txForId += \"00\";\n\n // Sequence (4 bytes)\n txForId += \"feffffff\";\n\n // Output count\n txForId += (\"0\" + txPlan.outputs.length.toString(16)).slice(-2);\n\n // Add all outputs\n for (const output of txPlan.outputs) {\n const amountHex = (\"0000000000000000\" + output.value.toString(16)).slice(-16);\n const amountLittleEndian = amountHex.match(/../g)!.reverse().join(\"\");\n txForId += amountLittleEndian;\n\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (\"0\" + (scriptPubKey.length / 2).toString(16)).slice(-2);\n txForId += scriptLength;\n txForId += scriptPubKey;\n }\n\n // Locktime (4 bytes)\n txForId += \"00000000\";\n\n // Calculate the correct txid\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(txForId));\n const hash2 = CryptoJS.SHA256(hash1);\n const txid = hash2.toString().match(/../g)!.reverse().join(\"\");\n\n return {\n hex: txHex,\n txid: txid,\n };\n}\n\n/**\n * Create and sign a transaction\n * Uses the private key for the specific address being spent from\n */\nexport function createAndSignTransaction(\n wallet: Wallet,\n txPlan: Transaction\n): { raw: string; txid: string } {\n // Find the address entry that matches the input address\n const fromAddress = txPlan.input.address;\n const addressEntry = wallet.addresses.find(a => a.address === fromAddress);\n\n // Use the private key from the address entry, or fall back to childPrivateKey/masterPrivateKey\n let privateKeyHex: string | undefined;\n\n if (addressEntry?.privateKey) {\n // Use the specific private key for this address\n privateKeyHex = addressEntry.privateKey;\n } else if (wallet.childPrivateKey) {\n // Fall back to childPrivateKey (first address)\n privateKeyHex = wallet.childPrivateKey;\n } else {\n // Last resort: use master key\n privateKeyHex = wallet.masterPrivateKey;\n }\n\n if (!privateKeyHex) {\n throw new Error(\"No private key available for address: \" + fromAddress);\n }\n\n const keyPair = ec.keyFromPrivate(privateKeyHex, \"hex\");\n const publicKey = keyPair.getPublic(true, \"hex\"); // compressed\n\n // Convert Transaction to the format expected by buildSegWitTransaction\n const txPlanForBuild = {\n input: {\n tx_hash: txPlan.input.txid,\n tx_pos: txPlan.input.vout,\n value: txPlan.input.value,\n },\n outputs: txPlan.outputs,\n };\n\n const tx = buildSegWitTransaction(txPlanForBuild, keyPair, publicKey);\n\n return {\n raw: tx.hex,\n txid: tx.txid,\n };\n}\n\n/**\n * Collect UTXOs for required amount\n * Based on index.html collectUtxosForAmount()\n *\n * Strategy: First try to find a single UTXO that can cover amount + fee.\n * If not found, fall back to combining multiple UTXOs.\n */\nexport function collectUtxosForAmount(\n utxoList: UTXO[],\n amountSats: number,\n recipientAddress: string,\n senderAddress: string\n): TransactionPlan {\n const totalAvailable = utxoList.reduce((sum, u) => sum + u.value, 0);\n\n if (totalAvailable < amountSats + FEE) {\n return {\n success: false,\n transactions: [],\n error: `Insufficient funds. Available: ${totalAvailable / SAT} ALPHA, Required: ${(amountSats + FEE) / SAT} ALPHA (including fee)`,\n };\n }\n\n // Strategy 1: Find a single UTXO that covers amount + fee\n // Sort by value ascending to find the smallest sufficient UTXO\n const sortedByValue = [...utxoList].sort((a, b) => a.value - b.value);\n const sufficientUtxo = sortedByValue.find(u => u.value >= amountSats + FEE);\n\n if (sufficientUtxo) {\n const changeAmount = sufficientUtxo.value - amountSats - FEE;\n const tx: Transaction = {\n input: {\n txid: sufficientUtxo.txid ?? sufficientUtxo.tx_hash ?? \"\",\n vout: sufficientUtxo.vout ?? sufficientUtxo.tx_pos ?? 0,\n value: sufficientUtxo.value,\n address: sufficientUtxo.address ?? senderAddress,\n },\n outputs: [{ address: recipientAddress, value: amountSats }],\n fee: FEE,\n changeAmount: changeAmount,\n changeAddress: senderAddress,\n };\n\n // Add change output if above dust\n if (changeAmount > DUST) {\n tx.outputs.push({ value: changeAmount, address: senderAddress });\n }\n\n return {\n success: true,\n transactions: [tx],\n };\n }\n\n // Strategy 2: No single UTXO is sufficient, combine multiple UTXOs\n // Sort descending to use larger UTXOs first (fewer transactions)\n const sortedDescending = [...utxoList].sort((a, b) => b.value - a.value);\n\n const transactions: Transaction[] = [];\n let remainingAmount = amountSats;\n\n for (const utxo of sortedDescending) {\n if (remainingAmount <= 0) break;\n\n const utxoValue = utxo.value;\n let txAmount = 0;\n let changeAmount = 0;\n\n if (utxoValue >= remainingAmount + FEE) {\n // This UTXO covers the remaining amount plus fee\n txAmount = remainingAmount;\n changeAmount = utxoValue - remainingAmount - FEE;\n remainingAmount = 0;\n } else {\n // Use entire UTXO minus fee\n txAmount = utxoValue - FEE;\n if (txAmount <= 0) continue; // Skip UTXOs too small to cover fee\n remainingAmount -= txAmount;\n }\n\n const tx: Transaction = {\n input: {\n txid: utxo.txid ?? utxo.tx_hash ?? \"\",\n vout: utxo.vout ?? utxo.tx_pos ?? 0,\n value: utxo.value,\n address: utxo.address ?? senderAddress,\n },\n outputs: [{ address: recipientAddress, value: txAmount }],\n fee: FEE,\n changeAmount: changeAmount,\n changeAddress: senderAddress,\n };\n\n // Add change output if above dust\n if (changeAmount > DUST) {\n tx.outputs.push({ value: changeAmount, address: senderAddress });\n }\n\n transactions.push(tx);\n }\n\n if (remainingAmount > 0) {\n return {\n success: false,\n transactions: [],\n error: `Unable to collect enough UTXOs. Short by ${remainingAmount / SAT} ALPHA after fees.`,\n };\n }\n\n return {\n success: true,\n transactions,\n };\n}\n\n/**\n * Create transaction plan from wallet\n * @param wallet - The wallet\n * @param toAddress - Recipient address\n * @param amountAlpha - Amount in ALPHA\n * @param fromAddress - Optional: specific address to send from (defaults to first address)\n */\nexport async function createTransactionPlan(\n wallet: Wallet,\n toAddress: string,\n amountAlpha: number,\n fromAddress?: string\n): Promise<TransactionPlan> {\n if (!decodeBech32(toAddress)) {\n throw new Error(\"Invalid recipient address\");\n }\n\n // Use specified fromAddress or default to first external address\n const defaultAddr = WalletAddressHelper.getDefault(wallet);\n const senderAddress = fromAddress || defaultAddr.address;\n const amountSats = Math.floor(amountAlpha * SAT);\n\n // Get UTXOs filtered by current vesting mode (set in SendModal)\n let utxos: UTXO[];\n const currentMode = vestingState.getMode();\n\n if (vestingState.hasClassifiedData(senderAddress)) {\n // Use vesting-filtered UTXOs based on selected mode\n utxos = vestingState.getFilteredUtxos(senderAddress);\n console.log(`Using ${utxos.length} ${currentMode} UTXOs`);\n } else {\n // Fall back to all UTXOs if not yet classified\n utxos = await getUtxo(senderAddress);\n console.log(`Using ${utxos.length} UTXOs (vesting not classified yet)`);\n }\n\n if (!Array.isArray(utxos) || utxos.length === 0) {\n const modeText = currentMode !== 'all' ? ` (${currentMode} coins)` : '';\n throw new Error(`No UTXOs available${modeText} for address: ` + senderAddress);\n }\n\n return collectUtxosForAmount(utxos, amountSats, toAddress, senderAddress);\n}\n\n/**\n * Send ALPHA to address\n * @param wallet - The wallet\n * @param toAddress - Recipient address\n * @param amountAlpha - Amount in ALPHA\n * @param fromAddress - Optional: specific address to send from\n */\nexport async function sendAlpha(\n wallet: Wallet,\n toAddress: string,\n amountAlpha: number,\n fromAddress?: string\n) {\n const plan = await createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress);\n\n if (!plan.success) {\n throw new Error(plan.error || \"Transaction planning failed\");\n }\n\n const results = [];\n\n for (const tx of plan.transactions) {\n const signed = createAndSignTransaction(wallet, tx);\n const result = await broadcast(signed.raw);\n results.push({\n txid: signed.txid,\n raw: signed.raw,\n broadcastResult: result,\n });\n }\n\n return results;\n}\n","/**\n * VestingClassifier - Traces UTXOs to their coinbase origin to determine vesting status\n * VESTED: Coins from coinbase transactions in blocks <= VESTING_THRESHOLD (280000)\n * UNVESTED: Coins from coinbase transactions in blocks > VESTING_THRESHOLD\n *\n * Direct port from index.html VestingClassifier\n */\nimport { getTransaction, getCurrentBlockHeight } from \"./network\";\nimport type { UTXO, ClassifiedUTXO, ClassificationResult } from \"./types\";\n\nexport const VESTING_THRESHOLD = 280000;\n\n// Current block height - updated during classification\nlet currentBlockHeight: number | null = null;\n\ninterface CacheEntry {\n blockHeight: number | null; // null means \"not computed yet\"\n isCoinbase: boolean;\n inputTxId: string | null;\n}\n\ninterface TransactionData {\n txid: string;\n confirmations?: number;\n height?: number;\n vin?: Array<{\n txid?: string;\n coinbase?: string;\n }>;\n}\n\nclass VestingClassifier {\n private memoryCache = new Map<string, CacheEntry>();\n private dbName = \"SphereVestingCacheV5\"; // V5 - new cache with proper null handling\n private storeName = \"vestingCache\";\n private db: IDBDatabase | null = null;\n\n /**\n * Initialize IndexedDB for persistent caching\n */\n async initDB(): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(this.storeName)) {\n db.createObjectStore(this.storeName, { keyPath: \"txHash\" });\n }\n };\n\n request.onsuccess = (event) => {\n this.db = (event.target as IDBOpenDBRequest).result;\n resolve();\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Check if transaction is coinbase\n */\n private isCoinbaseTransaction(txData: TransactionData): boolean {\n if (txData.vin && txData.vin.length === 1) {\n const vin = txData.vin[0];\n // Check for coinbase field or missing txid\n if (vin.coinbase || (!vin.txid && vin.coinbase !== undefined)) {\n return true;\n }\n // Some formats use empty txid for coinbase\n if (vin.txid === \"0000000000000000000000000000000000000000000000000000000000000000\") {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Load from IndexedDB cache\n */\n private async loadFromDB(txHash: string): Promise<CacheEntry | null> {\n if (!this.db) return null;\n\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.storeName, \"readonly\");\n const store = tx.objectStore(this.storeName);\n const request = store.get(txHash);\n\n request.onsuccess = () => {\n if (request.result) {\n resolve({\n blockHeight: request.result.blockHeight,\n isCoinbase: request.result.isCoinbase,\n inputTxId: request.result.inputTxId,\n });\n } else {\n resolve(null);\n }\n };\n request.onerror = () => resolve(null);\n });\n }\n\n /**\n * Save to IndexedDB cache\n */\n private async saveToDB(txHash: string, entry: CacheEntry): Promise<void> {\n if (!this.db) return;\n\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.storeName, \"readwrite\");\n const store = tx.objectStore(this.storeName);\n store.put({ txHash, ...entry });\n tx.oncomplete = () => resolve();\n tx.onerror = () => resolve();\n });\n }\n\n /**\n * Trace a transaction to its coinbase origin\n * Alpha blockchain has single-input transactions, making this a linear trace\n */\n async traceToOrigin(txHash: string): Promise<{ coinbaseHeight: number | null; error?: string }> {\n let currentTxHash = txHash;\n let iterations = 0;\n const MAX_ITERATIONS = 10000;\n\n while (iterations < MAX_ITERATIONS) {\n iterations++;\n\n // Check memory cache first\n const cached = this.memoryCache.get(currentTxHash);\n if (cached) {\n if (cached.isCoinbase) {\n // Skip cache if blockHeight is null - needs re-fetch\n if (cached.blockHeight !== null && cached.blockHeight !== undefined) {\n return { coinbaseHeight: cached.blockHeight };\n }\n // Fall through to re-fetch\n } else if (cached.inputTxId) {\n // Follow the input chain\n currentTxHash = cached.inputTxId;\n continue;\n }\n }\n\n // Check IndexedDB cache\n const dbCached = await this.loadFromDB(currentTxHash);\n if (dbCached) {\n // Also store in memory cache\n this.memoryCache.set(currentTxHash, dbCached);\n if (dbCached.isCoinbase) {\n // Skip cache if blockHeight is null - needs re-fetch\n if (dbCached.blockHeight !== null && dbCached.blockHeight !== undefined) {\n return { coinbaseHeight: dbCached.blockHeight };\n }\n // Fall through to re-fetch\n } else if (dbCached.inputTxId) {\n currentTxHash = dbCached.inputTxId;\n continue;\n }\n }\n\n // Fetch from network\n const txData = await getTransaction(currentTxHash) as TransactionData;\n if (!txData || !txData.txid) {\n return { coinbaseHeight: null, error: `Failed to fetch tx ${currentTxHash}` };\n }\n\n // Determine if this is a coinbase transaction\n const isCoinbase = this.isCoinbaseTransaction(txData);\n\n // Calculate block height from confirmations (like index.html does)\n let blockHeight: number | null = null;\n if (txData.confirmations && currentBlockHeight !== null && currentBlockHeight !== undefined) {\n blockHeight = currentBlockHeight - txData.confirmations + 1;\n }\n\n // Get input transaction ID (if not coinbase)\n let inputTxId: string | null = null;\n if (!isCoinbase && txData.vin && txData.vin.length > 0 && txData.vin[0].txid) {\n inputTxId = txData.vin[0].txid;\n }\n\n // Cache the result\n const cacheEntry: CacheEntry = {\n blockHeight, // Can be null if confirmations not available\n isCoinbase,\n inputTxId,\n };\n this.memoryCache.set(currentTxHash, cacheEntry);\n await this.saveToDB(currentTxHash, cacheEntry);\n\n if (isCoinbase) {\n return { coinbaseHeight: blockHeight };\n }\n\n if (!inputTxId) {\n return { coinbaseHeight: null, error: \"Could not find input transaction\" };\n }\n\n currentTxHash = inputTxId;\n }\n\n return { coinbaseHeight: null, error: \"Max iterations exceeded\" };\n }\n\n /**\n * Classify a single UTXO\n */\n async classifyUtxo(utxo: UTXO): Promise<ClassificationResult> {\n const txHash = utxo.tx_hash || utxo.txid;\n if (!txHash) {\n return { isVested: false, coinbaseHeight: null, error: \"No transaction hash\" };\n }\n\n try {\n const result = await this.traceToOrigin(txHash);\n if (result.error || result.coinbaseHeight === null) {\n return { isVested: false, coinbaseHeight: null, error: result.error || \"Could not trace to origin\" };\n }\n return {\n isVested: result.coinbaseHeight <= VESTING_THRESHOLD,\n coinbaseHeight: result.coinbaseHeight,\n };\n } catch (err) {\n return {\n isVested: false,\n coinbaseHeight: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /**\n * Classify multiple UTXOs with progress callback\n */\n async classifyUtxos(\n utxos: UTXO[],\n onProgress?: (current: number, total: number) => void\n ): Promise<{\n vested: ClassifiedUTXO[];\n unvested: ClassifiedUTXO[];\n errors: Array<{ utxo: UTXO; error: string }>;\n }> {\n // Get current block height before classification\n currentBlockHeight = await getCurrentBlockHeight();\n\n // Clear memory cache to force re-fetch with current block height\n this.memoryCache.clear();\n\n const vested: ClassifiedUTXO[] = [];\n const unvested: ClassifiedUTXO[] = [];\n const errors: Array<{ utxo: UTXO; error: string }> = [];\n\n for (let i = 0; i < utxos.length; i++) {\n const utxo = utxos[i];\n const result = await this.classifyUtxo(utxo);\n\n if (result.error) {\n errors.push({ utxo, error: result.error });\n // Default to unvested on error for safety\n unvested.push({\n ...utxo,\n vestingStatus: \"error\",\n coinbaseHeight: null,\n });\n } else if (result.isVested) {\n vested.push({\n ...utxo,\n vestingStatus: \"vested\",\n coinbaseHeight: result.coinbaseHeight,\n });\n } else {\n unvested.push({\n ...utxo,\n vestingStatus: \"unvested\",\n coinbaseHeight: result.coinbaseHeight,\n });\n }\n\n // Report progress\n if (onProgress) {\n onProgress(i + 1, utxos.length);\n }\n\n // Yield every 5 UTXOs\n if (i % 5 === 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n return { vested, unvested, errors };\n }\n\n /**\n * Clear all caches\n */\n clearCaches(): void {\n this.memoryCache.clear();\n if (this.db) {\n const tx = this.db.transaction(this.storeName, \"readwrite\");\n tx.objectStore(this.storeName).clear();\n }\n }\n\n /**\n * Destroy caches and delete the IndexedDB database entirely.\n */\n async destroy(): Promise<void> {\n this.memoryCache.clear();\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n if (typeof indexedDB !== 'undefined') {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(this.dbName);\n req.onsuccess = () => resolve();\n req.onerror = () => resolve();\n req.onblocked = () => resolve();\n });\n }\n }\n}\n\nexport const vestingClassifier = new VestingClassifier();\n","import { vestingClassifier } from \"./vesting\";\nimport type {\n UTXO,\n ClassifiedUTXO,\n VestingMode,\n VestingBalances,\n} from \"./types\";\n\ninterface AddressVestingCache {\n classifiedUtxos: {\n vested: ClassifiedUTXO[];\n unvested: ClassifiedUTXO[];\n all: ClassifiedUTXO[];\n };\n vestingBalances: VestingBalances;\n}\n\nclass VestingStateManager {\n private currentMode: VestingMode = \"all\";\n private addressCache = new Map<string, AddressVestingCache>();\n private classificationInProgress = false;\n\n /**\n * Set the current vesting mode\n */\n setMode(mode: VestingMode): void {\n if (![\"all\", \"vested\", \"unvested\"].includes(mode)) {\n throw new Error(`Invalid vesting mode: ${mode}`);\n }\n this.currentMode = mode;\n }\n\n getMode(): VestingMode {\n return this.currentMode;\n }\n\n /**\n * Classify all UTXOs for an address\n */\n async classifyAddressUtxos(\n address: string,\n utxos: UTXO[],\n onProgress?: (current: number, total: number) => void\n ): Promise<void> {\n if (this.classificationInProgress) return;\n\n this.classificationInProgress = true;\n\n try {\n await vestingClassifier.initDB();\n\n const result = await vestingClassifier.classifyUtxos(utxos, onProgress);\n\n // Calculate balances\n const vestedBalance = result.vested.reduce(\n (sum, utxo) => sum + BigInt(utxo.value),\n 0n\n );\n const unvestedBalance = result.unvested.reduce(\n (sum, utxo) => sum + BigInt(utxo.value),\n 0n\n );\n\n // Store in cache\n this.addressCache.set(address, {\n classifiedUtxos: {\n vested: result.vested,\n unvested: result.unvested,\n all: [...result.vested, ...result.unvested],\n },\n vestingBalances: {\n vested: vestedBalance,\n unvested: unvestedBalance,\n all: vestedBalance + unvestedBalance,\n },\n });\n\n // Log any errors\n if (result.errors.length > 0) {\n console.warn(`Vesting classification errors: ${result.errors.length}`);\n result.errors.slice(0, 5).forEach((err) => {\n const txHash = err.utxo.tx_hash || err.utxo.txid;\n console.warn(` ${txHash}: ${err.error}`);\n });\n }\n } finally {\n this.classificationInProgress = false;\n }\n }\n\n /**\n * Get filtered UTXOs based on current vesting mode\n */\n getFilteredUtxos(address: string): ClassifiedUTXO[] {\n const cache = this.addressCache.get(address);\n if (!cache) return [];\n\n switch (this.currentMode) {\n case \"vested\":\n return cache.classifiedUtxos.vested;\n case \"unvested\":\n return cache.classifiedUtxos.unvested;\n default:\n return cache.classifiedUtxos.all;\n }\n }\n\n /**\n * Get all UTXOs regardless of vesting mode (for transactions)\n */\n getAllUtxos(address: string): ClassifiedUTXO[] {\n const cache = this.addressCache.get(address);\n if (!cache) return [];\n return cache.classifiedUtxos.all;\n }\n\n /**\n * Get balance for current vesting mode (in satoshis)\n */\n getBalance(address: string): bigint {\n const cache = this.addressCache.get(address);\n if (!cache) return 0n;\n\n return cache.vestingBalances[this.currentMode];\n }\n\n /**\n * Get all balances for display\n */\n getAllBalances(address: string): VestingBalances {\n const cache = this.addressCache.get(address);\n if (!cache) {\n return { vested: 0n, unvested: 0n, all: 0n };\n }\n return cache.vestingBalances;\n }\n\n /**\n * Check if address has been classified\n */\n hasClassifiedData(address: string): boolean {\n return this.addressCache.has(address);\n }\n\n /**\n * Check if classification is in progress\n */\n isClassifying(): boolean {\n return this.classificationInProgress;\n }\n\n /**\n * Clear cache for an address\n */\n clearAddressCache(address: string): void {\n this.addressCache.delete(address);\n }\n\n /**\n * Clear all caches\n */\n clearAllCaches(): void {\n this.addressCache.clear();\n vestingClassifier.clearCaches();\n }\n}\n\nexport const vestingState = new VestingStateManager();\n","/**\n * WalletAddressHelper - Path-based address lookup and mutation utilities\n *\n * Key principle: A BIP32 path ALWAYS derives the same address from a given master key.\n * - If we try to add a different address for an existing path → FATAL ERROR\n * - This indicates corruption, wrong derivation, or data integrity issue\n *\n * Performance: O(n) lookup is negligible for typical wallet sizes (5-100 addresses)\n */\n\nimport type { Wallet, WalletAddress } from \"./types\";\n\nexport class WalletAddressHelper {\n /**\n * Find address by BIP32 derivation path\n * @param wallet - The wallet to search\n * @param path - Full BIP32 path like \"m/84'/1'/0'/0/5\"\n * @returns The address if found, undefined otherwise\n */\n static findByPath(wallet: Wallet, path: string): WalletAddress | undefined {\n return wallet.addresses.find((a) => a.path === path);\n }\n\n /**\n * Get the default address (first external/non-change address)\n * This replaces `wallet.addresses[0]` pattern for safer access\n *\n * @param wallet - The wallet\n * @returns First non-change address, or first address if all are change\n */\n static getDefault(wallet: Wallet): WalletAddress {\n return wallet.addresses.find((a) => !a.isChange) ?? wallet.addresses[0];\n }\n\n /**\n * Get the default address, or undefined if wallet has no addresses\n * Safe version that doesn't throw on empty wallet\n */\n static getDefaultOrNull(wallet: Wallet): WalletAddress | undefined {\n if (!wallet.addresses || wallet.addresses.length === 0) {\n return undefined;\n }\n return wallet.addresses.find((a) => !a.isChange) ?? wallet.addresses[0];\n }\n\n /**\n * Add new address to wallet (immutable operation)\n *\n * THROWS if address with same path but different address string already exists.\n * This indicates a serious derivation or data corruption issue.\n *\n * If the same path+address already exists, returns wallet unchanged (idempotent).\n *\n * @param wallet - The wallet to add to\n * @param newAddress - The address to add\n * @returns New wallet object with address added\n * @throws Error if path exists with different address (corruption indicator)\n */\n static add(wallet: Wallet, newAddress: WalletAddress): Wallet {\n if (!newAddress.path) {\n throw new Error(\"Cannot add address without a path\");\n }\n\n const existing = this.findByPath(wallet, newAddress.path);\n\n if (existing) {\n // Path exists - verify it's the SAME address\n if (existing.address !== newAddress.address) {\n throw new Error(\n `CRITICAL: Attempted to overwrite address for path ${newAddress.path}\\n` +\n `Existing: ${existing.address}\\n` +\n `New: ${newAddress.address}\\n` +\n `This indicates master key corruption or derivation logic error.`\n );\n }\n\n // Same path + same address = idempotent, return unchanged\n return wallet;\n }\n\n // New path - add to array\n return {\n ...wallet,\n addresses: [...wallet.addresses, newAddress],\n };\n }\n\n /**\n * Remove address by path (immutable operation)\n * @param wallet - The wallet to modify\n * @param path - The path of the address to remove\n * @returns New wallet object with address removed\n */\n static removeByPath(wallet: Wallet, path: string): Wallet {\n return {\n ...wallet,\n addresses: wallet.addresses.filter((a) => a.path !== path),\n };\n }\n\n /**\n * Get all external (non-change) addresses\n * @param wallet - The wallet\n * @returns Array of external addresses\n */\n static getExternal(wallet: Wallet): WalletAddress[] {\n return wallet.addresses.filter((a) => !a.isChange);\n }\n\n /**\n * Get all change addresses\n * @param wallet - The wallet\n * @returns Array of change addresses\n */\n static getChange(wallet: Wallet): WalletAddress[] {\n return wallet.addresses.filter((a) => a.isChange);\n }\n\n /**\n * Check if wallet has an address with the given path\n * @param wallet - The wallet to check\n * @param path - The path to look for\n * @returns true if path exists\n */\n static hasPath(wallet: Wallet, path: string): boolean {\n return wallet.addresses.some((a) => a.path === path);\n }\n\n /**\n * Validate wallet address array integrity\n * Checks for duplicate paths which indicate data corruption\n *\n * @param wallet - The wallet to validate\n * @throws Error if duplicate paths found\n */\n static validate(wallet: Wallet): void {\n const paths = wallet.addresses.map((a) => a.path).filter(Boolean);\n const uniquePaths = new Set(paths);\n\n if (paths.length !== uniquePaths.size) {\n // Find duplicates for error message\n const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);\n throw new Error(\n `CRITICAL: Wallet has duplicate paths: ${duplicates.join(\", \")}\\n` +\n `This indicates data corruption. Please restore from backup.`\n );\n }\n }\n\n /**\n * Sort addresses with external first, then change, each sorted by index\n * Useful for display purposes\n *\n * @param wallet - The wallet\n * @returns New wallet with sorted addresses\n */\n static sortAddresses(wallet: Wallet): Wallet {\n const sorted = [...wallet.addresses].sort((a, b) => {\n // External addresses first (isChange = false/undefined)\n const aIsChange = a.isChange ? 1 : 0;\n const bIsChange = b.isChange ? 1 : 0;\n if (aIsChange !== bIsChange) return aIsChange - bIsChange;\n // Then by index\n return a.index - b.index;\n });\n\n return {\n ...wallet,\n addresses: sorted,\n };\n }\n}\n","/**\n * L1 Payments Sub-Module\n *\n * Handles Layer 1 (ALPHA blockchain) transactions including:\n * - Balance queries\n * - UTXO management\n * - Transaction sending\n * - Vesting classification\n * - Transaction history\n */\n\nimport type { FullIdentity } from '../../types';\nimport type { TransportProvider } from '../../transport';\nimport {\n connect as l1Connect,\n disconnect as l1Disconnect,\n isWebSocketConnected,\n getUtxo,\n getBalance as l1GetBalance,\n getTransactionHistory,\n getTransaction as l1GetTransaction,\n getCurrentBlockHeight,\n sendAlpha as l1SendAlpha,\n createTransactionPlan as l1CreateTransactionPlan,\n vestingClassifier,\n VESTING_THRESHOLD,\n type UTXO,\n type Wallet,\n type TransactionDetail,\n} from '../../l1';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface L1SendRequest {\n /** Recipient address */\n to: string;\n /** Amount in satoshis */\n amount: string;\n /** Fee rate in sat/byte */\n feeRate?: number;\n /** Use vested coins only */\n useVested?: boolean;\n /** Memo/OP_RETURN data */\n memo?: string;\n}\n\nexport interface L1SendResult {\n success: boolean;\n txHash?: string;\n fee?: string;\n error?: string;\n}\n\nexport interface L1Balance {\n confirmed: string;\n unconfirmed: string;\n vested: string;\n unvested: string;\n total: string;\n}\n\nexport interface L1Utxo {\n txid: string;\n vout: number;\n amount: string;\n address: string;\n isVested: boolean;\n confirmations: number;\n coinbaseHeight?: number;\n}\n\nexport interface L1Transaction {\n txid: string;\n type: 'send' | 'receive';\n amount: string;\n fee?: string;\n address: string;\n confirmations: number;\n timestamp: number;\n blockHeight?: number;\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface L1PaymentsModuleConfig {\n /** Fulcrum server URL */\n electrumUrl?: string;\n /** Network: mainnet or testnet */\n network?: 'mainnet' | 'testnet';\n /** Default fee rate */\n defaultFeeRate?: number;\n /** Enable vesting classification */\n enableVesting?: boolean;\n}\n\n// =============================================================================\n// Dependencies\n// =============================================================================\n\nexport interface L1PaymentsModuleDependencies {\n identity: FullIdentity;\n chainCode?: string;\n addresses?: string[];\n /** Transport provider for nametag resolution (optional) */\n transport?: TransportProvider;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * L1 Payments Module - Full Implementation\n *\n * Handles all L1 (ALPHA blockchain) operations including balance queries,\n * transaction sending, UTXO management, and vesting classification.\n */\nexport class L1PaymentsModule {\n private _initialized = false;\n private _config: L1PaymentsModuleConfig;\n private _identity?: FullIdentity;\n private _addresses: string[] = [];\n private _wallet?: Wallet;\n private _transport?: TransportProvider;\n\n constructor(config?: L1PaymentsModuleConfig) {\n this._config = {\n electrumUrl: config?.electrumUrl ?? 'wss://fulcrum.unicity.network:50004',\n network: config?.network ?? 'mainnet',\n defaultFeeRate: config?.defaultFeeRate ?? 10,\n enableVesting: config?.enableVesting ?? true,\n };\n }\n\n async initialize(deps: L1PaymentsModuleDependencies): Promise<void> {\n this._identity = deps.identity;\n this._addresses = deps.addresses ?? [];\n this._transport = deps.transport;\n\n // Build wallet object for L1 SDK functions\n this._wallet = {\n masterPrivateKey: deps.identity.privateKey,\n chainCode: deps.chainCode,\n addresses: [\n {\n address: deps.identity.l1Address,\n publicKey: deps.identity.chainPubkey,\n privateKey: deps.identity.privateKey,\n path: 'm/0',\n index: 0,\n },\n ],\n };\n\n // Add additional addresses\n for (const addr of this._addresses) {\n if (addr !== deps.identity.l1Address) {\n this._wallet.addresses.push({\n address: addr,\n path: null,\n index: this._wallet.addresses.length,\n });\n }\n }\n\n // NOTE: We do NOT connect to Fulcrum here. Connection is deferred to\n // first use (ensureConnected) so that import + scan flows are not\n // disrupted by an early L1 WebSocket connection on the global singleton.\n\n this._initialized = true;\n }\n\n /**\n * Ensure the Fulcrum WebSocket is connected. Called lazily before any\n * operation that needs the network. If the singleton is already connected\n * (e.g. by the address scanner), this is a no-op.\n */\n private async ensureConnected(): Promise<void> {\n if (!isWebSocketConnected() && this._config.electrumUrl) {\n await l1Connect(this._config.electrumUrl);\n }\n }\n\n destroy(): void {\n if (isWebSocketConnected()) {\n l1Disconnect();\n }\n this._initialized = false;\n this._identity = undefined;\n this._addresses = [];\n this._wallet = undefined;\n }\n\n /**\n * Check if a string looks like an L1 address (alpha1... or alphat1...)\n */\n private isL1Address(value: string): boolean {\n return value.startsWith('alpha1') || value.startsWith('alphat1');\n }\n\n /**\n * Resolve recipient to L1 address\n * Supports: L1 address (alpha1...), nametag (with or without @)\n */\n async resolveL1Address(recipient: string): Promise<string> {\n // Explicit nametag with @\n if (recipient.startsWith('@')) {\n const nametag = recipient.slice(1);\n return this.resolveNametagToL1Address(nametag);\n }\n\n // If it looks like an L1 address, return as-is\n if (this.isL1Address(recipient)) {\n return recipient;\n }\n\n // Smart detection: try as nametag\n try {\n const l1Address = await this.resolveNametagToL1Address(recipient);\n return l1Address;\n } catch {\n throw new Error(\n `Recipient \"${recipient}\" is not a valid nametag or L1 address. ` +\n `Use @nametag for explicit nametag or a valid alpha1... address.`\n );\n }\n }\n\n /**\n * Resolve nametag to L1 address using transport provider\n */\n private async resolveNametagToL1Address(nametag: string): Promise<string> {\n if (!this._transport?.resolve) {\n throw new Error('Transport provider does not support resolution');\n }\n\n const info = await this._transport.resolve(nametag);\n if (!info) {\n throw new Error(`Nametag not found: ${nametag}`);\n }\n\n if (!info.l1Address) {\n throw new Error(\n `Nametag @${nametag} does not have L1 address information. ` +\n `The owner needs to update their nametag registration.`\n );\n }\n\n return info.l1Address;\n }\n\n async send(request: L1SendRequest): Promise<L1SendResult> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n if (!this._wallet || !this._identity) {\n return { success: false, error: 'No wallet available' };\n }\n\n try {\n // Resolve recipient to L1 address (supports nametag)\n const recipientAddress = await this.resolveL1Address(request.to);\n\n // Convert amount from satoshis to ALPHA\n const amountAlpha = parseInt(request.amount, 10) / 100_000_000;\n\n // Send using the L1 SDK\n const results = await l1SendAlpha(\n this._wallet,\n recipientAddress,\n amountAlpha,\n this._identity.l1Address\n );\n\n if (results && results.length > 0) {\n // Calculate total fee from all transactions\n const txids = results.map((r) => r.txid);\n return {\n success: true,\n txHash: txids[0], // Return first txid (usually only one)\n };\n } else {\n return {\n success: false,\n error: 'Transaction failed - no results returned',\n };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n async getBalance(): Promise<L1Balance> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const addresses = this._getWatchedAddresses();\n let totalAlpha = 0;\n let vestedSats = BigInt(0);\n let unvestedSats = BigInt(0);\n\n // Get balance for all addresses\n for (const address of addresses) {\n const balance = await l1GetBalance(address);\n totalAlpha += balance;\n }\n\n const totalSats = BigInt(Math.floor(totalAlpha * 100_000_000));\n\n // Calculate vesting if enabled\n if (this._config.enableVesting) {\n await vestingClassifier.initDB();\n const allUtxos = await this._getAllUtxos();\n const classified = await vestingClassifier.classifyUtxos(allUtxos);\n\n for (const utxo of classified.vested) {\n vestedSats += BigInt(utxo.value);\n }\n for (const utxo of classified.unvested) {\n unvestedSats += BigInt(utxo.value);\n }\n }\n\n return {\n confirmed: totalSats.toString(),\n unconfirmed: '0', // Simplified - would need separate tracking\n vested: vestedSats.toString(),\n unvested: unvestedSats.toString(),\n total: totalSats.toString(),\n };\n }\n\n async getUtxos(): Promise<L1Utxo[]> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const result: L1Utxo[] = [];\n const currentHeight = await getCurrentBlockHeight();\n const allUtxos = await this._getAllUtxos();\n\n // Classify if vesting is enabled\n const classifiedVested: Set<string> = new Set();\n const classifiedCoinbaseHeights: Map<string, number | null> = new Map();\n\n if (this._config.enableVesting) {\n await vestingClassifier.initDB();\n const classified = await vestingClassifier.classifyUtxos(allUtxos);\n\n for (const utxo of classified.vested) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n classifiedVested.add(key);\n classifiedCoinbaseHeights.set(key, utxo.coinbaseHeight ?? null);\n }\n for (const utxo of classified.unvested) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n classifiedCoinbaseHeights.set(key, utxo.coinbaseHeight ?? null);\n }\n }\n\n for (const utxo of allUtxos) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n const isVested = classifiedVested.has(key);\n const coinbaseHeight = classifiedCoinbaseHeights.get(key) ?? undefined;\n\n result.push({\n txid: utxo.tx_hash ?? utxo.txid ?? '',\n vout: utxo.tx_pos ?? utxo.vout ?? 0,\n amount: utxo.value.toString(),\n address: utxo.address ?? '',\n isVested,\n confirmations: currentHeight - (utxo.height || currentHeight),\n coinbaseHeight: coinbaseHeight ?? undefined,\n });\n }\n\n return result;\n }\n\n async getHistory(limit?: number): Promise<L1Transaction[]> {\n await this.ensureConnected();\n this.ensureInitialized();\n\n const addresses = this._getWatchedAddresses();\n const transactions: L1Transaction[] = [];\n const seenTxids = new Set<string>();\n const currentHeight = await getCurrentBlockHeight();\n\n // Cache for fetched transactions (avoids re-fetching the same tx)\n const txCache = new Map<string, TransactionDetail | null>();\n const fetchTx = async (txid: string): Promise<TransactionDetail | null> => {\n if (txCache.has(txid)) return txCache.get(txid)!;\n const detail = (await l1GetTransaction(txid)) as TransactionDetail | null;\n txCache.set(txid, detail);\n return detail;\n };\n\n const addressSet = new Set(addresses.map((a) => a.toLowerCase()));\n\n for (const address of addresses) {\n const history = await getTransactionHistory(address);\n\n for (const item of history) {\n if (seenTxids.has(item.tx_hash)) continue;\n seenTxids.add(item.tx_hash);\n\n const tx = await fetchTx(item.tx_hash);\n if (!tx) continue;\n\n // Resolve input addresses by looking up previous transactions\n let isSend = false;\n for (const vin of (tx.vin ?? [])) {\n if (!vin.txid) continue;\n const prevTx = await fetchTx(vin.txid);\n if (prevTx?.vout?.[vin.vout]) {\n const prevOut = prevTx.vout[vin.vout];\n const prevAddrs = [\n ...(prevOut.scriptPubKey?.addresses ?? []),\n ...(prevOut.scriptPubKey?.address ? [prevOut.scriptPubKey.address] : []),\n ];\n if (prevAddrs.some((a) => addressSet.has(a.toLowerCase()))) {\n isSend = true;\n break;\n }\n }\n }\n\n // Calculate amounts: sum outputs to us vs outputs to others\n let amountToUs = 0;\n let amountToOthers = 0;\n let txAddress = address;\n let externalAddress = '';\n if (tx.vout) {\n for (const vout of tx.vout) {\n const voutAddresses = [\n ...(vout.scriptPubKey?.addresses ?? []),\n ...(vout.scriptPubKey?.address ? [vout.scriptPubKey.address] : []),\n ];\n const isOurs = voutAddresses.some((a) => addressSet.has(a.toLowerCase()));\n const valueSats = Math.floor((vout.value ?? 0) * 100_000_000);\n if (isOurs) {\n amountToUs += valueSats;\n if (!txAddress) txAddress = voutAddresses[0];\n } else {\n amountToOthers += valueSats;\n if (!externalAddress && voutAddresses.length > 0) {\n externalAddress = voutAddresses[0];\n }\n }\n }\n }\n\n // For sends: amount is what went to external addresses; address is the recipient\n // For receives: amount is what came to us; address is our address\n const amount = isSend ? amountToOthers.toString() : amountToUs.toString();\n const displayAddress = isSend ? (externalAddress || txAddress) : txAddress;\n\n transactions.push({\n txid: item.tx_hash,\n type: isSend ? 'send' : 'receive',\n amount,\n address: displayAddress,\n confirmations: item.height > 0 ? currentHeight - item.height : 0,\n timestamp: tx.time ? tx.time * 1000 : Date.now(),\n blockHeight: item.height > 0 ? item.height : undefined,\n });\n }\n }\n\n // Sort by block height descending\n transactions.sort((a, b) => (b.blockHeight ?? 0) - (a.blockHeight ?? 0));\n\n return limit ? transactions.slice(0, limit) : transactions;\n }\n\n async getTransaction(txid: string): Promise<L1Transaction | null> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const tx = (await l1GetTransaction(txid)) as TransactionDetail | null;\n if (!tx) return null;\n\n const addresses = this._getWatchedAddresses();\n const currentHeight = await getCurrentBlockHeight();\n\n // Determine if this is a send (our address in inputs)\n const isSend = tx.vin?.some((vin) =>\n addresses.includes(vin.txid ?? '')\n );\n\n let amount = '0';\n let txAddress = '';\n if (tx.vout) {\n for (const vout of tx.vout) {\n const voutAddresses = vout.scriptPubKey?.addresses ?? [];\n if (vout.scriptPubKey?.address) {\n voutAddresses.push(vout.scriptPubKey.address);\n }\n const matchedAddr = voutAddresses.find((a) => addresses.includes(a));\n if (matchedAddr) {\n amount = Math.floor((vout.value ?? 0) * 100_000_000).toString();\n txAddress = matchedAddr;\n break;\n }\n }\n }\n\n return {\n txid,\n type: isSend ? 'send' : 'receive',\n amount,\n address: txAddress,\n confirmations: tx.confirmations ?? 0,\n timestamp: tx.time ? tx.time * 1000 : Date.now(),\n blockHeight: tx.confirmations ? currentHeight - tx.confirmations + 1 : undefined,\n };\n }\n\n async estimateFee(\n to: string,\n amount: string\n ): Promise<{ fee: string; feeRate: number }> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n if (!this._wallet) {\n return { fee: '0', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n\n try {\n // Convert satoshis to ALPHA\n const amountAlpha = parseInt(amount, 10) / 100_000_000;\n\n const plan = await l1CreateTransactionPlan(\n this._wallet,\n to,\n amountAlpha\n );\n\n if (!plan.success) {\n return { fee: '0', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n\n // Sum fees from all transactions\n const totalFee = plan.transactions.reduce((sum, tx) => sum + tx.fee, 0);\n\n return {\n fee: totalFee.toString(),\n feeRate: this._config.defaultFeeRate ?? 10,\n };\n } catch {\n return { fee: '10000', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n }\n\n getAddresses(): string[] {\n return [...this._addresses];\n }\n\n addAddress(address: string): void {\n if (!this._addresses.includes(address)) {\n this._addresses.push(address);\n\n // Also add to wallet object\n if (this._wallet) {\n this._wallet.addresses.push({\n address,\n path: null,\n index: this._wallet.addresses.length,\n });\n }\n }\n }\n\n getVestingThreshold(): number {\n return VESTING_THRESHOLD;\n }\n\n isConnected(): boolean {\n return isWebSocketConnected();\n }\n\n private ensureInitialized(): void {\n if (!this._initialized) {\n throw new Error('L1PaymentsModule not initialized');\n }\n }\n\n private _getWatchedAddresses(): string[] {\n const addresses = [...this._addresses];\n if (this._identity?.l1Address && !addresses.includes(this._identity.l1Address)) {\n addresses.unshift(this._identity.l1Address);\n }\n return addresses;\n }\n\n private async _getAllUtxos(): Promise<UTXO[]> {\n const addresses = this._getWatchedAddresses();\n const allUtxos: UTXO[] = [];\n\n for (const addr of addresses) {\n const utxos = await getUtxo(addr);\n allUtxos.push(...utxos);\n }\n\n return allUtxos;\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createL1PaymentsModule(\n config?: L1PaymentsModuleConfig\n): L1PaymentsModule {\n return new L1PaymentsModule(config);\n}\n","/**\n * Token Split Calculator\n * Calculates optimal token splits for partial transfers\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { Token } from '../../types';\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TokenWithAmount {\n sdkToken: SdkToken<any>;\n amount: bigint;\n uiToken: Token;\n}\n\nexport interface SplitPlan {\n /** Tokens that can be transferred directly (exact match or combination) */\n tokensToTransferDirectly: TokenWithAmount[];\n /** Token that needs to be split (if requiresSplit is true) */\n tokenToSplit: TokenWithAmount | null;\n /** Amount to send to recipient from split token */\n splitAmount: bigint | null;\n /** Amount to keep as change from split token */\n remainderAmount: bigint | null;\n /** Total amount being transferred */\n totalTransferAmount: bigint;\n /** Coin type being transferred */\n coinId: string;\n /** Whether a split operation is required */\n requiresSplit: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenSplitCalculator {\n /**\n * Calculate optimal split plan for transferring a specific amount\n *\n * Strategy:\n * 1. Try to find exact match (single token = amount)\n * 2. Try to find combination of tokens that sum to exact amount\n * 3. If no exact match, determine which token to split\n */\n async calculateOptimalSplit(\n availableTokens: Token[],\n targetAmount: bigint,\n targetCoinIdHex: string\n ): Promise<SplitPlan | null> {\n const candidates: TokenWithAmount[] = [];\n\n // Build candidate list from available tokens\n for (const t of availableTokens) {\n if (t.coinId !== targetCoinIdHex) continue;\n if (t.status !== 'confirmed') continue;\n if (!t.sdkData) continue;\n\n try {\n const parsed = JSON.parse(t.sdkData);\n const sdkToken = await SdkToken.fromJSON(parsed);\n const realAmount = this.getTokenBalance(sdkToken, targetCoinIdHex);\n\n if (realAmount <= 0n) {\n console.warn(`[SplitCalculator] Token ${t.id} has 0 balance for coinId ${targetCoinIdHex}`);\n continue;\n }\n\n candidates.push({\n sdkToken,\n amount: realAmount,\n uiToken: t,\n });\n } catch (e) {\n console.warn('[SplitCalculator] Failed to parse token', t.id, e);\n }\n }\n\n // Sort by amount (ascending) for greedy algorithm\n candidates.sort((a, b) => (a.amount < b.amount ? -1 : 1));\n\n // Check total available\n const totalAvailable = candidates.reduce((sum, t) => sum + t.amount, 0n);\n if (totalAvailable < targetAmount) {\n console.error(\n `[SplitCalculator] Insufficient funds. Available: ${totalAvailable}, Required: ${targetAmount}`\n );\n return null;\n }\n\n // Strategy 1: Find exact match\n const exactMatch = candidates.find((t) => t.amount === targetAmount);\n if (exactMatch) {\n return this.createDirectPlan([exactMatch], targetAmount, targetCoinIdHex);\n }\n\n // Strategy 2: Try to find combination of tokens (up to 5)\n const maxCombinationSize = Math.min(5, candidates.length);\n for (let size = 2; size <= maxCombinationSize; size++) {\n const combo = this.findCombinationOfSize(candidates, targetAmount, size);\n if (combo) {\n return this.createDirectPlan(combo, targetAmount, targetCoinIdHex);\n }\n }\n\n // Strategy 3: Greedy selection with split\n const toTransfer: TokenWithAmount[] = [];\n let currentSum = 0n;\n\n for (const candidate of candidates) {\n const newSum = currentSum + candidate.amount;\n\n if (newSum === targetAmount) {\n // Perfect match found during greedy\n toTransfer.push(candidate);\n return this.createDirectPlan(toTransfer, targetAmount, targetCoinIdHex);\n } else if (newSum < targetAmount) {\n // Add to transfer set\n toTransfer.push(candidate);\n currentSum = newSum;\n } else {\n // Need to split this token\n const neededFromThisToken = targetAmount - currentSum;\n const remainderForSender = candidate.amount - neededFromThisToken;\n\n return {\n tokensToTransferDirectly: toTransfer,\n tokenToSplit: candidate,\n splitAmount: neededFromThisToken,\n remainderAmount: remainderForSender,\n totalTransferAmount: targetAmount,\n coinId: targetCoinIdHex,\n requiresSplit: true,\n };\n }\n }\n\n // Should not reach here if totalAvailable >= targetAmount\n return null;\n }\n\n /**\n * Get balance of a specific coin from token (lottery-compatible)\n */\n private getTokenBalance(sdkToken: SdkToken<any>, coinIdHex: string): bigint {\n try {\n if (!sdkToken.coins) return 0n;\n const coinId = CoinId.fromJSON(coinIdHex);\n return sdkToken.coins.get(coinId) ?? 0n;\n } catch {\n return 0n;\n }\n }\n\n /**\n * Create a plan for direct transfer (no split needed)\n */\n private createDirectPlan(\n tokens: TokenWithAmount[],\n total: bigint,\n coinId: string\n ): SplitPlan {\n return {\n tokensToTransferDirectly: tokens,\n tokenToSplit: null,\n splitAmount: null,\n remainderAmount: null,\n totalTransferAmount: total,\n coinId,\n requiresSplit: false,\n };\n }\n\n /**\n * Find a combination of exactly `size` tokens that sum to targetAmount\n */\n private findCombinationOfSize(\n tokens: TokenWithAmount[],\n targetAmount: bigint,\n size: number\n ): TokenWithAmount[] | null {\n const generator = this.generateCombinations(tokens, size);\n\n for (const combo of generator) {\n const sum = combo.reduce((acc, t) => acc + t.amount, 0n);\n if (sum === targetAmount) {\n return combo;\n }\n }\n return null;\n }\n\n /**\n * Generator for k-combinations of tokens\n */\n private *generateCombinations(\n tokens: TokenWithAmount[],\n k: number,\n start: number = 0,\n current: TokenWithAmount[] = []\n ): Generator<TokenWithAmount[]> {\n if (k === 0) {\n yield current;\n return;\n }\n\n for (let i = start; i < tokens.length; i++) {\n yield* this.generateCombinations(tokens, k - 1, i + 1, [\n ...current,\n tokens[i],\n ]);\n }\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createTokenSplitCalculator(): TokenSplitCalculator {\n return new TokenSplitCalculator();\n}\n","/**\n * Token Split Executor\n * Token split operations for payments\n *\n * Split flow:\n * 1. Burn original token\n * 2. Mint two new tokens: one for recipient, one for sender (change)\n * 3. Create transfer commitment for recipient token\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TokenCoinData } from '@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData';\nimport { TokenSplitBuilder } from '@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SplitResult {\n tokenForRecipient: any;\n tokenForSender: any;\n recipientTransferTx: any;\n}\n\nexport interface TokenSplitExecutorConfig {\n stateTransitionClient: any;\n trustBase: any;\n signingService: any;\n}\n\n// =============================================================================\n// Hash Utilities\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenSplitExecutor {\n private client: any;\n private trustBase: any;\n private signingService: any;\n\n constructor(config: TokenSplitExecutorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n }\n\n async executeSplit(\n tokenToSplit: any,\n splitAmount: bigint,\n remainderAmount: bigint,\n coinIdHex: string,\n recipientAddress: any\n ): Promise<SplitResult> {\n const tokenIdHex = toHex(tokenToSplit.id.bytes);\n console.log(`[TokenSplitExecutor] Splitting token ${tokenIdHex.slice(0, 8)}...`);\n\n const coinId = new CoinId(fromHex(coinIdHex));\n const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}`;\n\n // Generate IDs and salts\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Create sender address\n const senderAddressRef = await UnmaskedPredicateReference.create(\n tokenToSplit.type,\n this.signingService.algorithm,\n this.signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const senderAddress = await senderAddressRef.toAddress();\n\n // Build split\n const builder = new TokenSplitBuilder();\n\n const coinDataA = TokenCoinData.create([[coinId, splitAmount]]);\n builder.createToken(recipientTokenId, tokenToSplit.type, new Uint8Array(0), coinDataA, senderAddress, recipientSalt, null);\n\n const coinDataB = TokenCoinData.create([[coinId, remainderAmount]]);\n builder.createToken(senderTokenId, tokenToSplit.type, new Uint8Array(0), coinDataB, senderAddress, senderSalt, null);\n\n const split = await builder.build(tokenToSplit);\n\n // Step 1: Burn\n console.log('[TokenSplitExecutor] Step 1: Burning original token...');\n const burnSalt = await sha256(seedString + '_burn_salt');\n const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn failed: ${burnResponse.status}`);\n }\n\n const burnInclusionProof = await waitInclusionProof(this.trustBase, this.client, burnCommitment);\n const burnTransaction = burnCommitment.toTransaction(burnInclusionProof);\n console.log('[TokenSplitExecutor] Original token burned.');\n\n // Step 2: Mint\n console.log('[TokenSplitExecutor] Step 2: Minting split tokens...');\n const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);\n\n const mintedTokensInfo: Array<{ commitment: any; inclusionProof: any; isForRecipient: boolean; tokenId: any; salt: Uint8Array }> = [];\n\n for (const commitment of mintCommitments) {\n const res = await this.client.submitMintCommitment(commitment);\n if (res.status !== 'SUCCESS' && res.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint split token failed: ${res.status}`);\n }\n\n const proof = await waitInclusionProof(this.trustBase, this.client, commitment);\n const commTokenIdHex = toHex(commitment.transactionData.tokenId.bytes);\n const recipientIdHex = toHex(recipientTokenId.bytes);\n\n mintedTokensInfo.push({\n commitment,\n inclusionProof: proof,\n isForRecipient: commTokenIdHex === recipientIdHex,\n tokenId: commitment.transactionData.tokenId,\n salt: commitment.transactionData.salt,\n });\n }\n console.log('[TokenSplitExecutor] Split tokens minted.');\n\n // Step 3: Reconstruct tokens\n const recipientInfo = mintedTokensInfo.find((t) => t.isForRecipient)!;\n const senderInfo = mintedTokensInfo.find((t) => !t.isForRecipient)!;\n\n const createToken = async (info: typeof recipientInfo, label: string) => {\n const predicate = await UnmaskedPredicate.create(info.tokenId, tokenToSplit.type, this.signingService, HashAlgorithm.SHA256, info.salt);\n const state = new TokenState(predicate, null);\n const token = await Token.mint(this.trustBase, state, info.commitment.toTransaction(info.inclusionProof));\n const verification = await token.verify(this.trustBase);\n if (!verification.isSuccessful) throw new Error(`Token verification failed: ${label}`);\n return token;\n };\n\n const recipientTokenBeforeTransfer = await createToken(recipientInfo, 'Recipient');\n const senderToken = await createToken(senderInfo, 'Sender');\n\n // Step 4: Transfer\n console.log('[TokenSplitExecutor] Step 3: Transferring to recipient...');\n const transferSalt = await sha256(seedString + '_transfer_salt');\n\n const transferCommitment = await TransferCommitment.create(\n recipientTokenBeforeTransfer,\n recipientAddress,\n transferSalt,\n null,\n null,\n this.signingService\n );\n\n const transferRes = await this.client.submitTransferCommitment(transferCommitment);\n if (transferRes.status !== 'SUCCESS' && transferRes.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer failed: ${transferRes.status}`);\n }\n\n const transferProof = await waitInclusionProof(this.trustBase, this.client, transferCommitment);\n const transferTx = transferCommitment.toTransaction(transferProof);\n\n console.log('[TokenSplitExecutor] Split transfer complete!');\n\n return {\n tokenForRecipient: recipientTokenBeforeTransfer,\n tokenForSender: senderToken,\n recipientTransferTx: transferTx,\n };\n }\n}\n\nexport function createTokenSplitExecutor(config: TokenSplitExecutorConfig): TokenSplitExecutor {\n return new TokenSplitExecutor(config);\n}\n","/**\n * Nametag Minter\n * Mints nametag tokens on-chain for PROXY address support\n *\n * Flow (same as Sphere wallet and lottery):\n * 1. Generate salt\n * 2. Create MintTransactionData from nametag\n * 3. Create MintCommitment\n * 4. Submit to aggregator\n * 5. Wait for inclusion proof\n * 6. Create Token with proof\n * 7. Return token for storage\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { DirectAddress } from '@unicitylabs/state-transition-sdk/lib/address/DirectAddress';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { NametagData } from '../../types/txf';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Unicity token type for nametags\n * Same as used in Sphere wallet and lottery\n */\nconst UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface NametagMinterConfig {\n stateTransitionClient: any;\n trustBase: any;\n signingService: SigningService;\n /** Skip trust base verification (dev mode) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface MintNametagResult {\n success: boolean;\n token?: Token<any>;\n nametagData?: NametagData;\n error?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class NametagMinter {\n private client: any;\n private trustBase: any;\n private signingService: SigningService;\n private skipVerification: boolean;\n private debug: boolean;\n\n constructor(config: NametagMinterConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.skipVerification = config.skipVerification ?? false;\n this.debug = config.debug ?? false;\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[NametagMinter]', ...args);\n }\n }\n\n /**\n * Check if a nametag is available (not already minted)\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n try {\n const cleanNametag = nametag.replace('@', '').trim();\n const nametagTokenId = await TokenId.fromNameTag(cleanNametag);\n\n const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);\n return !isMinted;\n } catch (error) {\n this.log('Error checking nametag availability:', error);\n return false;\n }\n }\n\n /**\n * Mint a nametag token on-chain\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @param ownerAddress - The owner's direct address\n * @returns MintNametagResult with token if successful\n */\n async mintNametag(\n nametag: string,\n ownerAddress: DirectAddress\n ): Promise<MintNametagResult> {\n const cleanNametag = nametag.replace('@', '').trim();\n this.log(`Starting mint for nametag: ${cleanNametag}`);\n\n try {\n // 1. Create token ID and type\n const nametagTokenId = await TokenId.fromNameTag(cleanNametag);\n const nametagTokenType = new TokenType(\n Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex')\n );\n\n // 2. Generate deterministic salt from signing key + nametag.\n // This ensures the same wallet can recover its nametag token if lost\n // from local storage, because re-minting produces the same commitment\n // and the aggregator returns REQUEST_ID_EXISTS with the same inclusion proof.\n const nametagBytes = new TextEncoder().encode(cleanNametag);\n const pubKey = this.signingService.publicKey;\n const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);\n saltInput.set(pubKey, 0);\n saltInput.set(nametagBytes, pubKey.length);\n const saltBuffer = await crypto.subtle.digest('SHA-256', saltInput);\n const salt = new Uint8Array(saltBuffer);\n this.log('Generated deterministic salt');\n\n // 3. Create mint transaction data\n const mintData = await MintTransactionData.createFromNametag(\n cleanNametag,\n nametagTokenType,\n ownerAddress,\n salt,\n ownerAddress\n );\n this.log('Created MintTransactionData');\n\n // 4. Create commitment\n const commitment = await MintCommitment.create(mintData);\n this.log('Created MintCommitment');\n\n // 5. Submit to aggregator with retries\n // If the nametag was previously minted by this wallet (same deterministic salt),\n // the aggregator returns REQUEST_ID_EXISTS which is handled as success.\n const MAX_RETRIES = 3;\n let submitSuccess = false;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);\n const response = await this.client.submitMintCommitment(commitment);\n\n if (response.status === 'SUCCESS' || response.status === 'REQUEST_ID_EXISTS') {\n this.log(`Commitment ${response.status === 'REQUEST_ID_EXISTS' ? 'already exists' : 'submitted successfully'}`);\n submitSuccess = true;\n break;\n } else {\n this.log(`Commitment failed: ${response.status}`);\n if (attempt === MAX_RETRIES) {\n return {\n success: false,\n error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`,\n };\n }\n await new Promise(r => setTimeout(r, 1000 * attempt));\n }\n } catch (error) {\n this.log(`Attempt ${attempt} error:`, error);\n if (attempt === MAX_RETRIES) {\n return {\n success: false,\n error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n await new Promise(r => setTimeout(r, 1000 * attempt));\n }\n }\n\n if (!submitSuccess) {\n return {\n success: false,\n error: 'Failed to submit commitment after retries',\n };\n }\n\n // 6. Wait for inclusion proof\n this.log('Waiting for inclusion proof...');\n const inclusionProof = await waitInclusionProof(this.trustBase, this.client, commitment);\n this.log('Received inclusion proof');\n\n // 7. Create genesis transaction\n const genesisTransaction = commitment.toTransaction(inclusionProof);\n\n // 8. Create token predicate and state\n const nametagPredicate = await UnmaskedPredicate.create(\n nametagTokenId,\n nametagTokenType,\n this.signingService,\n HashAlgorithm.SHA256,\n salt\n );\n\n const tokenState = new TokenState(nametagPredicate, null);\n\n // 9. Create final token\n let token: Token<any>;\n\n if (this.skipVerification) {\n this.log('Creating token WITHOUT verification (dev mode)');\n const tokenJson = {\n version: '2.0',\n state: tokenState.toJSON(),\n genesis: genesisTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n token = await Token.fromJSON(tokenJson);\n } else {\n token = await Token.mint(\n this.trustBase,\n tokenState,\n genesisTransaction\n );\n }\n\n this.log(`Nametag minted successfully: ${cleanNametag}`);\n\n // 10. Create NametagData for storage\n const nametagData: NametagData = {\n name: cleanNametag,\n token: token.toJSON(),\n timestamp: Date.now(),\n format: 'txf',\n version: '2.0',\n };\n\n return {\n success: true,\n token,\n nametagData,\n };\n } catch (error) {\n this.log('Minting failed:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createNametagMinter(config: NametagMinterConfig): NametagMinter {\n return new NametagMinter(config);\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} 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} as const;\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n/** Default API key for aggregator authentication */\nexport const DEFAULT_AGGREGATOR_API_KEY = 'sk_06365a9c44654841a366068bcfc68986' as const;\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://ipfs.unicity.network',\n 'https://dweb.link',\n 'https://ipfs.io',\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// =============================================================================\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.alpha.unicity.network:50004' as const;\n\n/** Testnet Fulcrum electrum server */\nexport const TEST_ELECTRUM_URL = 'wss://fulcrum.alpha.testnet.unicity.network:50004' as const;\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/** 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 },\n testnet: {\n name: 'Testnet',\n aggregatorUrl: TEST_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n },\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 },\n} as const;\n\nexport type NetworkType = keyof typeof NETWORKS;\nexport type NetworkConfig = (typeof NETWORKS)[NetworkType];\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n/** Validation limits */\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * TXF (Token eXchange Format) Type Definitions\n * Based on TXF Format Specification v2.0\n *\n * These types define the serialization format for tokens,\n * independent of any UI or storage implementation.\n */\n\n// =============================================================================\n// TXF Token Structure (v2.0)\n// =============================================================================\n\n/**\n * Complete token object in TXF format\n */\nexport interface TxfToken {\n version: '2.0';\n genesis: TxfGenesis;\n state: TxfState;\n transactions: TxfTransaction[];\n nametags?: string[];\n _integrity?: TxfIntegrity;\n}\n\n/**\n * Genesis transaction (initial minting)\n */\nexport interface TxfGenesis {\n data: TxfGenesisData;\n inclusionProof: TxfInclusionProof;\n}\n\n/**\n * Genesis data payload\n */\nexport interface TxfGenesisData {\n tokenId: string; // 64-char hex\n tokenType: string; // 64-char hex\n coinData: [string, string][]; // [[coinId, amount], ...]\n tokenData: string; // Optional metadata\n salt: string; // 64-char hex\n recipient: string; // DIRECT://... address\n recipientDataHash: string | null;\n reason: string | null;\n}\n\n/**\n * Current token state\n */\nexport interface TxfState {\n data: string;\n predicate: string; // Hex-encoded CBOR predicate\n}\n\n/**\n * State transition transaction\n */\nexport interface TxfTransaction {\n previousStateHash: string;\n newStateHash?: string;\n predicate: string;\n inclusionProof: TxfInclusionProof | null; // null = uncommitted\n data?: Record<string, unknown>;\n}\n\n/**\n * Sparse Merkle Tree inclusion proof\n */\nexport interface TxfInclusionProof {\n authenticator: TxfAuthenticator;\n merkleTreePath: TxfMerkleTreePath;\n transactionHash: string;\n unicityCertificate: string; // Hex-encoded CBOR\n}\n\n/**\n * Proof authenticator\n */\nexport interface TxfAuthenticator {\n algorithm: string;\n publicKey: string;\n signature: string;\n stateHash: string;\n}\n\n/**\n * Merkle tree path for proof verification\n */\nexport interface TxfMerkleTreePath {\n root: string;\n steps: TxfMerkleStep[];\n}\n\n/**\n * Single step in merkle path\n */\nexport interface TxfMerkleStep {\n data: string;\n path: string;\n}\n\n/**\n * Token integrity metadata\n */\nexport interface TxfIntegrity {\n genesisDataJSONHash: string;\n currentStateHash?: string;\n}\n\n// =============================================================================\n// Storage Format (for IPFS/File storage)\n// =============================================================================\n\n/**\n * Nametag data (one per identity)\n */\nexport interface NametagData {\n name: string;\n token: object;\n timestamp: number;\n format: string;\n version: string;\n}\n\n/**\n * Tombstone entry for tracking spent token states\n */\nexport interface TombstoneEntry {\n tokenId: string;\n stateHash: string;\n timestamp: number;\n}\n\n/**\n * Invalidated nametag entry\n */\nexport interface InvalidatedNametagEntry {\n name: string;\n token: object;\n timestamp: number;\n format: string;\n version: string;\n invalidatedAt: number;\n invalidationReason: string;\n}\n\n/**\n * Outbox entry for pending transfers\n */\nexport interface OutboxEntry {\n id: string;\n status: 'pending' | 'submitted' | 'confirmed' | 'delivered' | 'failed';\n sourceTokenId: string;\n salt: string;\n commitmentJson: string;\n recipientPubkey: string;\n recipientNametag?: string;\n amount: string;\n createdAt: number;\n updatedAt: number;\n error?: string;\n retryCount?: number;\n}\n\n/**\n * Mint outbox entry for pending mints\n */\nexport interface MintOutboxEntry {\n id: string;\n status: 'pending' | 'submitted' | 'confirmed' | 'failed';\n type: 'split' | 'faucet' | 'other';\n salt: string;\n requestIdHex: string;\n mintDataJson: string;\n createdAt: number;\n updatedAt: number;\n error?: string;\n}\n\n/**\n * Storage metadata\n */\nexport interface TxfMeta {\n version: number;\n address: string;\n ipnsName: string;\n formatVersion: '2.0';\n lastCid?: string;\n deviceId?: string;\n}\n\n/**\n * Complete storage data structure\n */\nexport interface TxfStorageData {\n _meta: TxfMeta;\n _nametag?: NametagData;\n _tombstones?: TombstoneEntry[];\n _invalidatedNametags?: InvalidatedNametagEntry[];\n _outbox?: OutboxEntry[];\n _mintOutbox?: MintOutboxEntry[];\n [key: string]: TxfToken | TxfMeta | NametagData | TombstoneEntry[] | InvalidatedNametagEntry[] | OutboxEntry[] | MintOutboxEntry[] | undefined;\n}\n\n// =============================================================================\n// Token Storage Provider Interface\n// =============================================================================\n\n/**\n * Base interface that storage providers must implement\n * to support TXF token storage\n */\nexport interface TxfStorageDataBase {\n _meta: TxfMeta;\n _nametag?: NametagData;\n _tombstones?: TombstoneEntry[];\n _invalidatedNametags?: InvalidatedNametagEntry[];\n _outbox?: OutboxEntry[];\n _mintOutbox?: MintOutboxEntry[];\n [key: string]: unknown;\n}\n\n// =============================================================================\n// Validation Types\n// =============================================================================\n\nexport interface ValidationIssue {\n tokenId: string;\n reason: string;\n recoverable?: boolean;\n}\n\nexport interface TokenValidationResult {\n isValid: boolean;\n reason?: string;\n action?: 'ACCEPT' | 'RETRY_LATER' | 'DISCARD_FORK';\n}\n\n// =============================================================================\n// Key Utilities\n// =============================================================================\n\nconst ARCHIVED_PREFIX = 'archived-';\nconst FORKED_PREFIX = '_forked_';\nconst RESERVED_KEYS = ['_meta', '_nametag', '_tombstones', '_invalidatedNametags', '_outbox', '_mintOutbox', '_sent', '_invalid', '_integrity'];\n\n/**\n * Check if a key is an active token key\n */\nexport function isTokenKey(key: string): boolean {\n return key.startsWith('_') &&\n !key.startsWith(ARCHIVED_PREFIX) &&\n !key.startsWith(FORKED_PREFIX) &&\n !RESERVED_KEYS.includes(key);\n}\n\n/**\n * Check if a key is an archived token key\n */\nexport function isArchivedKey(key: string): boolean {\n return key.startsWith(ARCHIVED_PREFIX);\n}\n\n/**\n * Check if a key is a forked token key\n */\nexport function isForkedKey(key: string): boolean {\n return key.startsWith(FORKED_PREFIX);\n}\n\n/**\n * Extract token ID from storage key\n */\nexport function tokenIdFromKey(key: string): string {\n return key.startsWith('_') ? key.substring(1) : key;\n}\n\n/**\n * Create storage key from token ID\n */\nexport function keyFromTokenId(tokenId: string): string {\n return `_${tokenId}`;\n}\n\n/**\n * Extract token ID from archived key\n */\nexport function tokenIdFromArchivedKey(key: string): string {\n return key.startsWith(ARCHIVED_PREFIX) ? key.substring(ARCHIVED_PREFIX.length) : key;\n}\n\n/**\n * Create archived key from token ID\n */\nexport function archivedKeyFromTokenId(tokenId: string): string {\n return `${ARCHIVED_PREFIX}${tokenId}`;\n}\n\n/**\n * Create forked key from token ID and state hash\n */\nexport function forkedKeyFromTokenIdAndState(tokenId: string, stateHash: string): string {\n return `${FORKED_PREFIX}${tokenId}_${stateHash}`;\n}\n\n/**\n * Parse forked key into tokenId and stateHash\n */\nexport function parseForkedKey(key: string): { tokenId: string; stateHash: string } | null {\n if (!key.startsWith(FORKED_PREFIX)) return null;\n const remainder = key.substring(FORKED_PREFIX.length);\n const underscoreIndex = remainder.indexOf('_');\n if (underscoreIndex === -1 || underscoreIndex < 64) return null;\n return {\n tokenId: remainder.substring(0, underscoreIndex),\n stateHash: remainder.substring(underscoreIndex + 1),\n };\n}\n\n/**\n * Validate 64-character hex token ID\n */\nexport function isValidTokenId(tokenId: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(tokenId);\n}\n","/**\n * TXF Serializer for SDK2\n * Converts between SDK Token format and TXF storage format\n *\n * Platform-independent implementation that works with SDK types directly.\n */\n\nimport type {\n TxfToken,\n TxfTransaction,\n TxfStorageData,\n TxfMeta,\n NametagData,\n TombstoneEntry,\n OutboxEntry,\n MintOutboxEntry,\n InvalidatedNametagEntry,\n} from '../types/txf';\nimport {\n isTokenKey,\n isArchivedKey,\n isForkedKey,\n tokenIdFromKey,\n tokenIdFromArchivedKey,\n parseForkedKey,\n keyFromTokenId,\n archivedKeyFromTokenId,\n forkedKeyFromTokenIdAndState,\n} from '../types/txf';\nimport type { Token, TokenStatus } from '../types';\n\n// =============================================================================\n// SDK Token Normalization\n// =============================================================================\n\n/**\n * Convert bytes array/object to hex string\n */\nfunction bytesToHex(bytes: number[] | Uint8Array): string {\n const arr = Array.isArray(bytes) ? bytes : Array.from(bytes);\n return arr.map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Normalize a value that may be a hex string, bytes object, or Buffer to hex string\n */\nfunction normalizeToHex(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n // SDK format: { bytes: [...] }\n if ('bytes' in obj && (Array.isArray(obj.bytes) || obj.bytes instanceof Uint8Array)) {\n return bytesToHex(obj.bytes as number[] | Uint8Array);\n }\n // Buffer.toJSON() format: { type: \"Buffer\", data: [...] }\n if (obj.type === 'Buffer' && Array.isArray(obj.data)) {\n return bytesToHex(obj.data as number[]);\n }\n }\n return String(value);\n}\n\n/**\n * Normalize SDK token JSON to canonical TXF storage format.\n * Converts all bytes objects to hex strings before storage.\n */\nexport function normalizeSdkTokenToStorage(sdkTokenJson: unknown): TxfToken {\n const txf = JSON.parse(JSON.stringify(sdkTokenJson));\n\n // Normalize genesis.data fields\n if (txf.genesis?.data) {\n const data = txf.genesis.data;\n if (data.tokenId !== undefined) {\n data.tokenId = normalizeToHex(data.tokenId);\n }\n if (data.tokenType !== undefined) {\n data.tokenType = normalizeToHex(data.tokenType);\n }\n if (data.salt !== undefined) {\n data.salt = normalizeToHex(data.salt);\n }\n }\n\n // Normalize authenticator fields in genesis inclusion proof\n if (txf.genesis?.inclusionProof?.authenticator) {\n const auth = txf.genesis.inclusionProof.authenticator;\n if (auth.publicKey !== undefined) {\n auth.publicKey = normalizeToHex(auth.publicKey);\n }\n if (auth.signature !== undefined) {\n auth.signature = normalizeToHex(auth.signature);\n }\n }\n\n // Normalize transaction authenticators\n if (Array.isArray(txf.transactions)) {\n for (const tx of txf.transactions) {\n if (tx.inclusionProof?.authenticator) {\n const auth = tx.inclusionProof.authenticator;\n if (auth.publicKey !== undefined) {\n auth.publicKey = normalizeToHex(auth.publicKey);\n }\n if (auth.signature !== undefined) {\n auth.signature = normalizeToHex(auth.signature);\n }\n }\n }\n }\n\n return txf as TxfToken;\n}\n\n// =============================================================================\n// Token → TXF Conversion\n// =============================================================================\n\n/**\n * Extract TXF token structure from Token.sdkData (jsonData)\n */\nexport function tokenToTxf(token: Token): TxfToken | null {\n const jsonData = token.sdkData;\n if (!jsonData) {\n return null;\n }\n\n try {\n const txfData = normalizeSdkTokenToStorage(JSON.parse(jsonData));\n\n if (!txfData.genesis || !txfData.state) {\n return null;\n }\n\n // Ensure required fields\n if (!txfData.version) {\n txfData.version = '2.0';\n }\n if (!txfData.transactions) {\n txfData.transactions = [];\n }\n if (!txfData.nametags) {\n txfData.nametags = [];\n }\n if (!txfData._integrity) {\n txfData._integrity = {\n genesisDataJSONHash: '0000' + '0'.repeat(60),\n };\n }\n\n return txfData;\n } catch {\n return null;\n }\n}\n\n/**\n * Convert token interface to simplified Token for parsing\n */\ninterface TokenLike {\n id: string;\n sdkData?: string;\n}\n\n/**\n * Extract TXF from any object with id and sdkData\n */\nexport function objectToTxf(obj: TokenLike): TxfToken | null {\n if (!obj.sdkData) return null;\n try {\n const txfData = normalizeSdkTokenToStorage(JSON.parse(obj.sdkData));\n if (!txfData.genesis || !txfData.state) return null;\n return txfData;\n } catch {\n return null;\n }\n}\n\n// =============================================================================\n// TXF → Token Conversion\n// =============================================================================\n\n/**\n * Determine token status from TXF data\n */\nfunction determineTokenStatus(txf: TxfToken): TokenStatus {\n if (txf.transactions.length > 0) {\n const lastTx = txf.transactions[txf.transactions.length - 1];\n if (lastTx.inclusionProof === null) {\n return 'pending';\n }\n }\n return 'confirmed';\n}\n\n/**\n * Convert TXF token to Token interface\n */\nexport function txfToToken(tokenId: string, txf: TxfToken): Token {\n const coinData = txf.genesis.data.coinData;\n const totalAmount = coinData.reduce((sum, [, amt]) => {\n return sum + BigInt(amt || '0');\n }, BigInt(0));\n\n // Get coin ID (use first non-zero coin, or first coin)\n let coinId = coinData[0]?.[0] || '';\n for (const [cid, amt] of coinData) {\n if (BigInt(amt || '0') > 0) {\n coinId = cid;\n break;\n }\n }\n\n const tokenType = txf.genesis.data.tokenType;\n const isNft = tokenType === '455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89';\n\n const now = Date.now();\n\n return {\n id: tokenId,\n coinId,\n symbol: isNft ? 'NFT' : 'UCT',\n name: isNft ? 'NFT' : 'Token',\n decimals: isNft ? 0 : 8,\n amount: totalAmount.toString(),\n status: determineTokenStatus(txf),\n createdAt: now,\n updatedAt: now,\n sdkData: JSON.stringify(txf),\n };\n}\n\n// =============================================================================\n// Storage Data Building\n// =============================================================================\n\n/**\n * Build TXF storage data from tokens and metadata\n */\nexport async function buildTxfStorageData(\n tokens: Token[],\n meta: Omit<TxfMeta, 'formatVersion'>,\n options?: {\n nametag?: NametagData;\n tombstones?: TombstoneEntry[];\n archivedTokens?: Map<string, TxfToken>;\n forkedTokens?: Map<string, TxfToken>;\n outboxEntries?: OutboxEntry[];\n mintOutboxEntries?: MintOutboxEntry[];\n invalidatedNametags?: InvalidatedNametagEntry[];\n }\n): Promise<TxfStorageData> {\n const storageData: TxfStorageData = {\n _meta: {\n ...meta,\n formatVersion: '2.0',\n },\n };\n\n // Note: nametag is no longer saved here to avoid duplication.\n // Nametag is saved separately via saveNametagToFileStorage() as nametag-{name}.json\n // The options.nametag parameter is kept for backwards compatibility but ignored.\n\n if (options?.tombstones && options.tombstones.length > 0) {\n storageData._tombstones = options.tombstones;\n }\n\n if (options?.outboxEntries && options.outboxEntries.length > 0) {\n storageData._outbox = options.outboxEntries;\n }\n\n if (options?.mintOutboxEntries && options.mintOutboxEntries.length > 0) {\n storageData._mintOutbox = options.mintOutboxEntries;\n }\n\n if (options?.invalidatedNametags && options.invalidatedNametags.length > 0) {\n storageData._invalidatedNametags = options.invalidatedNametags;\n }\n\n // Add active tokens\n for (const token of tokens) {\n const txf = tokenToTxf(token);\n if (txf) {\n const actualTokenId = txf.genesis.data.tokenId;\n storageData[keyFromTokenId(actualTokenId)] = txf;\n }\n }\n\n // Add archived tokens\n if (options?.archivedTokens && options.archivedTokens.size > 0) {\n for (const [tokenId, txf] of options.archivedTokens) {\n storageData[archivedKeyFromTokenId(tokenId)] = txf;\n }\n }\n\n // Add forked tokens\n if (options?.forkedTokens && options.forkedTokens.size > 0) {\n for (const [key, txf] of options.forkedTokens) {\n const [tokenId, stateHash] = key.split('_');\n if (tokenId && stateHash) {\n storageData[forkedKeyFromTokenIdAndState(tokenId, stateHash)] = txf;\n }\n }\n }\n\n return storageData;\n}\n\n// =============================================================================\n// Storage Data Parsing\n// =============================================================================\n\nexport interface ParsedStorageData {\n tokens: Token[];\n meta: TxfMeta | null;\n nametag: NametagData | null;\n tombstones: TombstoneEntry[];\n archivedTokens: Map<string, TxfToken>;\n forkedTokens: Map<string, TxfToken>;\n outboxEntries: OutboxEntry[];\n mintOutboxEntries: MintOutboxEntry[];\n invalidatedNametags: InvalidatedNametagEntry[];\n validationErrors: string[];\n}\n\n/**\n * Parse TXF storage data\n */\nexport function parseTxfStorageData(data: unknown): ParsedStorageData {\n const result: ParsedStorageData = {\n tokens: [],\n meta: null,\n nametag: null,\n tombstones: [],\n archivedTokens: new Map(),\n forkedTokens: new Map(),\n outboxEntries: [],\n mintOutboxEntries: [],\n invalidatedNametags: [],\n validationErrors: [],\n };\n\n if (!data || typeof data !== 'object') {\n result.validationErrors.push('Storage data is not an object');\n return result;\n }\n\n const storageData = data as Record<string, unknown>;\n\n // Extract metadata\n if (storageData._meta && typeof storageData._meta === 'object') {\n result.meta = storageData._meta as TxfMeta;\n }\n\n // Extract nametag\n if (storageData._nametag && typeof storageData._nametag === 'object') {\n result.nametag = storageData._nametag as NametagData;\n }\n\n // Extract tombstones\n if (storageData._tombstones && Array.isArray(storageData._tombstones)) {\n for (const entry of storageData._tombstones) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as TombstoneEntry).tokenId === 'string' &&\n typeof (entry as TombstoneEntry).stateHash === 'string' &&\n typeof (entry as TombstoneEntry).timestamp === 'number'\n ) {\n result.tombstones.push(entry as TombstoneEntry);\n }\n }\n }\n\n // Extract outbox entries\n if (storageData._outbox && Array.isArray(storageData._outbox)) {\n for (const entry of storageData._outbox) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as OutboxEntry).id === 'string' &&\n typeof (entry as OutboxEntry).status === 'string'\n ) {\n result.outboxEntries.push(entry as OutboxEntry);\n }\n }\n }\n\n // Extract mint outbox entries\n if (storageData._mintOutbox && Array.isArray(storageData._mintOutbox)) {\n for (const entry of storageData._mintOutbox) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as MintOutboxEntry).id === 'string' &&\n typeof (entry as MintOutboxEntry).status === 'string'\n ) {\n result.mintOutboxEntries.push(entry as MintOutboxEntry);\n }\n }\n }\n\n // Extract invalidated nametags\n if (storageData._invalidatedNametags && Array.isArray(storageData._invalidatedNametags)) {\n for (const entry of storageData._invalidatedNametags) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as InvalidatedNametagEntry).name === 'string'\n ) {\n result.invalidatedNametags.push(entry as InvalidatedNametagEntry);\n }\n }\n }\n\n // Extract tokens\n for (const key of Object.keys(storageData)) {\n // Active tokens\n if (isTokenKey(key)) {\n const tokenId = tokenIdFromKey(key);\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n const token = txfToToken(tokenId, txfToken);\n result.tokens.push(token);\n }\n } catch (err) {\n result.validationErrors.push(`Token ${tokenId}: ${err}`);\n }\n }\n // Archived tokens\n else if (isArchivedKey(key)) {\n const tokenId = tokenIdFromArchivedKey(key);\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n result.archivedTokens.set(tokenId, txfToken);\n }\n } catch {\n result.validationErrors.push(`Archived token ${tokenId}: invalid structure`);\n }\n }\n // Forked tokens\n else if (isForkedKey(key)) {\n const parsed = parseForkedKey(key);\n if (parsed) {\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n const mapKey = `${parsed.tokenId}_${parsed.stateHash}`;\n result.forkedTokens.set(mapKey, txfToken);\n }\n } catch {\n result.validationErrors.push(`Forked token ${parsed.tokenId}: invalid structure`);\n }\n }\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Get token ID from Token object (prefers genesis.data.tokenId)\n */\nexport function getTokenId(token: Token): string {\n if (token.sdkData) {\n try {\n const txf = JSON.parse(token.sdkData);\n if (txf.genesis?.data?.tokenId) {\n return txf.genesis.data.tokenId;\n }\n } catch {\n // Fall through\n }\n }\n return token.id;\n}\n\n/**\n * Get the current state hash from a TXF token\n * Checks multiple sources in order of preference:\n * 1. Last transaction's newStateHash\n * 2. _integrity.currentStateHash\n * 3. Last transaction's inclusionProof authenticator stateHash\n * 4. Genesis inclusionProof authenticator stateHash (for never-transferred tokens)\n */\nexport function getCurrentStateHash(txf: TxfToken): string | undefined {\n // Check last transaction's explicit newStateHash\n if (txf.transactions && txf.transactions.length > 0) {\n const lastTx = txf.transactions[txf.transactions.length - 1];\n if (lastTx?.newStateHash) {\n return lastTx.newStateHash;\n }\n // Check authenticator stateHash from last transaction's proof\n if (lastTx?.inclusionProof?.authenticator?.stateHash) {\n return lastTx.inclusionProof.authenticator.stateHash;\n }\n }\n\n // Check integrity metadata\n if (txf._integrity?.currentStateHash) {\n return txf._integrity.currentStateHash;\n }\n\n // For tokens with no transactions, use genesis proof's stateHash\n if (txf.genesis?.inclusionProof?.authenticator?.stateHash) {\n return txf.genesis.inclusionProof.authenticator.stateHash;\n }\n\n return undefined;\n}\n\n/**\n * Check if token has valid TXF data\n */\nexport function hasValidTxfData(token: Token): boolean {\n if (!token.sdkData) return false;\n\n try {\n const txf = JSON.parse(token.sdkData);\n return !!(\n txf.genesis &&\n txf.genesis.data &&\n txf.genesis.data.tokenId &&\n txf.state &&\n txf.genesis.inclusionProof\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if token has uncommitted transactions\n */\nexport function hasUncommittedTransactions(token: Token): boolean {\n if (!token.sdkData) return false;\n\n try {\n const txf = JSON.parse(token.sdkData);\n if (!txf.transactions || txf.transactions.length === 0) return false;\n\n return txf.transactions.some(\n (tx: TxfTransaction) => tx.inclusionProof === null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a TXF token has missing newStateHash on any transaction\n */\nexport function hasMissingNewStateHash(txf: TxfToken): boolean {\n if (!txf.transactions || txf.transactions.length === 0) {\n return false;\n }\n return txf.transactions.some(tx => !tx.newStateHash);\n}\n\n/**\n * Count committed transactions in a token\n */\nexport function countCommittedTransactions(token: Token): number {\n if (!token.sdkData) return 0;\n\n try {\n const txf = JSON.parse(token.sdkData);\n if (!txf.transactions) return 0;\n\n return txf.transactions.filter(\n (tx: TxfTransaction) => tx.inclusionProof !== null\n ).length;\n } catch {\n return 0;\n }\n}\n","[\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"non-fungible\",\n \"name\": \"unicity\",\n \"description\": \"Unicity testnet token type\",\n \"id\": \"f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity\",\n \"symbol\": \"UCT\",\n \"decimals\": 18,\n \"description\": \"Unicity testnet native coin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity_logo_32.png\" }\n ],\n \"id\": \"455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity-usd\",\n \"symbol\": \"USDU\",\n \"decimals\": 6,\n \"description\": \"Unicity testnet USD stablecoin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/usdu_logo_32.png\" }\n ],\n \"id\": \"8f0f3d7a5e7297be0ee98c63b81bcebb2740f43f616566fc290f9823a54f52d7\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity-eur\",\n \"symbol\": \"EURU\",\n \"decimals\": 6,\n \"description\": \"Unicity testnet EUR stablecoin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/euru_logo_32.png\" }\n ],\n \"id\": \"5e160d5e9fdbb03b553fb9c3f6e6c30efa41fa807be39fb4f18e43776e492925\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"solana\",\n \"symbol\": \"SOL\",\n \"decimals\": 9,\n \"description\": \"Solana testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/sol.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/sol.png\" }\n ],\n \"id\": \"dee5f8ce778562eec90e9c38a91296a023210ccc76ff4c29d527ac3eb64ade93\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"bitcoin\",\n \"symbol\": \"BTC\",\n \"decimals\": 8,\n \"description\": \"Bitcoin testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/btc.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/btc.png\" }\n ],\n \"id\": \"86bc190fcf7b2d07c6078de93db803578760148b16d4431aa2f42a3241ff0daa\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"ethereum\",\n \"symbol\": \"ETH\",\n \"decimals\": 18,\n \"description\": \"Ethereum testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/eth.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/eth.png\" }\n ],\n \"id\": \"3c2450f2fd867e7bb60c6a69d7ad0e53ce967078c201a3ecaa6074ed4c0deafb\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"alpha_test\",\n \"symbol\": \"ALPHT\",\n \"decimals\": 8,\n \"description\": \"ALPHA testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/alpha_coin.png\" }\n ],\n \"id\": \"cde78ded16ef65818a51f43138031c4284e519300ab0cb60c30a8f9078080e5f\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"tether\",\n \"symbol\": \"USDT\",\n \"decimals\": 6,\n \"description\": \"Tether (Ethereum) testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdt.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdt.png\" }\n ],\n \"id\": \"40d25444648418fe7efd433e147187a3a6adf049ac62bc46038bda5b960bf690\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"usd-coin\",\n \"symbol\": \"USDC\",\n \"decimals\": 6,\n \"description\": \"USDC (Ethereum) testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdc.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdc.png\" }\n ],\n \"id\": \"2265121770fa6f41131dd9a6cc571e28679263d09a53eb2642e145b5b9a5b0a2\"\n }\n]\n","/**\n * Token Registry\n *\n * Provides token definitions (metadata) for known tokens on the Unicity network.\n * Uses bundled static data for offline access and consistency.\n */\n\nimport testnetRegistry from './token-registry.testnet.json';\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// 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 * @example\n * ```ts\n * import { TokenRegistry } from '@unicitylabs/sphere-sdk';\n *\n * const registry = TokenRegistry.getInstance();\n * const def = registry.getDefinition('455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89');\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 private constructor() {\n this.definitionsById = new Map();\n this.definitionsBySymbol = new Map();\n this.definitionsByName = new Map();\n this.loadRegistry();\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 * Reset the singleton instance (useful for testing)\n */\n static resetInstance(): void {\n TokenRegistry.instance = null;\n }\n\n /**\n * Load registry data from bundled JSON\n */\n private loadRegistry(): void {\n const definitions = testnetRegistry as TokenDefinition[];\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 // 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 * InstantSplitExecutor\n *\n * Optimized token split executor that achieves ~2.3s critical path latency\n * instead of the standard ~42s sequential flow.\n *\n * Key Insight: TransferCommitment.create() only needs token.state, NOT the mint proof.\n * This allows creating transfer commitments immediately after mint data creation,\n * without waiting for mint proofs.\n *\n * V5 Flow (Production Mode):\n * 1. Create burn commitment, submit to aggregator (~50ms)\n * 2. Wait for burn inclusion proof (~2s - unavoidable)\n * 3. Create mint commitments with proper SplitMintReason (~50ms)\n * 4. Create transfer commitment from mint data (~100ms)\n * 5. Package bundle -> send via transport -> SUCCESS (~150ms)\n * TOTAL: ~2.3s\n *\n * Background (non-blocking):\n * 6. Submit mint commitments (parallel)\n * 7. Wait for mint proofs\n * 8. Reconstruct & save change token\n * 9. Sync to storage\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TokenCoinData } from '@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData';\nimport { TokenSplitBuilder } from '@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { IAddress } from '@unicitylabs/state-transition-sdk/lib/address/IAddress';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport type {\n InstantSplitBundleV5,\n InstantSplitResult,\n InstantSplitOptions,\n BackgroundProgressStatus,\n} from '../../types/instant-split';\nimport type { TransportProvider } from '../../transport';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InstantSplitExecutorConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n /** Dev mode skips trust base verification (for testing) */\n devMode?: boolean;\n}\n\nexport interface InstantSplitExecutorDeps {\n stClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n devMode?: boolean;\n}\n\nexport interface BackgroundContext {\n signingService: SigningService;\n tokenType: TokenType;\n coinId: CoinId;\n senderTokenId: TokenId;\n senderSalt: Uint8Array;\n onProgress?: (status: BackgroundProgressStatus) => void;\n onChangeTokenCreated?: (token: Token<any>) => Promise<void>;\n onStorageSync?: () => Promise<boolean>;\n}\n\n// =============================================================================\n// Hash Utilities\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// InstantSplitExecutor Implementation\n// =============================================================================\n\nexport class InstantSplitExecutor {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private signingService: SigningService;\n private devMode: boolean;\n\n constructor(config: InstantSplitExecutorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Execute an instant split transfer with V5 optimized flow.\n *\n * Critical path (~2.3s):\n * 1. Create and submit burn commitment\n * 2. Wait for burn proof\n * 3. Create mint commitments with SplitMintReason\n * 4. Create transfer commitment (no mint proof needed)\n * 5. Send bundle via transport\n *\n * @param tokenToSplit - The SDK token to split\n * @param splitAmount - Amount to send to recipient\n * @param remainderAmount - Amount to keep as change\n * @param coinIdHex - Coin ID in hex format\n * @param recipientAddress - Recipient's address (PROXY or DIRECT)\n * @param transport - Transport provider for sending the bundle\n * @param recipientPubkey - Recipient's transport public key\n * @param options - Optional configuration\n * @returns InstantSplitResult with success status and timing info\n */\n async executeSplitInstant(\n tokenToSplit: Token<any>,\n splitAmount: bigint,\n remainderAmount: bigint,\n coinIdHex: string,\n recipientAddress: IAddress,\n transport: TransportProvider,\n recipientPubkey: string,\n options?: InstantSplitOptions\n ): Promise<InstantSplitResult> {\n const startTime = performance.now();\n const splitGroupId = crypto.randomUUID();\n\n const tokenIdHex = toHex(tokenToSplit.id.bytes);\n console.log(`[InstantSplit] Starting V5 split for token ${tokenIdHex.slice(0, 8)}...`);\n\n try {\n const coinId = new CoinId(fromHex(coinIdHex));\n const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}_${Date.now()}`;\n\n // Generate IDs and salts (deterministic from seed)\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Create sender address (for minting to self first)\n const senderAddressRef = await UnmaskedPredicateReference.create(\n tokenToSplit.type,\n this.signingService.algorithm,\n this.signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const senderAddress = await senderAddressRef.toAddress();\n\n // Build split configuration\n const builder = new TokenSplitBuilder();\n\n // Recipient token (will be transferred)\n const coinDataA = TokenCoinData.create([[coinId, splitAmount]]);\n builder.createToken(\n recipientTokenId,\n tokenToSplit.type,\n new Uint8Array(0),\n coinDataA,\n senderAddress, // Mint to sender first, then transfer\n recipientSalt,\n null\n );\n\n // Sender token (change)\n const coinDataB = TokenCoinData.create([[coinId, remainderAmount]]);\n builder.createToken(\n senderTokenId,\n tokenToSplit.type,\n new Uint8Array(0),\n coinDataB,\n senderAddress,\n senderSalt,\n null\n );\n\n const split = await builder.build(tokenToSplit);\n\n // === STEP 1: CREATE AND SUBMIT BURN COMMITMENT ===\n console.log('[InstantSplit] Step 1: Creating and submitting burn...');\n const burnSalt = await sha256(seedString + '_burn_salt');\n const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn submission failed: ${burnResponse.status}`);\n }\n\n // === STEP 2: WAIT FOR BURN PROOF (~2s) ===\n console.log('[InstantSplit] Step 2: Waiting for burn proof...');\n const burnProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(burnCommitment, options?.burnProofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, burnCommitment);\n const burnTransaction = burnCommitment.toTransaction(burnProof);\n\n const burnDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Burn proof received in ${burnDuration.toFixed(0)}ms`);\n\n options?.onBurnCompleted?.(JSON.stringify(burnTransaction.toJSON()));\n\n // === STEP 3: CREATE MINT COMMITMENTS WITH SPLITMINT REASON ===\n console.log('[InstantSplit] Step 3: Creating mint commitments...');\n const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);\n\n // Find recipient and sender mint commitments\n const recipientIdHex = toHex(recipientTokenId.bytes);\n const senderIdHex = toHex(senderTokenId.bytes);\n\n const recipientMintCommitment = mintCommitments.find(\n (c) => toHex(c.transactionData.tokenId.bytes) === recipientIdHex\n );\n const senderMintCommitment = mintCommitments.find(\n (c) => toHex(c.transactionData.tokenId.bytes) === senderIdHex\n );\n\n if (!recipientMintCommitment || !senderMintCommitment) {\n throw new Error('Failed to find expected mint commitments');\n }\n\n // === STEP 4: CREATE TRANSFER COMMITMENT FROM MINT DATA ===\n console.log('[InstantSplit] Step 4: Creating transfer commitment...');\n const transferSalt = await sha256(seedString + '_transfer_salt');\n\n const transferCommitment = await this.createTransferCommitmentFromMintData(\n recipientMintCommitment.transactionData,\n recipientAddress,\n transferSalt,\n this.signingService\n );\n\n // Create minted token state for recipient to reconstruct\n const mintedPredicate = await UnmaskedPredicate.create(\n recipientTokenId,\n tokenToSplit.type,\n this.signingService,\n HashAlgorithm.SHA256,\n recipientSalt\n );\n const mintedState = new TokenState(mintedPredicate, null);\n\n // === STEP 5: PACKAGE V5 BUNDLE ===\n console.log('[InstantSplit] Step 5: Packaging V5 bundle...');\n const senderPubkey = toHex(this.signingService.publicKey);\n\n // Get nametag token if this is a PROXY address transfer\n let nametagTokenJson: string | undefined;\n const recipientAddressStr = recipientAddress.toString();\n if (recipientAddressStr.startsWith('PROXY://') && tokenToSplit.nametagTokens?.length > 0) {\n // Include sender's nametag token for PROXY verification\n nametagTokenJson = JSON.stringify(tokenToSplit.nametagTokens[0].toJSON());\n }\n\n const bundle: InstantSplitBundleV5 = {\n version: '5.0',\n type: 'INSTANT_SPLIT',\n burnTransaction: JSON.stringify(burnTransaction.toJSON()),\n recipientMintData: JSON.stringify(recipientMintCommitment.transactionData.toJSON()),\n transferCommitment: JSON.stringify(transferCommitment.toJSON()),\n amount: splitAmount.toString(),\n coinId: coinIdHex,\n tokenTypeHex: toHex(tokenToSplit.type.bytes),\n splitGroupId,\n senderPubkey,\n recipientSaltHex: toHex(recipientSalt),\n transferSaltHex: toHex(transferSalt),\n mintedTokenStateJson: JSON.stringify(mintedState.toJSON()),\n finalRecipientStateJson: '', // Recipient creates their own\n recipientAddressJson: recipientAddressStr,\n nametagTokenJson,\n };\n\n // === STEP 6: SEND VIA TRANSPORT ===\n console.log('[InstantSplit] Step 6: Sending via transport...');\n const nostrEventId = await transport.sendTokenTransfer(recipientPubkey, {\n token: JSON.stringify(bundle),\n proof: null, // Proof is included in the bundle\n memo: 'INSTANT_SPLIT_V5',\n sender: {\n transportPubkey: senderPubkey,\n },\n });\n\n const criticalPathDuration = performance.now() - startTime;\n console.log(`[InstantSplit] V5 complete in ${criticalPathDuration.toFixed(0)}ms`);\n\n options?.onNostrDelivered?.(nostrEventId);\n\n // === STEP 7: BACKGROUND PROCESSING ===\n if (!options?.skipBackground) {\n this.submitBackgroundV5(senderMintCommitment, recipientMintCommitment, transferCommitment, {\n signingService: this.signingService,\n tokenType: tokenToSplit.type,\n coinId,\n senderTokenId,\n senderSalt,\n onProgress: options?.onBackgroundProgress,\n onChangeTokenCreated: options?.onChangeTokenCreated,\n onStorageSync: options?.onStorageSync,\n });\n }\n\n return {\n success: true,\n nostrEventId,\n splitGroupId,\n criticalPathDurationMs: criticalPathDuration,\n backgroundStarted: !options?.skipBackground,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplit] Failed after ${duration.toFixed(0)}ms:`, error);\n\n return {\n success: false,\n splitGroupId,\n criticalPathDurationMs: duration,\n error: errorMessage,\n backgroundStarted: false,\n };\n }\n }\n\n /**\n * Create a TransferCommitment from MintTransactionData WITHOUT waiting for mint proof.\n *\n * Key insight: TransferCommitment.create() only needs token.state and token.nametagTokens.\n * It does NOT need the genesis transaction or mint proof.\n */\n private async createTransferCommitmentFromMintData(\n mintData: MintTransactionData<any>,\n recipientAddress: IAddress,\n transferSalt: Uint8Array,\n signingService: SigningService,\n nametagTokens?: Token<any>[]\n ): Promise<TransferCommitment> {\n // Recreate the predicate from mint data\n const predicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n mintData.tokenType,\n signingService,\n HashAlgorithm.SHA256,\n mintData.salt\n );\n\n // Create token state (what TransferCommitment.create actually uses)\n const state = new TokenState(predicate, null);\n\n // Create a minimal token-like object\n // TransferCommitment.create() only accesses token.state and token.nametagTokens\n const minimalToken = {\n state,\n nametagTokens: nametagTokens || [],\n id: mintData.tokenId,\n type: mintData.tokenType,\n };\n\n // Create the transfer commitment\n const transferCommitment = await TransferCommitment.create(\n minimalToken as any,\n recipientAddress,\n transferSalt,\n null, // recipientData\n null, // recipientDataHash\n signingService\n );\n\n return transferCommitment;\n }\n\n /**\n * V5 background submission.\n *\n * Submits mint commitments to aggregator in PARALLEL after transport delivery.\n * Then waits for sender's mint proof, reconstructs change token, and saves it.\n */\n private submitBackgroundV5(\n senderMintCommitment: MintCommitment<any>,\n recipientMintCommitment: MintCommitment<any>,\n transferCommitment: TransferCommitment,\n context: BackgroundContext\n ): void {\n console.log('[InstantSplit] Background: Starting parallel mint submission...');\n const startTime = performance.now();\n\n // Submit all commitments in parallel\n const submissions = Promise.all([\n this.client\n .submitMintCommitment(senderMintCommitment)\n .then((res) => ({ type: 'senderMint', status: res.status }))\n .catch((err) => ({ type: 'senderMint', status: 'ERROR', error: err })),\n\n this.client\n .submitMintCommitment(recipientMintCommitment)\n .then((res) => ({ type: 'recipientMint', status: res.status }))\n .catch((err) => ({ type: 'recipientMint', status: 'ERROR', error: err })),\n\n this.client\n .submitTransferCommitment(transferCommitment)\n .then((res) => ({ type: 'transfer', status: res.status }))\n .catch((err) => ({ type: 'transfer', status: 'ERROR', error: err })),\n ]);\n\n submissions\n .then(async (results) => {\n const submitDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Background: Submissions complete in ${submitDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'MINTS_SUBMITTED',\n message: `All commitments submitted in ${submitDuration.toFixed(0)}ms`,\n });\n\n // Check for critical failures\n const senderMintResult = results.find((r) => r.type === 'senderMint');\n if (\n senderMintResult?.status !== 'SUCCESS' &&\n senderMintResult?.status !== 'REQUEST_ID_EXISTS'\n ) {\n console.error('[InstantSplit] Background: Sender mint failed - cannot save change token');\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Sender mint submission failed',\n error: String((senderMintResult as any)?.error),\n });\n return;\n }\n\n // Wait for sender's mint proof to save change token\n console.log('[InstantSplit] Background: Waiting for sender mint proof...');\n const proofStartTime = performance.now();\n\n try {\n const senderMintProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(senderMintCommitment)\n : await waitInclusionProof(this.trustBase, this.client, senderMintCommitment);\n\n const proofDuration = performance.now() - proofStartTime;\n console.log(`[InstantSplit] Background: Sender mint proof received in ${proofDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'MINTS_PROVEN',\n message: `Mint proof received in ${proofDuration.toFixed(0)}ms`,\n });\n\n // Reconstruct change token\n const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);\n const predicate = await UnmaskedPredicate.create(\n context.senderTokenId,\n context.tokenType,\n context.signingService,\n HashAlgorithm.SHA256,\n context.senderSalt\n );\n const state = new TokenState(predicate, null);\n const changeToken = await Token.mint(this.trustBase, state, mintTransaction);\n\n // Verify if not in dev mode\n if (!this.devMode) {\n const verification = await changeToken.verify(this.trustBase);\n if (!verification.isSuccessful) {\n throw new Error(`Change token verification failed`);\n }\n }\n\n console.log('[InstantSplit] Background: Change token created');\n\n context.onProgress?.({\n stage: 'CHANGE_TOKEN_SAVED',\n message: 'Change token created and verified',\n });\n\n // Save change token via callback\n if (context.onChangeTokenCreated) {\n await context.onChangeTokenCreated(changeToken);\n console.log('[InstantSplit] Background: Change token saved');\n }\n\n // Trigger storage sync if provided\n if (context.onStorageSync) {\n try {\n const syncSuccess = await context.onStorageSync();\n console.log(`[InstantSplit] Background: Storage sync ${syncSuccess ? 'completed' : 'deferred'}`);\n context.onProgress?.({\n stage: 'STORAGE_SYNCED',\n message: syncSuccess ? 'Storage synchronized' : 'Sync deferred',\n });\n } catch (syncError) {\n console.warn('[InstantSplit] Background: Storage sync error:', syncError);\n }\n }\n\n const totalDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Background: Complete in ${totalDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'COMPLETED',\n message: `Background processing complete in ${totalDuration.toFixed(0)}ms`,\n });\n } catch (proofError) {\n console.error('[InstantSplit] Background: Failed to get sender mint proof:', proofError);\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Failed to get mint proof',\n error: String(proofError),\n });\n }\n })\n .catch((err) => {\n console.error('[InstantSplit] Background: Submission batch failed:', err);\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Background submission failed',\n error: String(err),\n });\n });\n }\n\n /**\n * Dev mode bypass for waitInclusionProof.\n * In dev mode, we create a mock proof for testing.\n */\n private async waitInclusionProofWithDevBypass(\n commitment: TransferCommitment | MintCommitment<any>,\n timeoutMs = 60000\n ): Promise<any> {\n if (this.devMode) {\n // In dev mode, try to get real proof but with shorter timeout\n try {\n return await Promise.race([\n waitInclusionProof(this.trustBase, this.client, commitment as any),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Dev mode timeout')), Math.min(timeoutMs, 5000))\n ),\n ]);\n } catch {\n // Return a mock proof in dev mode\n console.log('[InstantSplit] Dev mode: Using mock proof');\n return {\n toJSON: () => ({ mock: true }),\n };\n }\n }\n return waitInclusionProof(this.trustBase, this.client, commitment as any);\n }\n}\n\n/**\n * Factory function for creating InstantSplitExecutor\n */\nexport function createInstantSplitExecutor(config: InstantSplitExecutorConfig): InstantSplitExecutor {\n return new InstantSplitExecutor(config);\n}\n","/**\n * InstantSplitProcessor\n *\n * Processes received INSTANT_SPLIT bundles on the recipient side.\n *\n * V5 Flow (Production Mode):\n * 1. Validate burn transaction (already has proof)\n * 2. Recreate and submit mint commitment -> wait for proof\n * 3. Reconstruct minted token using sender's state from bundle\n * 4. Submit transfer commitment -> wait for proof\n * 5. Create recipient's final state\n * 6. Finalize token with transfer transaction\n * 7. Verify and return finalized token\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport {\n type InstantSplitBundle,\n type InstantSplitBundleV5,\n type InstantSplitBundleV4,\n type InstantSplitProcessResult,\n isInstantSplitBundleV5,\n isInstantSplitBundleV4,\n} from '../../types/instant-split';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InstantSplitProcessorConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n /** Dev mode skips trust base verification (for testing) */\n devMode?: boolean;\n}\n\nexport interface ProcessBundleOptions {\n /** Timeout for proof waiting in ms (default: 60000) */\n proofTimeoutMs?: number;\n /** Callback to find nametag token for PROXY address */\n findNametagToken?: (proxyAddress: string) => Promise<Token<any> | null>;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// InstantSplitProcessor Implementation\n// =============================================================================\n\nexport class InstantSplitProcessor {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private devMode: boolean;\n\n constructor(config: InstantSplitProcessorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Process a received INSTANT_SPLIT bundle.\n *\n * @param bundle - The received bundle (V4 or V5)\n * @param signingService - Recipient's signing service\n * @param senderPubkey - Sender's public key (for verification)\n * @param options - Processing options\n * @returns Processing result with finalized token if successful\n */\n async processReceivedBundle(\n bundle: InstantSplitBundle,\n signingService: SigningService,\n senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n if (isInstantSplitBundleV5(bundle)) {\n return this.processV5Bundle(bundle, signingService, senderPubkey, options);\n } else if (isInstantSplitBundleV4(bundle)) {\n return this.processV4Bundle(bundle, signingService, senderPubkey, options);\n }\n\n return {\n success: false,\n error: `Unknown bundle version: ${(bundle as any).version}`,\n durationMs: 0,\n };\n }\n\n /**\n * Process a V5 bundle (production mode).\n *\n * V5 Flow:\n * 1. Burn transaction already has proof (just validate)\n * 2. Submit mint commitment -> wait for proof\n * 3. Reconstruct minted token (use sender's state from bundle)\n * 4. Submit transfer commitment -> wait for proof\n * 5. Create recipient's final state and finalize token\n */\n private async processV5Bundle(\n bundle: InstantSplitBundleV5,\n signingService: SigningService,\n senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n console.log('[InstantSplitProcessor] Processing V5 bundle...');\n const startTime = performance.now();\n\n try {\n // Validate sender pubkey matches bundle\n if (bundle.senderPubkey !== senderPubkey) {\n console.warn('[InstantSplitProcessor] Sender pubkey mismatch (non-fatal)');\n }\n\n // === Step 1: Validate burn transaction ===\n const burnTxJson = JSON.parse(bundle.burnTransaction);\n const burnTransaction = await TransferTransaction.fromJSON(burnTxJson);\n console.log('[InstantSplitProcessor] Burn transaction validated');\n\n // === Step 2: Deserialize and submit MintCommitment ===\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n\n const mintCommitment = await MintCommitment.create(mintData);\n console.log('[InstantSplitProcessor] Mint commitment recreated');\n\n const mintResponse = await this.client.submitMintCommitment(mintCommitment);\n if (mintResponse.status !== 'SUCCESS' && mintResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint submission failed: ${mintResponse.status}`);\n }\n console.log(`[InstantSplitProcessor] Mint submitted: ${mintResponse.status}`);\n\n // === Step 3: Wait for mint inclusion proof ===\n const mintProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(mintCommitment, options?.proofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, mintCommitment);\n const mintTransaction = mintCommitment.toTransaction(mintProof);\n console.log('[InstantSplitProcessor] Mint proof received');\n\n // === Step 4: Reconstruct minted token using sender's state ===\n const tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);\n\n const tokenJson = {\n version: '2.0',\n state: senderMintedStateJson,\n genesis: mintTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n const mintedToken = await Token.fromJSON(tokenJson);\n console.log('[InstantSplitProcessor] Minted token reconstructed from sender state');\n\n // === Step 5: Submit transfer commitment ===\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n\n const transferResponse = await this.client.submitTransferCommitment(transferCommitment);\n if (transferResponse.status !== 'SUCCESS' && transferResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer submission failed: ${transferResponse.status}`);\n }\n console.log(`[InstantSplitProcessor] Transfer submitted: ${transferResponse.status}`);\n\n // === Step 6: Wait for transfer inclusion proof ===\n const transferProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(transferCommitment, options?.proofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, transferCommitment);\n const transferTransaction = transferCommitment.toTransaction(transferProof);\n console.log('[InstantSplitProcessor] Transfer proof received');\n\n // === Step 7: Create recipient's final state ===\n const transferSalt = fromHex(bundle.transferSaltHex);\n const finalRecipientPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const finalRecipientState = new TokenState(finalRecipientPredicate, null);\n console.log('[InstantSplitProcessor] Final recipient state created');\n\n // === Step 8: Find nametag token for PROXY addresses ===\n let nametagTokens: Token<any>[] = [];\n const recipientAddressStr = bundle.recipientAddressJson;\n\n if (recipientAddressStr.startsWith('PROXY://')) {\n console.log('[InstantSplitProcessor] PROXY address detected, finding nametag token...');\n\n // Try to get nametag token from bundle first\n if (bundle.nametagTokenJson) {\n try {\n const nametagToken = await Token.fromJSON(JSON.parse(bundle.nametagTokenJson));\n // Validate PROXY address matches nametag token\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address !== recipientAddressStr) {\n console.warn('[InstantSplitProcessor] Nametag PROXY address mismatch, ignoring bundle token');\n // Fall through to callback path\n } else {\n nametagTokens = [nametagToken];\n console.log('[InstantSplitProcessor] Using nametag token from bundle (address validated)');\n }\n } catch (err) {\n console.warn('[InstantSplitProcessor] Failed to parse nametag token from bundle:', err);\n }\n }\n\n // If not in bundle, use callback to find it\n if (nametagTokens.length === 0 && options?.findNametagToken) {\n const token = await options.findNametagToken(recipientAddressStr);\n if (token) {\n nametagTokens = [token];\n console.log('[InstantSplitProcessor] Found nametag token via callback');\n }\n }\n\n // CRITICAL: For PROXY addresses, we MUST have a nametag token\n if (nametagTokens.length === 0 && !this.devMode) {\n throw new Error(\n `PROXY address transfer requires nametag token for verification. ` +\n `Address: ${recipientAddressStr}`\n );\n }\n }\n\n // === Step 9: Finalize token ===\n let finalToken: Token<any>;\n\n if (this.devMode) {\n // Dev mode: create token without verification\n console.log('[InstantSplitProcessor] Dev mode: finalizing without verification');\n const tokenJson = mintedToken.toJSON() as any;\n tokenJson.state = finalRecipientState.toJSON();\n tokenJson.transactions = [transferTransaction.toJSON()];\n finalToken = await Token.fromJSON(tokenJson);\n } else {\n // Production: use client.finalizeTransaction\n finalToken = await this.client.finalizeTransaction(\n this.trustBase,\n mintedToken,\n finalRecipientState,\n transferTransaction,\n nametagTokens\n );\n }\n console.log('[InstantSplitProcessor] Token finalized');\n\n // === Step 10: Verify the final token ===\n if (!this.devMode) {\n const verification = await finalToken.verify(this.trustBase);\n if (!verification.isSuccessful) {\n throw new Error(`Token verification failed`);\n }\n console.log('[InstantSplitProcessor] Token verified');\n }\n\n const duration = performance.now() - startTime;\n console.log(`[InstantSplitProcessor] V5 bundle processed in ${duration.toFixed(0)}ms`);\n\n return {\n success: true,\n token: finalToken,\n durationMs: duration,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplitProcessor] V5 processing failed:`, error);\n\n return {\n success: false,\n error: errorMessage,\n durationMs: duration,\n };\n }\n }\n\n /**\n * Process a V4 bundle (dev mode only).\n *\n * V4 Flow:\n * 1. Submit burn commitment -> wait for proof\n * 2. Submit mint commitment -> wait for proof\n * 3. Reconstruct minted token\n * 4. Submit transfer commitment -> wait for proof\n * 5. Finalize token\n */\n private async processV4Bundle(\n bundle: InstantSplitBundleV4,\n signingService: SigningService,\n _senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n if (!this.devMode) {\n return {\n success: false,\n error: 'INSTANT_SPLIT V4 is only supported in dev mode',\n durationMs: 0,\n };\n }\n\n console.log('[InstantSplitProcessor] Processing V4 bundle (dev mode)...');\n const startTime = performance.now();\n\n try {\n // === Step 1: Submit burn commitment and wait for proof ===\n const burnCommitmentJson = JSON.parse(bundle.burnCommitment);\n const burnCommitment = await TransferCommitment.fromJSON(burnCommitmentJson);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn submission failed: ${burnResponse.status}`);\n }\n\n await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);\n console.log('[InstantSplitProcessor] V4: Burn proof received');\n\n // === Step 2: Submit mint commitment and wait for proof ===\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n\n const mintCommitment = await MintCommitment.create(mintData);\n\n const mintResponse = await this.client.submitMintCommitment(mintCommitment);\n if (mintResponse.status !== 'SUCCESS' && mintResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint submission failed: ${mintResponse.status}`);\n }\n\n const mintProof = await this.waitInclusionProofWithDevBypass(\n mintCommitment,\n options?.proofTimeoutMs\n );\n const mintTransaction = mintCommitment.toTransaction(mintProof);\n console.log('[InstantSplitProcessor] V4: Mint proof received');\n\n // === Step 3: Reconstruct minted token ===\n const tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n const recipientSalt = fromHex(bundle.recipientSaltHex);\n\n const recipientPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n recipientSalt\n );\n const recipientState = new TokenState(recipientPredicate, null);\n\n const tokenJson = {\n version: '2.0',\n state: recipientState.toJSON(),\n genesis: mintTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n const mintedToken = await Token.fromJSON(tokenJson);\n console.log('[InstantSplitProcessor] V4: Minted token reconstructed');\n\n // === Step 4: Submit transfer commitment and wait for proof ===\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n\n const transferResponse = await this.client.submitTransferCommitment(transferCommitment);\n if (transferResponse.status !== 'SUCCESS' && transferResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer submission failed: ${transferResponse.status}`);\n }\n\n const transferProof = await this.waitInclusionProofWithDevBypass(\n transferCommitment,\n options?.proofTimeoutMs\n );\n const transferTransaction = transferCommitment.toTransaction(transferProof);\n console.log('[InstantSplitProcessor] V4: Transfer proof received');\n\n // === Step 5: Finalize token (dev mode - no verification) ===\n const transferSalt = fromHex(bundle.transferSaltHex);\n const finalPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const finalState = new TokenState(finalPredicate, null);\n\n const finalTokenJson = mintedToken.toJSON() as any;\n finalTokenJson.state = finalState.toJSON();\n finalTokenJson.transactions = [transferTransaction.toJSON()];\n const finalToken = await Token.fromJSON(finalTokenJson);\n console.log('[InstantSplitProcessor] V4: Token finalized');\n\n const duration = performance.now() - startTime;\n console.log(`[InstantSplitProcessor] V4 bundle processed in ${duration.toFixed(0)}ms`);\n\n return {\n success: true,\n token: finalToken,\n durationMs: duration,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplitProcessor] V4 processing failed:`, error);\n\n return {\n success: false,\n error: errorMessage,\n durationMs: duration,\n };\n }\n }\n\n /**\n * Dev mode bypass for waitInclusionProof.\n */\n private async waitInclusionProofWithDevBypass(\n commitment: TransferCommitment | MintCommitment<any>,\n timeoutMs = 60000\n ): Promise<any> {\n if (this.devMode) {\n try {\n return await Promise.race([\n waitInclusionProof(this.trustBase, this.client, commitment as any),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Dev mode timeout')), Math.min(timeoutMs, 5000))\n ),\n ]);\n } catch {\n console.log('[InstantSplitProcessor] Dev mode: Using mock proof');\n return {\n toJSON: () => ({ mock: true }),\n };\n }\n }\n return waitInclusionProof(this.trustBase, this.client, commitment as any);\n }\n}\n\n/**\n * Factory function for creating InstantSplitProcessor\n */\nexport function createInstantSplitProcessor(\n config: InstantSplitProcessorConfig\n): InstantSplitProcessor {\n return new InstantSplitProcessor(config);\n}\n","/**\n * INSTANT_SPLIT V5 Types\n *\n * Optimized token split transfer types that achieve ~2.3s critical path latency\n * instead of the standard ~42s sequential flow.\n *\n * Key Insight: TransferCommitment.create() only needs token.state, NOT the mint proof.\n * This allows creating transfer commitments immediately after mint data creation,\n * without waiting for mint proofs.\n *\n * V5 Flow (Production Mode):\n * 1. Create burn commitment, submit to aggregator\n * 2. Wait for burn inclusion proof (~2s - unavoidable)\n * 3. Create mint commitments with proper SplitMintReason (requires burn proof)\n * 4. Create transfer commitment from mint data (no mint proof needed)\n * 5. Package bundle -> send via Nostr -> SUCCESS (~2.3s total!)\n * 6. Background: submit mints, wait for proofs, save change token, sync storage\n */\n\n// Note: Token type is generic - we use 'any' to avoid import complexity\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype SdkToken = any;\n\n// =============================================================================\n// INSTANT_SPLIT V5 Bundle Types\n// =============================================================================\n\n/**\n * Bundle payload for INSTANT_SPLIT V5 (Production Mode)\n *\n * V5 achieves ~2.3s sender latency while working with production aggregators:\n * - Burn proof is required for creating SplitMintReason\n * - Transfer commitment is created from mint data WITHOUT waiting for mint proof\n * - Mints are submitted in background after Nostr delivery\n *\n * Security: Burn is proven on-chain before mints can be created, preventing double-spend.\n */\nexport interface InstantSplitBundleV5 {\n /** Bundle version - V5 is production mode (proper SplitMintReason) */\n version: '5.0';\n\n /** Bundle type identifier */\n type: 'INSTANT_SPLIT';\n\n /**\n * Burn TRANSACTION JSON (WITH inclusion proof!)\n * V5 sends the proven burn transaction so recipient can verify burn completed.\n */\n burnTransaction: string;\n\n /**\n * Recipient's MintTransactionData JSON (contains proper SplitMintReason in V5)\n * The SplitMintReason references the burn transaction.\n */\n recipientMintData: string;\n\n /**\n * Pre-created TransferCommitment JSON (recipient submits and waits for proof)\n * Created from mint data WITHOUT any proofs.\n */\n transferCommitment: string;\n\n /** Payment amount (display metadata) */\n amount: string;\n\n /** Coin ID hex */\n coinId: string;\n\n /** Token type hex */\n tokenTypeHex: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId: string;\n\n /** Sender's pubkey for acknowledgment */\n senderPubkey: string;\n\n /** Salt for recipient predicate creation (hex) */\n recipientSaltHex: string;\n\n /** Salt for transfer commitment creation (hex) */\n transferSaltHex: string;\n\n /**\n * Serialized TokenState JSON for the intermediate minted token.\n *\n * In V5, the mint is to sender's address first, then transferred to recipient.\n * The recipient needs this state to reconstruct the minted token before applying transfer.\n * Without this, the recipient can't create a matching predicate (they don't have sender's signing key).\n */\n mintedTokenStateJson: string;\n\n /**\n * Serialized TokenState JSON for the final recipient state (after transfer).\n *\n * The sender creates the transfer commitment targeting the recipient's PROXY address.\n * The recipient can't recreate this state correctly because their signingService\n * creates predicates for their DIRECT address, not the PROXY address.\n * This is optional - recipient can create their own if they have the correct address.\n */\n finalRecipientStateJson: string;\n\n /**\n * Serialized recipient address JSON (PROXY or DIRECT).\n *\n * Used by the recipient to identify which nametag token is being targeted.\n * For PROXY address transfers, the recipient needs to find the matching\n * nametag token and pass it to finalizeTransaction() for verification.\n */\n recipientAddressJson: string;\n\n /**\n * Serialized nametag token JSON (for PROXY address transfers).\n *\n * For PROXY address transfers, the sender includes the nametag token\n * so the recipient can verify they're authorized to receive at this address.\n * This is REQUIRED for PROXY addresses - transfers without this will fail.\n */\n nametagTokenJson?: string;\n}\n\n// =============================================================================\n// INSTANT_SPLIT V4 Bundle Types (Dev Mode Only)\n// =============================================================================\n\n/**\n * Bundle payload for INSTANT_SPLIT V4 (Dev Mode Only - True Nostr-First Split)\n *\n * V4 achieves near-zero sender latency (~0.3s) by:\n * 1. Creating ALL commitments locally BEFORE any aggregator submission\n * 2. Persisting via Nostr FIRST\n * 3. Then submitting ALL to aggregator in background\n *\n * NOTE: V4 only works in dev mode. Production requires V5 with proper SplitMintReason.\n */\nexport interface InstantSplitBundleV4 {\n /** Bundle version - V4 is true Nostr-first (dev mode only) */\n version: '4.0';\n\n /** Bundle type identifier */\n type: 'INSTANT_SPLIT';\n\n /**\n * Burn commitment JSON (NOT transaction - no proof yet!)\n * Both sender and recipient submit this to aggregator.\n */\n burnCommitment: string;\n\n /** Recipient's MintTransactionData JSON (they recreate commitment and submit) */\n recipientMintData: string;\n\n /**\n * Pre-created TransferCommitment JSON (recipient submits and waits for proof)\n * Created from mint data WITHOUT any proofs.\n */\n transferCommitment: string;\n\n /** Payment amount (display metadata) */\n amount: string;\n\n /** Coin ID hex */\n coinId: string;\n\n /** Token type hex */\n tokenTypeHex: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId: string;\n\n /** Sender's pubkey for acknowledgment */\n senderPubkey: string;\n\n /** Salt for recipient predicate creation (hex) */\n recipientSaltHex: string;\n\n /** Salt for transfer commitment creation (hex) */\n transferSaltHex: string;\n}\n\n/** Union type for all InstantSplit bundle versions */\nexport type InstantSplitBundle = InstantSplitBundleV4 | InstantSplitBundleV5;\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard to check if an object is an InstantSplitBundle (V4 or V5)\n */\nexport function isInstantSplitBundle(obj: unknown): obj is InstantSplitBundle {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n const bundle = obj as Record<string, unknown>;\n\n // Check common fields\n if (bundle.type !== 'INSTANT_SPLIT') return false;\n if (typeof bundle.recipientMintData !== 'string') return false;\n if (typeof bundle.transferCommitment !== 'string') return false;\n if (typeof bundle.amount !== 'string') return false;\n if (typeof bundle.coinId !== 'string') return false;\n if (typeof bundle.splitGroupId !== 'string') return false;\n if (typeof bundle.senderPubkey !== 'string') return false;\n if (typeof bundle.recipientSaltHex !== 'string') return false;\n if (typeof bundle.transferSaltHex !== 'string') return false;\n\n // Version-specific checks\n if (bundle.version === '4.0') {\n // V4 has burnCommitment (no proof)\n return typeof bundle.burnCommitment === 'string';\n } else if (bundle.version === '5.0') {\n // V5 has burnTransaction (with proof), mintedTokenStateJson, finalRecipientStateJson, and recipientAddressJson\n return (\n typeof bundle.burnTransaction === 'string' &&\n typeof bundle.mintedTokenStateJson === 'string' &&\n typeof bundle.finalRecipientStateJson === 'string' &&\n typeof bundle.recipientAddressJson === 'string'\n );\n }\n\n return false;\n}\n\n/**\n * Type guard to check if bundle is V4 (dev mode)\n */\nexport function isInstantSplitBundleV4(obj: unknown): obj is InstantSplitBundleV4 {\n return isInstantSplitBundle(obj) && obj.version === '4.0';\n}\n\n/**\n * Type guard to check if bundle is V5 (production mode)\n */\nexport function isInstantSplitBundleV5(obj: unknown): obj is InstantSplitBundleV5 {\n return isInstantSplitBundle(obj) && obj.version === '5.0';\n}\n\n// =============================================================================\n// Result Types\n// =============================================================================\n\n/**\n * Result of an instant split send operation\n */\nexport interface InstantSplitResult {\n /** Whether the operation succeeded (Nostr delivery) */\n success: boolean;\n\n /** Nostr event ID (if delivered) */\n nostrEventId?: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId?: string;\n\n /** Time taken for critical path (Nostr delivery) in ms */\n criticalPathDurationMs: number;\n\n /** Error message (if failed) */\n error?: string;\n\n /** Whether background processing was started */\n backgroundStarted?: boolean;\n}\n\n/**\n * Result from processing an INSTANT_SPLIT bundle (recipient side)\n */\nexport interface InstantSplitProcessResult {\n /** Whether processing succeeded */\n success: boolean;\n\n /** The finalized SDK token (if successful) */\n token?: SdkToken;\n\n /** Error message (if failed) */\n error?: string;\n\n /** Processing duration in ms */\n durationMs: number;\n}\n\n// =============================================================================\n// Options Types\n// =============================================================================\n\n/**\n * Options for instant split send operation\n */\nexport interface InstantSplitOptions {\n /** Timeout for Nostr delivery in ms (default: 30000) */\n nostrTimeoutMs?: number;\n\n /** Timeout for burn proof wait in ms (default: 60000) */\n burnProofTimeoutMs?: number;\n\n /** Timeout for mint proof wait in ms (default: 60000) */\n mintProofTimeoutMs?: number;\n\n /** Skip background processing (for testing) */\n skipBackground?: boolean;\n\n /** Use dev mode (V4 flow without SplitMintReason validation) */\n devMode?: boolean;\n\n /** Callback when burn is completed */\n onBurnCompleted?: (burnTxJson: string) => void;\n\n /** Callback when Nostr delivery is completed */\n onNostrDelivered?: (eventId: string) => void;\n\n /** Callback for background progress updates */\n onBackgroundProgress?: (status: BackgroundProgressStatus) => void;\n\n /** Callback when change token is created (background) */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChangeTokenCreated?: (token: any) => Promise<void>;\n\n /** Callback to trigger storage sync (background) */\n onStorageSync?: () => Promise<boolean>;\n}\n\n/**\n * Background processing status\n */\nexport interface BackgroundProgressStatus {\n stage:\n | 'MINTS_SUBMITTED'\n | 'MINTS_PROVEN'\n | 'CHANGE_TOKEN_SAVED'\n | 'STORAGE_SYNCED'\n | 'COMPLETED'\n | 'FAILED';\n message: string;\n error?: string;\n}\n\n// =============================================================================\n// Recovery Types\n// =============================================================================\n\n/**\n * Metadata for V5 recovery (stored with outbox entry)\n */\nexport interface InstantSplitV5RecoveryMetadata {\n version: '5.0';\n seedString: string;\n senderTokenIdHex: string;\n senderSaltHex: string;\n changeAmount: string;\n burnRequestIdHex: string;\n}\n\n/**\n * Result of recovering an orphaned split\n */\nexport interface SplitRecoveryResult {\n /** Number of splits successfully recovered */\n splitsRecovered: number;\n\n /** Number of change tokens recovered */\n changeTokensRecovered: number;\n\n /** Errors encountered during recovery */\n errors: Array<{\n splitGroupId: string;\n error: string;\n timestamp: number;\n }>;\n\n /** Total duration of recovery in ms */\n durationMs: number;\n}\n","/**\n * Payments Module\n * Platform-independent token operations with full wallet repository functionality\n *\n * Includes:\n * - Token CRUD operations\n * - Tombstones for sync\n * - Archived tokens (spent history)\n * - Forked tokens (alternative histories)\n * - Transaction history\n * - Nametag storage\n */\n\nimport type {\n Asset,\n Token,\n TokenStatus,\n TransferRequest,\n TransferResult,\n IncomingTransfer,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n} from '../../types';\nimport type {\n TxfToken,\n TxfTransaction,\n TombstoneEntry,\n NametagData,\n} from '../../types/txf';\nimport { L1PaymentsModule, type L1PaymentsModuleConfig } from './L1PaymentsModule';\nimport { TokenSplitCalculator } from './TokenSplitCalculator';\nimport { TokenSplitExecutor } from './TokenSplitExecutor';\nimport { NametagMinter, type MintNametagResult } from './NametagMinter';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../../storage';\nimport type {\n TransportProvider,\n PeerInfo,\n IncomingTokenTransfer,\n PaymentRequestPayload,\n PaymentRequestResponsePayload,\n IncomingPaymentRequest as TransportPaymentRequest,\n IncomingPaymentRequestResponse as TransportPaymentRequestResponse,\n} from '../../transport';\nimport type { OracleProvider } from '../../oracle';\nimport type { PriceProvider } from '../../price';\nimport type {\n PaymentRequest,\n IncomingPaymentRequest,\n OutgoingPaymentRequest,\n PaymentRequestResult,\n PaymentRequestStatus,\n PaymentRequestHandler,\n PaymentRequestResponse,\n PaymentRequestResponseHandler,\n} from '../../types';\nimport { STORAGE_KEYS_ADDRESS } from '../../constants';\nimport {\n tokenToTxf,\n getCurrentStateHash,\n buildTxfStorageData,\n parseTxfStorageData,\n} from '../../serialization/txf-serializer';\nimport { TokenRegistry } from '../../registry';\n\n// Instant split imports\nimport { InstantSplitExecutor } from './InstantSplitExecutor';\nimport { InstantSplitProcessor } from './InstantSplitProcessor';\nimport type {\n InstantSplitBundle,\n InstantSplitResult,\n InstantSplitProcessResult,\n InstantSplitOptions,\n} from '../../types/instant-split';\nimport { isInstantSplitBundle } from '../../types/instant-split';\n\n// SDK imports for token parsing and transfers\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { AddressScheme } from '@unicitylabs/state-transition-sdk/lib/address/AddressScheme';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport type { IAddress } from '@unicitylabs/state-transition-sdk/lib/address/IAddress';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\n\n// =============================================================================\n// Transaction History Entry\n// =============================================================================\n\nexport interface TransactionHistoryEntry {\n id: string;\n type: 'SENT' | 'RECEIVED' | 'SPLIT' | 'MINT';\n amount: string;\n coinId: string;\n symbol: string;\n timestamp: number;\n recipientNametag?: string;\n senderPubkey?: string;\n txHash?: string;\n}\n\n// =============================================================================\n// Token Parsing Utilities\n// =============================================================================\n\ninterface ParsedTokenInfo {\n coinId: string;\n symbol: string;\n name: string;\n decimals: number;\n iconUrl?: string;\n amount: string;\n tokenId?: string;\n}\n\n/**\n * Enrich token info with data from TokenRegistry\n */\nfunction enrichWithRegistry(info: ParsedTokenInfo): ParsedTokenInfo {\n const registry = TokenRegistry.getInstance();\n const def = registry.getDefinition(info.coinId);\n if (def) {\n return {\n ...info,\n symbol: def.symbol || info.symbol,\n name: def.name.charAt(0).toUpperCase() + def.name.slice(1),\n decimals: def.decimals ?? 0,\n iconUrl: registry.getIconUrl(info.coinId) ?? undefined,\n };\n }\n return info;\n}\n\n/**\n * Parse token info from SDK token data or TXF JSON\n */\nasync function parseTokenInfo(tokenData: unknown): Promise<ParsedTokenInfo> {\n const defaultInfo: ParsedTokenInfo = {\n coinId: 'ALPHA',\n symbol: 'ALPHA',\n name: 'Alpha Token',\n decimals: 0,\n amount: '0',\n };\n\n try {\n // If it's a string, try to parse as JSON\n const data = typeof tokenData === 'string' ? JSON.parse(tokenData) : tokenData;\n\n // Try to create SDK token and extract coin info using SDK methods\n try {\n const sdkToken = await SdkToken.fromJSON(data);\n\n // Try to get token ID\n if (sdkToken.id) {\n defaultInfo.tokenId = sdkToken.id.toJSON();\n }\n\n // Extract coinId from SDK token's coins structure (lottery-compatible)\n if (sdkToken.coins && sdkToken.coins.coins) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawCoins = sdkToken.coins.coins as any[];\n if (rawCoins.length > 0) {\n const firstCoin = rawCoins[0];\n // Format: [[CoinId, amount]] or [CoinId, amount]\n let coinIdObj: unknown;\n let amount: unknown;\n\n if (Array.isArray(firstCoin) && firstCoin.length === 2) {\n [coinIdObj, amount] = firstCoin;\n }\n\n // Extract hex string from CoinId object\n if (coinIdObj instanceof CoinId) {\n const coinIdHex = coinIdObj.toJSON() as string;\n return enrichWithRegistry({\n coinId: coinIdHex,\n symbol: coinIdHex.slice(0, 8),\n name: `Token ${coinIdHex.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount ?? '0'),\n tokenId: defaultInfo.tokenId,\n });\n } else if (coinIdObj && typeof coinIdObj === 'object' && 'bytes' in coinIdObj) {\n // CoinId stored as object with bytes\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bytes = (coinIdObj as any).bytes;\n const coinIdHex = Buffer.isBuffer(bytes)\n ? bytes.toString('hex')\n : Array.isArray(bytes)\n ? Buffer.from(bytes).toString('hex')\n : String(bytes);\n return enrichWithRegistry({\n coinId: coinIdHex,\n symbol: coinIdHex.slice(0, 8),\n name: `Token ${coinIdHex.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount ?? '0'),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n\n // Fallback: Extract from JSON representation\n const tokenJson = sdkToken.toJSON() as unknown as Record<string, unknown>;\n const genesisData = tokenJson.genesis as Record<string, unknown> | undefined;\n if (genesisData?.data) {\n const gData = genesisData.data as Record<string, unknown>;\n if (gData.coinData && typeof gData.coinData === 'object') {\n // coinData might be array: [[coinIdHex, amount]]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = gData.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n const coinIdStr = typeof coinIdHex === 'string' ? coinIdHex : String(coinIdHex);\n return enrichWithRegistry({\n coinId: coinIdStr,\n symbol: coinIdStr.slice(0, 8),\n name: `Token ${coinIdStr.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n }\n } catch {\n // SDK parsing failed, try manual extraction\n }\n\n // Manual extraction from TXF format - handle array structure\n if (data.genesis?.data) {\n const genesis = data.genesis.data;\n if (genesis.coinData) {\n // coinData can be: [[coinIdHex, amount]] or {coinIdHex: amount}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = genesis.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n return enrichWithRegistry({\n coinId: String(coinIdHex),\n symbol: String(coinIdHex).slice(0, 8),\n name: `Token ${String(coinIdHex).slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: genesis.tokenId,\n });\n }\n } else if (typeof coinData === 'object') {\n const coinEntries = Object.entries(coinData);\n if (coinEntries.length > 0) {\n const [coinId, amount] = coinEntries[0] as [string, unknown];\n return enrichWithRegistry({\n coinId,\n symbol: coinId.slice(0, 8),\n name: `Token ${coinId.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: genesis.tokenId,\n });\n }\n }\n }\n if (genesis.tokenId) {\n defaultInfo.tokenId = genesis.tokenId;\n }\n }\n\n // Try to extract from state if available\n if (data.state?.coinData) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = data.state.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n return enrichWithRegistry({\n coinId: String(coinIdHex),\n symbol: String(coinIdHex).slice(0, 8),\n name: `Token ${String(coinIdHex).slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n } else if (typeof coinData === 'object') {\n const coinEntries = Object.entries(coinData);\n if (coinEntries.length > 0) {\n const [coinId, amount] = coinEntries[0] as [string, unknown];\n return enrichWithRegistry({\n coinId,\n symbol: coinId.slice(0, 8),\n name: `Token ${coinId.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n } catch (error) {\n console.warn('[Payments] Failed to parse token info:', error);\n }\n\n return defaultInfo;\n}\n\n// =============================================================================\n// Repository Utility Functions\n// =============================================================================\n\n/**\n * Extract token ID (genesis tokenId) from sdkData/jsonData\n */\nfunction extractTokenIdFromSdkData(sdkData: string | undefined): string | null {\n if (!sdkData) return null;\n try {\n const txf = JSON.parse(sdkData);\n return txf.genesis?.data?.tokenId || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract state hash from sdkData/jsonData\n */\nfunction extractStateHashFromSdkData(sdkData: string | undefined): string {\n if (!sdkData) return '';\n try {\n const txf = JSON.parse(sdkData) as TxfToken;\n const stateHash = getCurrentStateHash(txf);\n\n // Try alternative locations if not found in standard place\n if (!stateHash) {\n if ((txf as any).state?.hash) {\n return (txf as any).state.hash;\n }\n if ((txf as any).stateHash) {\n return (txf as any).stateHash;\n }\n if ((txf as any).currentStateHash) {\n return (txf as any).currentStateHash;\n }\n }\n\n return stateHash || '';\n } catch {\n return '';\n }\n}\n\n/**\n * Create composite key from tokenId and stateHash\n * Format: {tokenId}_{stateHash}\n * This uniquely identifies a token at a specific state\n */\nfunction createTokenStateKey(tokenId: string, stateHash: string): string {\n return `${tokenId}_${stateHash}`;\n}\n\n/**\n * Extract composite key (tokenId_stateHash) from token\n * Returns null if token doesn't have valid tokenId and stateHash\n */\nfunction extractTokenStateKey(token: Token): string | null {\n const tokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n if (!tokenId || !stateHash) return null;\n return createTokenStateKey(tokenId, stateHash);\n}\n\n/**\n * Check if two tokens have the same genesis tokenId (same token, possibly different states)\n */\nfunction hasSameGenesisTokenId(t1: Token, t2: Token): boolean {\n const id1 = extractTokenIdFromSdkData(t1.sdkData);\n const id2 = extractTokenIdFromSdkData(t2.sdkData);\n return !!(id1 && id2 && id1 === id2);\n}\n\n/**\n * Check if two tokens are exactly the same (same tokenId AND same stateHash)\n */\nfunction isSameTokenState(t1: Token, t2: Token): boolean {\n const key1 = extractTokenStateKey(t1);\n const key2 = extractTokenStateKey(t2);\n return !!(key1 && key2 && key1 === key2);\n}\n\n/**\n * Create tombstone from token - requires valid tokenId and stateHash\n */\nfunction createTombstoneFromToken(token: Token): TombstoneEntry | null {\n const tokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n\n // Both tokenId and stateHash are required for a valid tombstone\n if (!tokenId || !stateHash) {\n return null;\n }\n\n return {\n tokenId,\n stateHash,\n timestamp: Date.now(),\n };\n}\n\n/**\n * Check if incoming token is an incremental update\n */\nfunction isIncrementalUpdate(existing: TxfToken, incoming: TxfToken): boolean {\n if (existing.genesis?.data?.tokenId !== incoming.genesis?.data?.tokenId) {\n return false;\n }\n\n const existingTxns = existing.transactions || [];\n const incomingTxns = incoming.transactions || [];\n\n if (incomingTxns.length < existingTxns.length) {\n return false;\n }\n\n for (let i = 0; i < existingTxns.length; i++) {\n const existingTx = existingTxns[i];\n const incomingTx = incomingTxns[i];\n\n if (existingTx.previousStateHash !== incomingTx.previousStateHash ||\n existingTx.newStateHash !== incomingTx.newStateHash) {\n return false;\n }\n }\n\n for (let i = existingTxns.length; i < incomingTxns.length; i++) {\n const newTx = incomingTxns[i] as TxfTransaction;\n if (newTx.inclusionProof === null) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Count committed transactions\n */\nfunction countCommittedTxns(txf: TxfToken): number {\n return (txf.transactions || []).filter(\n (tx: TxfTransaction) => tx.inclusionProof !== null\n ).length;\n}\n\n/**\n * Prune tombstones by age and count\n */\nfunction pruneTombstonesByAge(\n tombstones: TombstoneEntry[],\n maxAge: number = 30 * 24 * 60 * 60 * 1000,\n maxCount: number = 100\n): TombstoneEntry[] {\n const now = Date.now();\n let result = tombstones.filter(t => (now - t.timestamp) < maxAge);\n\n if (result.length > maxCount) {\n result = [...result].sort((a, b) => b.timestamp - a.timestamp);\n result = result.slice(0, maxCount);\n }\n\n return result;\n}\n\n/**\n * Prune Map by count\n */\nfunction pruneMapByCount<T>(items: Map<string, T>, maxCount: number): Map<string, T> {\n if (items.size <= maxCount) {\n return new Map(items);\n }\n\n const entries = [...items.entries()];\n const toKeep = entries.slice(entries.length - maxCount);\n return new Map(toKeep);\n}\n\n/**\n * Find best token version from archives\n */\nfunction findBestTokenVersion(\n tokenId: string,\n archivedTokens: Map<string, TxfToken>,\n forkedTokens: Map<string, TxfToken>\n): TxfToken | null {\n const candidates: TxfToken[] = [];\n\n const archived = archivedTokens.get(tokenId);\n if (archived) candidates.push(archived);\n\n for (const [key, forked] of forkedTokens) {\n if (key.startsWith(tokenId + '_')) {\n candidates.push(forked);\n }\n }\n\n if (candidates.length === 0) return null;\n\n candidates.sort((a, b) => countCommittedTxns(b) - countCommittedTxns(a));\n return candidates[0];\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface PaymentsModuleConfig {\n /** Auto-sync after operations */\n autoSync?: boolean;\n /** Auto-validate with aggregator */\n autoValidate?: boolean;\n /** Retry failed transfers */\n retryFailed?: boolean;\n /** Max retry attempts */\n maxRetries?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** L1 (ALPHA blockchain) configuration. Set to null to explicitly disable L1. */\n l1?: L1PaymentsModuleConfig | null;\n}\n\n// =============================================================================\n// NOSTR-FIRST Proof Polling Types\n// =============================================================================\n\n/**\n * Job for background proof polling (NOSTR-FIRST pattern)\n */\nexport interface ProofPollingJob {\n tokenId: string;\n requestIdHex: string;\n commitmentJson: string;\n startedAt: number;\n attemptCount: number;\n lastAttemptAt: number;\n /** Callback when proof is received */\n onProofReceived?: (tokenId: string) => void;\n}\n\n// =============================================================================\n// Dependencies Interface\n// =============================================================================\n\nexport interface PaymentsModuleDependencies {\n identity: FullIdentity;\n storage: StorageProvider;\n /** @deprecated Use tokenStorageProviders instead */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Multiple token storage providers (e.g., IPFS, MongoDB, file) */\n tokenStorageProviders?: Map<string, TokenStorageProvider<TxfStorageDataBase>>;\n transport: TransportProvider;\n oracle: OracleProvider;\n emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;\n /** Chain code for BIP32 HD derivation (for L1 multi-address support) */\n chainCode?: string;\n /** Additional L1 addresses to watch */\n l1Addresses?: string[];\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class PaymentsModule {\n private readonly moduleConfig: Omit<Required<PaymentsModuleConfig>, 'l1'>;\n private deps: PaymentsModuleDependencies | null = null;\n\n /** L1 (ALPHA blockchain) payments sub-module (null if disabled) */\n readonly l1: L1PaymentsModule | null;\n\n // Token State\n private tokens: Map<string, Token> = new Map();\n private pendingTransfers: Map<string, TransferResult> = new Map();\n\n // Repository State (tombstones, archives, forked, history)\n private tombstones: TombstoneEntry[] = [];\n private archivedTokens: Map<string, TxfToken> = new Map();\n private forkedTokens: Map<string, TxfToken> = new Map();\n private transactionHistory: TransactionHistoryEntry[] = [];\n private nametag: NametagData | null = null;\n\n // Payment Requests State (Incoming)\n private paymentRequests: IncomingPaymentRequest[] = [];\n private paymentRequestHandlers: Set<PaymentRequestHandler> = new Set();\n\n // Payment Requests State (Outgoing)\n private outgoingPaymentRequests: Map<string, OutgoingPaymentRequest> = new Map();\n private paymentRequestResponseHandlers: Set<PaymentRequestResponseHandler> = new Set();\n private pendingResponseResolvers: Map<string, {\n resolve: (response: PaymentRequestResponse) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n }> = new Map();\n\n // Subscriptions\n private unsubscribeTransfers: (() => void) | null = null;\n private unsubscribePaymentRequests: (() => void) | null = null;\n private unsubscribePaymentRequestResponses: (() => void) | null = null;\n\n // NOSTR-FIRST proof polling (background proof verification)\n private proofPollingJobs: Map<string, ProofPollingJob> = new Map();\n private proofPollingInterval: ReturnType<typeof setInterval> | null = null;\n private static readonly PROOF_POLLING_INTERVAL_MS = 2000; // Poll every 2s\n private static readonly PROOF_POLLING_MAX_ATTEMPTS = 30; // Max 30 attempts (~60s)\n\n constructor(config?: PaymentsModuleConfig) {\n this.moduleConfig = {\n autoSync: config?.autoSync ?? true,\n autoValidate: config?.autoValidate ?? true,\n retryFailed: config?.retryFailed ?? true,\n maxRetries: config?.maxRetries ?? 3,\n debug: config?.debug ?? false,\n };\n\n // Initialize L1 sub-module by default (L1PaymentsModule has default electrumUrl).\n // Only skip if l1 is explicitly set to null.\n this.l1 = config?.l1 === null ? null : new L1PaymentsModule(config?.l1);\n }\n\n /** Get module configuration */\n getConfig(): Omit<Required<PaymentsModuleConfig>, 'l1'> {\n return this.moduleConfig;\n }\n\n /** Price provider (optional) */\n private priceProvider: PriceProvider | null = null;\n\n private log(...args: unknown[]): void {\n if (this.moduleConfig.debug) {\n console.log('[PaymentsModule]', ...args);\n }\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Initialize module with dependencies\n */\n initialize(deps: PaymentsModuleDependencies): void {\n // Clean up previous subscriptions before re-initializing\n this.unsubscribeTransfers?.();\n this.unsubscribeTransfers = null;\n this.unsubscribePaymentRequests?.();\n this.unsubscribePaymentRequests = null;\n this.unsubscribePaymentRequestResponses?.();\n this.unsubscribePaymentRequestResponses = null;\n\n // Reset per-address state (will be re-populated by load())\n this.tokens.clear();\n this.pendingTransfers.clear();\n this.tombstones = [];\n this.archivedTokens.clear();\n this.forkedTokens.clear();\n this.transactionHistory = [];\n this.nametag = null;\n\n this.deps = deps;\n this.priceProvider = deps.price ?? null;\n\n // Initialize L1 sub-module with chain code, addresses, and transport (if enabled)\n if (this.l1) {\n this.l1.initialize({\n identity: deps.identity,\n chainCode: deps.chainCode,\n addresses: deps.l1Addresses,\n transport: deps.transport,\n });\n }\n\n // Subscribe to incoming transfers\n this.unsubscribeTransfers = deps.transport.onTokenTransfer((transfer) => {\n this.handleIncomingTransfer(transfer);\n });\n\n // Subscribe to incoming payment requests (if supported)\n if (deps.transport.onPaymentRequest) {\n this.unsubscribePaymentRequests = deps.transport.onPaymentRequest((request) => {\n this.handleIncomingPaymentRequest(request);\n });\n }\n\n // Subscribe to payment request responses (if supported)\n if (deps.transport.onPaymentRequestResponse) {\n this.unsubscribePaymentRequestResponses = deps.transport.onPaymentRequestResponse((response) => {\n this.handlePaymentRequestResponse(response);\n });\n }\n }\n\n /**\n * Load tokens from storage\n */\n async load(): Promise<void> {\n this.ensureInitialized();\n\n // Load metadata from TokenStorageProviders (archived, tombstones, forked)\n // Active tokens are NOT stored in TXF - they are loaded from token-xxx files\n const providers = this.getTokenStorageProviders();\n for (const [id, provider] of providers) {\n try {\n const result = await provider.load();\n if (result.success && result.data) {\n this.loadFromStorageData(result.data);\n this.log(`Loaded metadata from provider ${id}`);\n break; // Use first successful provider\n }\n } catch (err) {\n console.error(`[Payments] Failed to load from provider ${id}:`, err);\n }\n }\n\n // Load active tokens from token-xxx files (primary storage for tokens)\n await this.loadTokensFromFileStorage();\n\n // Load nametag from file storage (nametag-{name}.json)\n // This is the primary source for nametag data now\n await this.loadNametagFromFileStorage();\n\n // Load transaction history\n const historyData = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);\n if (historyData) {\n try {\n this.transactionHistory = JSON.parse(historyData);\n } catch {\n this.transactionHistory = [];\n }\n }\n\n // Load pending transfers\n const pending = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);\n if (pending) {\n const transfers = JSON.parse(pending) as TransferResult[];\n for (const transfer of transfers) {\n this.pendingTransfers.set(transfer.id, transfer);\n }\n }\n }\n\n /**\n * Cleanup resources\n */\n destroy(): void {\n this.unsubscribeTransfers?.();\n this.unsubscribeTransfers = null;\n this.unsubscribePaymentRequests?.();\n this.unsubscribePaymentRequests = null;\n this.unsubscribePaymentRequestResponses?.();\n this.unsubscribePaymentRequestResponses = null;\n this.paymentRequestHandlers.clear();\n this.paymentRequestResponseHandlers.clear();\n\n // Stop proof polling (NOSTR-FIRST)\n this.stopProofPolling();\n this.proofPollingJobs.clear();\n\n // Clear pending response resolvers\n for (const [, resolver] of this.pendingResponseResolvers) {\n clearTimeout(resolver.timeout);\n resolver.reject(new Error('Module destroyed'));\n }\n this.pendingResponseResolvers.clear();\n\n if (this.l1) {\n this.l1.destroy();\n }\n }\n\n // ===========================================================================\n // Public API - Send\n // ===========================================================================\n\n /**\n * Send tokens to recipient\n * Supports automatic token splitting when exact amount is needed\n */\n async send(request: TransferRequest): Promise<TransferResult> {\n this.ensureInitialized();\n\n // Use mutable result for building the transfer\n const result: { -readonly [K in keyof TransferResult]: TransferResult[K] } = {\n id: crypto.randomUUID(),\n status: 'pending',\n tokens: [],\n };\n\n try {\n // Resolve recipient once — single network query\n const peerInfo = await this.deps!.transport.resolve?.(request.recipient) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(request.recipient, peerInfo);\n const recipientAddress = await this.resolveRecipientAddress(request.recipient, request.addressMode, peerInfo);\n\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available. Oracle provider must implement getStateTransitionClient()');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available. Oracle provider must implement getTrustBase()');\n }\n\n // Calculate optimal split plan\n const calculator = new TokenSplitCalculator();\n const availableTokens = Array.from(this.tokens.values());\n const splitPlan = await calculator.calculateOptimalSplit(\n availableTokens,\n BigInt(request.amount),\n request.coinId\n );\n\n if (!splitPlan) {\n throw new Error('Insufficient balance');\n }\n\n // Collect all tokens involved\n const tokensToSend: Token[] = splitPlan.tokensToTransferDirectly.map(t => t.uiToken);\n if (splitPlan.tokenToSplit) {\n tokensToSend.push(splitPlan.tokenToSplit.uiToken);\n }\n result.tokens = tokensToSend;\n\n // Mark as transferring\n for (const token of tokensToSend) {\n token.status = 'transferring';\n this.tokens.set(token.id, token);\n }\n\n // Save to outbox for recovery\n await this.saveToOutbox(result, recipientPubkey);\n\n result.status = 'submitted';\n\n const recipientNametag = request.recipient.startsWith('@') ? request.recipient.slice(1) : undefined;\n\n // Handle split if required\n if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {\n this.log('Executing token split...');\n\n const executor = new TokenSplitExecutor({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n });\n\n const splitResult = await executor.executeSplit(\n splitPlan.tokenToSplit.sdkToken,\n splitPlan.splitAmount!,\n splitPlan.remainderAmount!,\n splitPlan.coinId,\n recipientAddress\n );\n\n // Save change token for sender\n const changeTokenData = splitResult.tokenForSender.toJSON();\n const changeToken: Token = {\n id: crypto.randomUUID(),\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n name: this.getCoinName(request.coinId),\n decimals: this.getCoinDecimals(request.coinId),\n iconUrl: this.getCoinIconUrl(request.coinId),\n amount: splitPlan.remainderAmount!.toString(),\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(changeTokenData),\n };\n await this.addToken(changeToken, true); // Skip history for change\n this.log(`Change token saved: ${changeToken.id}, amount: ${changeToken.amount}`);\n\n // Send recipient token via Nostr (Sphere format)\n console.log(`[Payments] Sending split token to ${recipientPubkey.slice(0, 8)}... via Nostr`);\n await this.deps!.transport.sendTokenTransfer(recipientPubkey, {\n sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),\n transferTx: JSON.stringify(splitResult.recipientTransferTx.toJSON()),\n memo: request.memo,\n } as unknown as import('../../transport').TokenTransferPayload);\n console.log(`[Payments] Split token sent successfully`);\n\n // Remove the original token that was split — skipHistory because we record below\n await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);\n\n result.txHash = 'split-' + Date.now().toString(16);\n this.log(`Split transfer completed`);\n }\n\n // Transfer direct tokens (no split needed) - standard aggregator-first flow\n // NOTE: NOSTR-FIRST for direct transfers has receiver-side issues with commitment validation.\n // The InstantSplit V5 flow is used for splits which provides fast transfers.\n // For direct (non-split) tokens, we use the proven standard flow.\n for (const tokenWithAmount of splitPlan.tokensToTransferDirectly) {\n const token = tokenWithAmount.uiToken;\n\n // Create SDK transfer commitment\n const commitment = await this.createSdkCommitment(token, recipientAddress, signingService);\n\n // Submit commitment via SDK\n const response = await stClient.submitTransferCommitment(commitment);\n if (response.status !== 'SUCCESS' && response.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer commitment failed: ${response.status}`);\n }\n\n // Wait for inclusion proof using SDK\n if (!this.deps!.oracle.waitForProofSdk) {\n throw new Error('Oracle provider must implement waitForProofSdk()');\n }\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n\n // Create transfer transaction\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transferTx = commitment.toTransaction(inclusionProof as any);\n\n // Get request ID as hex string for tracking\n const requestIdBytes = commitment.requestId;\n result.txHash = requestIdBytes instanceof Uint8Array\n ? Array.from(requestIdBytes).map(b => b.toString(16).padStart(2, '0')).join('')\n : String(requestIdBytes);\n\n // Send via transport (Nostr) - use Sphere-compatible format\n console.log(`[Payments] Sending direct token ${token.id.slice(0, 8)}... to ${recipientPubkey.slice(0, 8)}... via Nostr`);\n await this.deps!.transport.sendTokenTransfer(recipientPubkey, {\n sourceToken: JSON.stringify(tokenWithAmount.sdkToken.toJSON()),\n transferTx: JSON.stringify(transferTx.toJSON()),\n memo: request.memo,\n } as unknown as import('../../transport').TokenTransferPayload);\n console.log(`[Payments] Direct token sent successfully`);\n\n this.log(`Token ${token.id} transferred, txHash: ${result.txHash}`);\n\n // Remove sent token (creates tombstone) — skipHistory because we record below\n await this.removeToken(token.id, recipientNametag, true);\n }\n\n result.status = 'delivered';\n\n // Save state\n await this.save();\n await this.removeFromOutbox(result.id);\n\n result.status = 'completed';\n\n // Add to transaction history\n await this.addToHistory({\n type: 'SENT',\n amount: request.amount,\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n timestamp: Date.now(),\n recipientNametag,\n });\n\n this.deps!.emitEvent('transfer:confirmed', result);\n return result;\n } catch (error) {\n result.status = 'failed';\n result.error = error instanceof Error ? error.message : String(error);\n\n // Restore tokens\n for (const token of result.tokens) {\n token.status = 'confirmed';\n this.tokens.set(token.id, token);\n }\n\n this.deps!.emitEvent('transfer:failed', result);\n throw error;\n }\n }\n\n /**\n * Get coin symbol from coinId\n */\n private getCoinSymbol(coinId: string): string {\n return TokenRegistry.getInstance().getSymbol(coinId);\n }\n\n /**\n * Get coin name from coinId\n */\n private getCoinName(coinId: string): string {\n return TokenRegistry.getInstance().getName(coinId);\n }\n\n /**\n * Get coin decimals from coinId\n */\n private getCoinDecimals(coinId: string): number {\n return TokenRegistry.getInstance().getDecimals(coinId);\n }\n\n /**\n * Get coin icon URL from coinId\n */\n private getCoinIconUrl(coinId: string): string | undefined {\n return TokenRegistry.getInstance().getIconUrl(coinId) ?? undefined;\n }\n\n // ===========================================================================\n // Public API - Instant Split (V5 Optimized)\n // ===========================================================================\n\n /**\n * Send tokens using INSTANT_SPLIT V5 optimized flow.\n *\n * This achieves ~2.3s critical path latency instead of ~42s by:\n * 1. Waiting only for burn proof (required)\n * 2. Creating transfer commitment from mint data (no mint proof needed)\n * 3. Sending bundle via Nostr immediately\n * 4. Processing mints in background\n *\n * @param request - Transfer request with recipient, amount, and coinId\n * @param options - Optional instant split configuration\n * @returns InstantSplitResult with timing info\n */\n async sendInstant(\n request: TransferRequest,\n options?: InstantSplitOptions\n ): Promise<InstantSplitResult> {\n this.ensureInitialized();\n\n const startTime = performance.now();\n\n try {\n // Resolve recipient once — single network query\n const peerInfo = await this.deps!.transport.resolve?.(request.recipient) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(request.recipient, peerInfo);\n const recipientAddress = await this.resolveRecipientAddress(request.recipient, request.addressMode, peerInfo);\n\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available');\n }\n\n // Calculate optimal split plan\n const calculator = new TokenSplitCalculator();\n const availableTokens = Array.from(this.tokens.values());\n const splitPlan = await calculator.calculateOptimalSplit(\n availableTokens,\n BigInt(request.amount),\n request.coinId\n );\n\n if (!splitPlan) {\n throw new Error('Insufficient balance');\n }\n\n if (!splitPlan.requiresSplit || !splitPlan.tokenToSplit) {\n // For direct transfers without split, fall back to standard flow\n this.log('No split required, falling back to standard send()');\n const result = await this.send(request);\n return {\n success: result.status === 'completed',\n criticalPathDurationMs: performance.now() - startTime,\n error: result.error,\n };\n }\n\n this.log(`InstantSplit: amount=${splitPlan.splitAmount}, remainder=${splitPlan.remainderAmount}`);\n\n // Mark token as transferring\n const tokenToSplit = splitPlan.tokenToSplit.uiToken;\n tokenToSplit.status = 'transferring';\n this.tokens.set(tokenToSplit.id, tokenToSplit);\n\n // Check if dev mode\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const devMode = options?.devMode ?? (this.deps!.oracle as any).isDevMode?.() ?? false;\n\n // Create instant split executor\n const executor = new InstantSplitExecutor({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n devMode,\n });\n\n // Execute instant split\n const result = await executor.executeSplitInstant(\n splitPlan.tokenToSplit.sdkToken,\n splitPlan.splitAmount!,\n splitPlan.remainderAmount!,\n splitPlan.coinId,\n recipientAddress,\n this.deps!.transport,\n recipientPubkey,\n {\n ...options,\n onChangeTokenCreated: async (changeToken) => {\n // Save change token when background completes\n const changeTokenData = changeToken.toJSON();\n const uiToken: Token = {\n id: crypto.randomUUID(),\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n name: this.getCoinName(request.coinId),\n decimals: this.getCoinDecimals(request.coinId),\n iconUrl: this.getCoinIconUrl(request.coinId),\n amount: splitPlan.remainderAmount!.toString(),\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(changeTokenData),\n };\n await this.addToken(uiToken, true);\n this.log(`Change token saved via background: ${uiToken.id}`);\n },\n onStorageSync: async () => {\n await this.save();\n return true;\n },\n }\n );\n\n if (result.success) {\n // Remove the original token — skipHistory because we record below\n const recipientNametag = request.recipient.startsWith('@') ? request.recipient.slice(1) : undefined;\n await this.removeToken(tokenToSplit.id, recipientNametag, true);\n\n // Add to transaction history (single entry for the actual sent amount)\n await this.addToHistory({\n type: 'SENT',\n amount: request.amount,\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n timestamp: Date.now(),\n recipientNametag,\n });\n\n await this.save();\n } else {\n // Restore token on failure\n tokenToSplit.status = 'confirmed';\n this.tokens.set(tokenToSplit.id, tokenToSplit);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n criticalPathDurationMs: performance.now() - startTime,\n error: errorMessage,\n };\n }\n }\n\n /**\n * Process a received INSTANT_SPLIT bundle.\n *\n * This should be called when receiving an instant split bundle via transport.\n * It handles the recipient-side processing:\n * 1. Validate burn transaction\n * 2. Submit and wait for mint proof\n * 3. Submit and wait for transfer proof\n * 4. Finalize and save the token\n *\n * @param bundle - The received InstantSplitBundle (V4 or V5)\n * @param senderPubkey - Sender's public key for verification\n * @returns Processing result with finalized token\n */\n async processInstantSplitBundle(\n bundle: InstantSplitBundle,\n senderPubkey: string\n ): Promise<InstantSplitProcessResult> {\n this.ensureInitialized();\n\n try {\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available');\n }\n\n // Check if dev mode\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const devMode = (this.deps!.oracle as any).isDevMode?.() ?? false;\n\n // Create processor\n const processor = new InstantSplitProcessor({\n stateTransitionClient: stClient,\n trustBase,\n devMode,\n });\n\n // Process the bundle\n const result = await processor.processReceivedBundle(\n bundle,\n signingService,\n senderPubkey,\n {\n findNametagToken: async (proxyAddress: string) => {\n if (this.nametag?.token) {\n try {\n const nametagToken = await SdkToken.fromJSON(this.nametag.token);\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address === proxyAddress) {\n return nametagToken;\n }\n this.log(`Nametag PROXY address mismatch: ${proxy.address} !== ${proxyAddress}`);\n return null;\n } catch (err) {\n this.log('Failed to parse nametag token:', err);\n return null;\n }\n }\n return null;\n },\n }\n );\n\n if (result.success && result.token) {\n // Save the received token\n const tokenData = result.token.toJSON();\n const info = await parseTokenInfo(tokenData);\n\n const uiToken: Token = {\n id: crypto.randomUUID(),\n coinId: info.coinId,\n symbol: info.symbol,\n name: info.name,\n decimals: info.decimals,\n iconUrl: info.iconUrl,\n amount: bundle.amount,\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(tokenData),\n };\n\n await this.addToken(uiToken);\n\n // Add to history\n await this.addToHistory({\n type: 'RECEIVED',\n amount: bundle.amount,\n coinId: info.coinId,\n symbol: info.symbol,\n timestamp: Date.now(),\n senderPubkey,\n });\n\n await this.save();\n\n // Emit event\n this.deps!.emitEvent('transfer:incoming', {\n id: bundle.splitGroupId,\n senderPubkey,\n tokens: [uiToken],\n receivedAt: Date.now(),\n });\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMessage,\n durationMs: 0,\n };\n }\n }\n\n /**\n * Check if a payload is an instant split bundle\n */\n isInstantSplitBundle(payload: unknown): payload is InstantSplitBundle {\n return isInstantSplitBundle(payload);\n }\n\n // ===========================================================================\n // Public API - Payment Requests\n // ===========================================================================\n\n /**\n * Send a payment request to someone\n * @param recipientPubkeyOrNametag - Recipient's pubkey or @nametag\n * @param request - Payment request details\n * @returns Result with event ID\n */\n async sendPaymentRequest(\n recipientPubkeyOrNametag: string,\n request: Omit<PaymentRequest, 'id' | 'createdAt'>\n ): Promise<PaymentRequestResult> {\n this.ensureInitialized();\n\n if (!this.deps!.transport.sendPaymentRequest) {\n return {\n success: false,\n error: 'Transport provider does not support payment requests',\n };\n }\n\n try {\n // Resolve recipient\n const peerInfo = await this.deps!.transport.resolve?.(recipientPubkeyOrNametag) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(recipientPubkeyOrNametag, peerInfo);\n\n // Build payload\n const payload: PaymentRequestPayload = {\n amount: request.amount,\n coinId: request.coinId,\n message: request.message,\n recipientNametag: request.recipientNametag,\n metadata: request.metadata,\n };\n\n // Send via transport\n const eventId = await this.deps!.transport.sendPaymentRequest(recipientPubkey, payload);\n const requestId = crypto.randomUUID();\n\n // Track outgoing request\n const outgoingRequest: OutgoingPaymentRequest = {\n id: requestId,\n eventId,\n recipientPubkey,\n recipientNametag: recipientPubkeyOrNametag.startsWith('@')\n ? recipientPubkeyOrNametag.slice(1)\n : undefined,\n amount: request.amount,\n coinId: request.coinId,\n message: request.message,\n createdAt: Date.now(),\n status: 'pending',\n };\n this.outgoingPaymentRequests.set(requestId, outgoingRequest);\n\n this.log(`Payment request sent: ${eventId}`);\n\n return {\n success: true,\n requestId,\n eventId,\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log(`Failed to send payment request: ${errorMsg}`);\n return {\n success: false,\n error: errorMsg,\n };\n }\n }\n\n /**\n * Subscribe to incoming payment requests\n * @param handler - Handler function for incoming requests\n * @returns Unsubscribe function\n */\n onPaymentRequest(handler: PaymentRequestHandler): () => void {\n this.paymentRequestHandlers.add(handler);\n return () => this.paymentRequestHandlers.delete(handler);\n }\n\n /**\n * Get all payment requests\n * @param filter - Optional status filter\n */\n getPaymentRequests(filter?: { status?: PaymentRequestStatus }): IncomingPaymentRequest[] {\n if (filter?.status) {\n return this.paymentRequests.filter((r) => r.status === filter.status);\n }\n return [...this.paymentRequests];\n }\n\n /**\n * Get pending payment requests count\n */\n getPendingPaymentRequestsCount(): number {\n return this.paymentRequests.filter((r) => r.status === 'pending').length;\n }\n\n /**\n * Accept a payment request (marks it as accepted, user should then call send())\n */\n async acceptPaymentRequest(requestId: string): Promise<void> {\n this.updatePaymentRequestStatus(requestId, 'accepted');\n await this.sendPaymentRequestResponse(requestId, 'accepted');\n }\n\n /**\n * Reject a payment request\n */\n async rejectPaymentRequest(requestId: string): Promise<void> {\n this.updatePaymentRequestStatus(requestId, 'rejected');\n await this.sendPaymentRequestResponse(requestId, 'rejected');\n }\n\n /**\n * Mark a payment request as paid (after successful transfer)\n */\n markPaymentRequestPaid(requestId: string): void {\n this.updatePaymentRequestStatus(requestId, 'paid');\n }\n\n /**\n * Clear processed (non-pending) payment requests\n */\n clearProcessedPaymentRequests(): void {\n this.paymentRequests = this.paymentRequests.filter((r) => r.status === 'pending');\n }\n\n /**\n * Remove a specific payment request\n */\n removePaymentRequest(requestId: string): void {\n this.paymentRequests = this.paymentRequests.filter((r) => r.id !== requestId);\n }\n\n /**\n * Pay a payment request directly\n * Convenience method that accepts, sends, and marks as paid\n */\n async payPaymentRequest(requestId: string, memo?: string): Promise<TransferResult> {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (!request) {\n throw new Error(`Payment request not found: ${requestId}`);\n }\n\n if (request.status !== 'pending' && request.status !== 'accepted') {\n throw new Error(`Payment request is not pending or accepted: ${request.status}`);\n }\n\n // Mark as accepted (don't send response yet, wait for payment)\n this.updatePaymentRequestStatus(requestId, 'accepted');\n\n try {\n // Send the payment\n const result = await this.send({\n coinId: request.coinId,\n amount: request.amount,\n recipient: request.senderPubkey,\n memo: memo || request.message,\n });\n\n // Mark as paid and send response with transfer ID\n this.updatePaymentRequestStatus(requestId, 'paid');\n await this.sendPaymentRequestResponse(requestId, 'paid', result.id);\n\n return result;\n } catch (error) {\n // Revert to pending on failure\n this.updatePaymentRequestStatus(requestId, 'pending');\n throw error;\n }\n }\n\n private updatePaymentRequestStatus(requestId: string, status: PaymentRequestStatus): void {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (request) {\n request.status = status;\n\n // Emit event\n const eventType = `payment_request:${status}` as const;\n if (eventType === 'payment_request:accepted' ||\n eventType === 'payment_request:rejected' ||\n eventType === 'payment_request:paid') {\n this.deps?.emitEvent(eventType, request);\n }\n }\n }\n\n private handleIncomingPaymentRequest(transportRequest: TransportPaymentRequest): void {\n // Check for duplicates\n if (this.paymentRequests.find((r) => r.id === transportRequest.id)) {\n return;\n }\n\n // Convert transport request to IncomingPaymentRequest\n const coinId = transportRequest.request.coinId;\n const registry = TokenRegistry.getInstance();\n const coinDef = registry.getDefinition(coinId);\n\n const request: IncomingPaymentRequest = {\n id: transportRequest.id,\n senderPubkey: transportRequest.senderTransportPubkey,\n senderNametag: transportRequest.senderNametag,\n amount: transportRequest.request.amount,\n coinId,\n symbol: coinDef?.symbol || coinId.slice(0, 8),\n message: transportRequest.request.message,\n recipientNametag: transportRequest.request.recipientNametag,\n requestId: transportRequest.request.requestId,\n timestamp: transportRequest.timestamp,\n status: 'pending',\n metadata: transportRequest.request.metadata,\n };\n\n // Add to list (newest first)\n this.paymentRequests.unshift(request);\n\n // Emit event\n this.deps?.emitEvent('payment_request:incoming', request);\n\n // Notify handlers\n for (const handler of this.paymentRequestHandlers) {\n try {\n handler(request);\n } catch (error) {\n this.log('Payment request handler error:', error);\n }\n }\n\n this.log(`Incoming payment request: ${request.id} for ${request.amount} ${request.symbol}`);\n }\n\n // ===========================================================================\n // Public API - Outgoing Payment Requests\n // ===========================================================================\n\n /**\n * Get outgoing payment requests\n * @param filter - Optional status filter\n */\n getOutgoingPaymentRequests(filter?: { status?: PaymentRequestStatus }): OutgoingPaymentRequest[] {\n const requests = Array.from(this.outgoingPaymentRequests.values());\n if (filter?.status) {\n return requests.filter((r) => r.status === filter.status);\n }\n return requests;\n }\n\n /**\n * Subscribe to payment request responses (for outgoing requests)\n * @param handler - Handler function for incoming responses\n * @returns Unsubscribe function\n */\n onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void {\n this.paymentRequestResponseHandlers.add(handler);\n return () => this.paymentRequestResponseHandlers.delete(handler);\n }\n\n /**\n * Wait for a response to a payment request\n * @param requestId - The outgoing request ID to wait for\n * @param timeoutMs - Timeout in milliseconds (default: 60000)\n * @returns Promise that resolves with the response or rejects on timeout\n */\n waitForPaymentResponse(requestId: string, timeoutMs: number = 60000): Promise<PaymentRequestResponse> {\n const outgoing = this.outgoingPaymentRequests.get(requestId);\n if (!outgoing) {\n return Promise.reject(new Error(`Outgoing payment request not found: ${requestId}`));\n }\n\n // If already has a response, return it\n if (outgoing.response) {\n return Promise.resolve(outgoing.response);\n }\n\n // Create a promise that resolves when response arrives or times out\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingResponseResolvers.delete(requestId);\n // Update status to expired\n const request = this.outgoingPaymentRequests.get(requestId);\n if (request && request.status === 'pending') {\n request.status = 'expired';\n }\n reject(new Error(`Payment request response timeout: ${requestId}`));\n }, timeoutMs);\n\n this.pendingResponseResolvers.set(requestId, { resolve, reject, timeout });\n });\n }\n\n /**\n * Cancel waiting for a payment response\n */\n cancelWaitForPaymentResponse(requestId: string): void {\n const resolver = this.pendingResponseResolvers.get(requestId);\n if (resolver) {\n clearTimeout(resolver.timeout);\n resolver.reject(new Error('Cancelled'));\n this.pendingResponseResolvers.delete(requestId);\n }\n }\n\n /**\n * Remove an outgoing payment request\n */\n removeOutgoingPaymentRequest(requestId: string): void {\n this.outgoingPaymentRequests.delete(requestId);\n this.cancelWaitForPaymentResponse(requestId);\n }\n\n /**\n * Clear completed/expired outgoing payment requests\n */\n clearCompletedOutgoingPaymentRequests(): void {\n for (const [id, request] of this.outgoingPaymentRequests) {\n if (request.status === 'paid' || request.status === 'rejected' || request.status === 'expired') {\n this.outgoingPaymentRequests.delete(id);\n }\n }\n }\n\n private handlePaymentRequestResponse(transportResponse: TransportPaymentRequestResponse): void {\n // Find the outgoing request by matching requestId\n let outgoingRequest: OutgoingPaymentRequest | undefined;\n let outgoingRequestId: string | undefined;\n\n for (const [id, request] of this.outgoingPaymentRequests) {\n // Match by eventId or requestId from the response\n if (request.eventId === transportResponse.response.requestId ||\n request.id === transportResponse.response.requestId) {\n outgoingRequest = request;\n outgoingRequestId = id;\n break;\n }\n }\n\n // Convert transport response to PaymentRequestResponse\n const response: PaymentRequestResponse = {\n id: transportResponse.id,\n responderPubkey: transportResponse.responderTransportPubkey,\n requestId: transportResponse.response.requestId,\n responseType: transportResponse.response.responseType,\n message: transportResponse.response.message,\n transferId: transportResponse.response.transferId,\n timestamp: transportResponse.timestamp,\n };\n\n // Update outgoing request if found\n if (outgoingRequest && outgoingRequestId) {\n outgoingRequest.status = response.responseType === 'paid' ? 'paid' :\n response.responseType === 'accepted' ? 'accepted' :\n 'rejected';\n outgoingRequest.response = response;\n\n // Resolve pending promise if any\n const resolver = this.pendingResponseResolvers.get(outgoingRequestId);\n if (resolver) {\n clearTimeout(resolver.timeout);\n resolver.resolve(response);\n this.pendingResponseResolvers.delete(outgoingRequestId);\n }\n }\n\n // Emit event\n this.deps?.emitEvent('payment_request:response', response);\n\n // Notify handlers\n for (const handler of this.paymentRequestResponseHandlers) {\n try {\n handler(response);\n } catch (error) {\n this.log('Payment request response handler error:', error);\n }\n }\n\n this.log(`Received payment request response: ${response.id} type: ${response.responseType}`);\n }\n\n /**\n * Send a response to a payment request (used internally by accept/reject/pay methods)\n */\n private async sendPaymentRequestResponse(\n requestId: string,\n responseType: 'accepted' | 'rejected' | 'paid',\n transferId?: string\n ): Promise<void> {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (!request) return;\n\n if (!this.deps?.transport.sendPaymentRequestResponse) {\n this.log('Transport does not support sendPaymentRequestResponse');\n return;\n }\n\n try {\n const payload: PaymentRequestResponsePayload = {\n requestId: request.requestId, // Original request ID from sender\n responseType,\n transferId,\n };\n\n await this.deps.transport.sendPaymentRequestResponse(request.senderPubkey, payload);\n this.log(`Sent payment request response: ${responseType} for ${requestId}`);\n } catch (error) {\n this.log('Failed to send payment request response:', error);\n }\n }\n\n // ===========================================================================\n // Public API - Balance & Tokens\n // ===========================================================================\n\n /**\n * Set or update price provider\n */\n setPriceProvider(provider: PriceProvider): void {\n this.priceProvider = provider;\n }\n\n /**\n * Get total portfolio value in USD\n * Returns null if PriceProvider is not configured\n */\n async getBalance(): Promise<number | null> {\n const assets = await this.getAssets();\n\n if (!this.priceProvider) {\n return null;\n }\n\n let total = 0;\n let hasAnyPrice = false;\n\n for (const asset of assets) {\n if (asset.fiatValueUsd != null) {\n total += asset.fiatValueUsd;\n hasAnyPrice = true;\n }\n }\n\n return hasAnyPrice ? total : null;\n }\n\n /**\n * Get aggregated assets (tokens grouped by coinId) with price data\n * Only includes confirmed tokens\n */\n async getAssets(coinId?: string): Promise<Asset[]> {\n // Aggregate tokens by coinId\n const assetsMap = new Map<string, {\n coinId: string;\n symbol: string;\n name: string;\n decimals: number;\n iconUrl?: string;\n totalAmount: string;\n tokenCount: number;\n }>();\n\n for (const token of this.tokens.values()) {\n if (token.status !== 'confirmed') continue;\n if (coinId && token.coinId !== coinId) continue;\n\n const key = token.coinId;\n const existing = assetsMap.get(key);\n\n if (existing) {\n existing.totalAmount = (\n BigInt(existing.totalAmount) + BigInt(token.amount)\n ).toString();\n existing.tokenCount++;\n } else {\n assetsMap.set(key, {\n coinId: token.coinId,\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n iconUrl: token.iconUrl,\n totalAmount: token.amount,\n tokenCount: 1,\n });\n }\n }\n\n const rawAssets = Array.from(assetsMap.values());\n\n // Fetch prices if provider is available\n let priceMap: Map<string, { priceUsd: number; priceEur?: number; change24h?: number }> | null = null;\n\n if (this.priceProvider && rawAssets.length > 0) {\n try {\n const registry = TokenRegistry.getInstance();\n const nameToCoins = new Map<string, string[]>(); // tokenName -> coinIds[]\n\n for (const asset of rawAssets) {\n const def = registry.getDefinition(asset.coinId);\n if (def?.name) {\n const existing = nameToCoins.get(def.name);\n if (existing) {\n existing.push(asset.coinId);\n } else {\n nameToCoins.set(def.name, [asset.coinId]);\n }\n }\n }\n\n if (nameToCoins.size > 0) {\n const tokenNames = Array.from(nameToCoins.keys());\n const prices = await this.priceProvider.getPrices(tokenNames);\n\n priceMap = new Map();\n for (const [name, coinIds] of nameToCoins) {\n const price = prices.get(name);\n if (price) {\n for (const cid of coinIds) {\n priceMap.set(cid, {\n priceUsd: price.priceUsd,\n priceEur: price.priceEur,\n change24h: price.change24h,\n });\n }\n }\n }\n }\n } catch (error) {\n console.warn('[Payments] Failed to fetch prices, returning assets without price data:', error);\n }\n }\n\n // Build final Asset array with price data\n return rawAssets.map((raw) => {\n const price = priceMap?.get(raw.coinId);\n let fiatValueUsd: number | null = null;\n let fiatValueEur: number | null = null;\n\n if (price) {\n const humanAmount = Number(raw.totalAmount) / Math.pow(10, raw.decimals);\n fiatValueUsd = humanAmount * price.priceUsd;\n if (price.priceEur != null) {\n fiatValueEur = humanAmount * price.priceEur;\n }\n }\n\n return {\n coinId: raw.coinId,\n symbol: raw.symbol,\n name: raw.name,\n decimals: raw.decimals,\n iconUrl: raw.iconUrl,\n totalAmount: raw.totalAmount,\n tokenCount: raw.tokenCount,\n priceUsd: price?.priceUsd ?? null,\n priceEur: price?.priceEur ?? null,\n change24h: price?.change24h ?? null,\n fiatValueUsd,\n fiatValueEur,\n };\n });\n }\n\n /**\n * Get all tokens\n */\n getTokens(filter?: { coinId?: string; status?: TokenStatus }): Token[] {\n let tokens = Array.from(this.tokens.values());\n\n if (filter?.coinId) {\n tokens = tokens.filter((t) => t.coinId === filter.coinId);\n }\n if (filter?.status) {\n tokens = tokens.filter((t) => t.status === filter.status);\n }\n\n return tokens;\n }\n\n /**\n * Get single token\n */\n getToken(id: string): Token | undefined {\n return this.tokens.get(id);\n }\n\n // ===========================================================================\n // Public API - Token Operations\n // ===========================================================================\n\n /**\n * Add a token\n * Tokens are uniquely identified by (tokenId, stateHash) composite key.\n * Multiple historic states of the same token can coexist.\n * @returns false if exact duplicate (same tokenId AND same stateHash)\n */\n async addToken(token: Token, skipHistory: boolean = false): Promise<boolean> {\n this.ensureInitialized();\n\n const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);\n const incomingStateHash = extractStateHashFromSdkData(token.sdkData);\n const incomingStateKey = incomingTokenId && incomingStateHash\n ? createTokenStateKey(incomingTokenId, incomingStateHash)\n : null;\n\n // Check tombstones - reject tokens with exact (tokenId, stateHash) match\n // This prevents spent tokens from being re-added via Nostr re-delivery\n // Tokens with the same tokenId but DIFFERENT stateHash are allowed (new state)\n if (incomingTokenId && incomingStateHash && this.isStateTombstoned(incomingTokenId, incomingStateHash)) {\n this.log(`Rejecting tombstoned token: ${incomingTokenId.slice(0, 8)}..._${incomingStateHash.slice(0, 8)}...`);\n return false;\n }\n\n // Check for exact duplicate (same tokenId AND same stateHash)\n if (incomingStateKey) {\n for (const [existingId, existing] of this.tokens) {\n if (isSameTokenState(existing, token)) {\n // Exact duplicate - same tokenId and same stateHash\n this.log(`Duplicate token state ignored: ${incomingTokenId?.slice(0, 8)}..._${incomingStateHash?.slice(0, 8)}...`);\n return false;\n }\n }\n }\n\n // Check for older states of the same token (same tokenId, different stateHash)\n // Replace older states with the new state\n for (const [existingId, existing] of this.tokens) {\n if (hasSameGenesisTokenId(existing, token)) {\n const existingStateHash = extractStateHashFromSdkData(existing.sdkData);\n\n // Skip if same state (already handled above)\n if (incomingStateHash && existingStateHash && incomingStateHash === existingStateHash) {\n continue;\n }\n\n // CASE 1: Existing token is spent/invalid - allow replacement\n if (existing.status === 'spent' || existing.status === 'invalid') {\n this.log(`Replacing spent/invalid token ${incomingTokenId?.slice(0, 8)}...`);\n this.tokens.delete(existingId);\n break;\n }\n\n // CASE 2: Different stateHash - this is a newer state of the token\n // Remove old state (it will be archived) and add new state\n if (incomingStateHash && existingStateHash && incomingStateHash !== existingStateHash) {\n this.log(`Token ${incomingTokenId?.slice(0, 8)}... state updated: ${existingStateHash.slice(0, 8)}... -> ${incomingStateHash.slice(0, 8)}...`);\n // Archive old state before removing\n await this.archiveToken(existing);\n this.tokens.delete(existingId);\n break;\n }\n\n // CASE 3: No state hashes available - use .id as heuristic\n if (!incomingStateHash || !existingStateHash) {\n if (existingId !== token.id) {\n this.log(`Token ${incomingTokenId?.slice(0, 8)}... .id changed, replacing`);\n await this.archiveToken(existing);\n this.tokens.delete(existingId);\n break;\n }\n }\n }\n }\n\n // Add the new token state\n this.tokens.set(token.id, token);\n\n // Archive the token (for recovery purposes)\n await this.archiveToken(token);\n\n // Add to transaction history\n if (!skipHistory && token.coinId && token.amount) {\n await this.addToHistory({\n type: 'RECEIVED',\n amount: token.amount,\n coinId: token.coinId,\n symbol: token.symbol || 'UNK',\n timestamp: token.createdAt || Date.now(),\n });\n }\n\n await this.save();\n\n // Save as individual token file (like lottery pattern)\n await this.saveTokenToFileStorage(token);\n\n this.log(`Added token ${token.id}, total: ${this.tokens.size}`);\n return true;\n }\n\n /**\n * Save token as individual file to token storage providers\n * Similar to lottery's saveReceivedToken() pattern\n */\n private async saveTokenToFileStorage(token: Token): Promise<void> {\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) return;\n\n // Extract SDK token ID for filename\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n const tokenIdPrefix = sdkTokenId ? sdkTokenId.slice(0, 16) : token.id.slice(0, 16);\n const filename = `token-${tokenIdPrefix}-${Date.now()}`;\n\n // Token data to save (similar to lottery format)\n const tokenData = {\n token: token.sdkData ? JSON.parse(token.sdkData) : null,\n receivedAt: Date.now(),\n meta: {\n id: token.id,\n coinId: token.coinId,\n symbol: token.symbol,\n amount: token.amount,\n status: token.status,\n },\n };\n\n // Save to all token storage providers\n for (const [providerId, provider] of providers) {\n try {\n if (provider.saveToken) {\n await provider.saveToken(filename, tokenData);\n this.log(`Saved token file ${filename} to ${providerId}`);\n }\n } catch (error) {\n console.warn(`[Payments] Failed to save token to ${providerId}:`, error);\n }\n }\n }\n\n /**\n * Load tokens from file storage providers (lottery compatibility)\n * This loads tokens from file-based storage that may have been saved\n * by other applications using the same storage directory.\n */\n private async loadTokensFromFileStorage(): Promise<void> {\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) return;\n\n for (const [providerId, provider] of providers) {\n if (!provider.listTokenIds || !provider.getToken) continue;\n\n try {\n const allIds = await provider.listTokenIds();\n // Only load token-xxx entries (not archived-, nametag-, or raw hex IDs)\n const tokenIds = allIds.filter(id => id.startsWith('token-'));\n this.log(`Found ${tokenIds.length} token files in ${providerId}`);\n\n for (const tokenId of tokenIds) {\n try {\n const fileData = await provider.getToken(tokenId);\n if (!fileData || typeof fileData !== 'object') continue;\n\n // Handle lottery format: { token, receivedAt } or { token, receivedAt, meta }\n const data = fileData as Record<string, unknown>;\n const tokenJson = data.token;\n if (!tokenJson) continue;\n\n // Check if already loaded from key-value storage\n let sdkTokenId: string | undefined;\n if (typeof tokenJson === 'object' && tokenJson !== null) {\n const tokenObj = tokenJson as Record<string, unknown>;\n const genesis = tokenObj.genesis as Record<string, unknown> | undefined;\n const genesisData = genesis?.data as Record<string, unknown> | undefined;\n sdkTokenId = genesisData?.tokenId as string | undefined;\n }\n\n if (sdkTokenId) {\n // Check if this token already exists\n let exists = false;\n for (const existing of this.tokens.values()) {\n const existingId = extractTokenIdFromSdkData(existing.sdkData);\n if (existingId === sdkTokenId) {\n exists = true;\n break;\n }\n }\n if (exists) continue;\n }\n\n // Parse token info\n const tokenInfo = await parseTokenInfo(tokenJson);\n\n // Create token entry\n const token: Token = {\n id: tokenInfo.tokenId ?? tokenId,\n coinId: tokenInfo.coinId,\n symbol: tokenInfo.symbol,\n name: tokenInfo.name,\n decimals: tokenInfo.decimals,\n iconUrl: tokenInfo.iconUrl,\n amount: tokenInfo.amount,\n status: 'confirmed',\n createdAt: (data.receivedAt as number) || Date.now(),\n updatedAt: Date.now(),\n sdkData: typeof tokenJson === 'string'\n ? tokenJson\n : JSON.stringify(tokenJson),\n };\n\n // Check if this token is tombstoned (was previously removed/sent)\n const loadedTokenId = extractTokenIdFromSdkData(token.sdkData);\n const loadedStateHash = extractStateHashFromSdkData(token.sdkData);\n if (loadedTokenId && loadedStateHash && this.isStateTombstoned(loadedTokenId, loadedStateHash)) {\n this.log(`Skipping tombstoned token file ${tokenId} (${loadedTokenId.slice(0, 8)}...)`);\n continue;\n }\n\n // Add to in-memory storage (skip file save since it's already in file)\n this.tokens.set(token.id, token);\n this.log(`Loaded token from file: ${tokenId}`);\n } catch (tokenError) {\n console.warn(`[Payments] Failed to load token ${tokenId}:`, tokenError);\n }\n }\n } catch (error) {\n console.warn(`[Payments] Failed to load tokens from ${providerId}:`, error);\n }\n }\n\n this.log(`Loaded ${this.tokens.size} tokens from file storage`);\n }\n\n /**\n * Update an existing token\n */\n async updateToken(token: Token): Promise<void> {\n this.ensureInitialized();\n\n const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);\n let found = false;\n\n // Find by genesis tokenId first\n for (const [id, existing] of this.tokens) {\n const existingTokenId = extractTokenIdFromSdkData(existing.sdkData);\n if ((existingTokenId && incomingTokenId && existingTokenId === incomingTokenId) ||\n existing.id === token.id) {\n this.tokens.delete(id);\n this.tokens.set(token.id, token);\n found = true;\n break;\n }\n }\n\n if (!found) {\n await this.addToken(token, true);\n return;\n }\n\n // Archive the updated token\n await this.archiveToken(token);\n\n await this.save();\n this.log(`Updated token ${token.id}`);\n }\n\n /**\n * Remove a token by ID\n */\n async removeToken(tokenId: string, recipientNametag?: string, skipHistory: boolean = false): Promise<void> {\n this.ensureInitialized();\n\n const token = this.tokens.get(tokenId);\n if (!token) return;\n\n // Archive before removing\n await this.archiveToken(token);\n\n // Create tombstone with exact (tokenId, stateHash) - requires both\n const tombstone = createTombstoneFromToken(token);\n if (tombstone) {\n const alreadyTombstoned = this.tombstones.some(\n t => t.tokenId === tombstone.tokenId && t.stateHash === tombstone.stateHash\n );\n if (!alreadyTombstoned) {\n this.tombstones.push(tombstone);\n this.log(`Created tombstone for ${tombstone.tokenId.slice(0, 8)}..._${tombstone.stateHash.slice(0, 8)}...`);\n }\n } else {\n // No valid tombstone could be created (missing tokenId or stateHash)\n // Token will still be removed but may be re-synced later\n this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);\n }\n\n // Remove from active tokens\n this.tokens.delete(tokenId);\n\n // Delete physical token file(s) from storage providers\n await this.deleteTokenFiles(token);\n\n // Add to transaction history\n if (!skipHistory && token.coinId && token.amount) {\n await this.addToHistory({\n type: 'SENT',\n amount: token.amount,\n coinId: token.coinId,\n symbol: token.symbol || 'UNK',\n timestamp: Date.now(),\n recipientNametag,\n });\n }\n\n await this.save();\n }\n\n /**\n * Delete physical token file(s) from all storage providers.\n * Finds files by matching the SDK token ID prefix in the filename.\n */\n private async deleteTokenFiles(token: Token): Promise<void> {\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n if (!sdkTokenId) return;\n\n const tokenIdPrefix = sdkTokenId.slice(0, 16);\n const providers = this.getTokenStorageProviders();\n\n for (const [providerId, provider] of providers) {\n if (!provider.listTokenIds || !provider.deleteToken) continue;\n try {\n const allIds = await provider.listTokenIds();\n const matchingFiles = allIds.filter(id =>\n id.startsWith(`token-${tokenIdPrefix}`)\n );\n for (const fileId of matchingFiles) {\n await provider.deleteToken(fileId);\n this.log(`Deleted token file ${fileId} from ${providerId}`);\n }\n } catch (error) {\n console.warn(`[Payments] Failed to delete token files from ${providerId}:`, error);\n }\n }\n }\n\n // ===========================================================================\n // Public API - Tombstones\n // ===========================================================================\n\n /**\n * Get all tombstones\n */\n getTombstones(): TombstoneEntry[] {\n return [...this.tombstones];\n }\n\n /**\n * Check if token state is tombstoned\n */\n isStateTombstoned(tokenId: string, stateHash: string): boolean {\n return this.tombstones.some(\n t => t.tokenId === tokenId && t.stateHash === stateHash\n );\n }\n\n /**\n * Merge remote tombstones\n * @returns number of local tokens removed\n */\n async mergeTombstones(remoteTombstones: TombstoneEntry[]): Promise<number> {\n this.ensureInitialized();\n\n let removedCount = 0;\n const tombstoneKeys = new Set(\n remoteTombstones.map(t => `${t.tokenId}:${t.stateHash}`)\n );\n\n // Find tokens to remove\n const tokensToRemove: Token[] = [];\n for (const token of this.tokens.values()) {\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n const currentStateHash = extractStateHashFromSdkData(token.sdkData);\n\n const key = `${sdkTokenId}:${currentStateHash}`;\n if (tombstoneKeys.has(key)) {\n tokensToRemove.push(token);\n }\n }\n\n for (const token of tokensToRemove) {\n this.tokens.delete(token.id);\n this.log(`Removed tombstoned token ${token.id.slice(0, 8)}...`);\n removedCount++;\n }\n\n // Merge tombstones (union)\n for (const remoteTombstone of remoteTombstones) {\n const alreadyExists = this.tombstones.some(\n t => t.tokenId === remoteTombstone.tokenId && t.stateHash === remoteTombstone.stateHash\n );\n if (!alreadyExists) {\n this.tombstones.push(remoteTombstone);\n }\n }\n\n if (removedCount > 0) {\n await this.save();\n }\n\n return removedCount;\n }\n\n /**\n * Prune old tombstones\n */\n async pruneTombstones(maxAge?: number): Promise<void> {\n const originalCount = this.tombstones.length;\n this.tombstones = pruneTombstonesByAge(this.tombstones, maxAge);\n\n if (this.tombstones.length < originalCount) {\n await this.save();\n this.log(`Pruned tombstones from ${originalCount} to ${this.tombstones.length}`);\n }\n }\n\n // ===========================================================================\n // Public API - Archives\n // ===========================================================================\n\n /**\n * Get archived tokens\n */\n getArchivedTokens(): Map<string, TxfToken> {\n return new Map(this.archivedTokens);\n }\n\n /**\n * Get best archived version of a token\n */\n getBestArchivedVersion(tokenId: string): TxfToken | null {\n return findBestTokenVersion(tokenId, this.archivedTokens, this.forkedTokens);\n }\n\n /**\n * Merge remote archived tokens\n * @returns number of tokens updated/added\n */\n async mergeArchivedTokens(remoteArchived: Map<string, TxfToken>): Promise<number> {\n let mergedCount = 0;\n\n for (const [tokenId, remoteTxf] of remoteArchived) {\n const existingArchive = this.archivedTokens.get(tokenId);\n\n if (!existingArchive) {\n this.archivedTokens.set(tokenId, remoteTxf);\n mergedCount++;\n } else if (isIncrementalUpdate(existingArchive, remoteTxf)) {\n this.archivedTokens.set(tokenId, remoteTxf);\n mergedCount++;\n } else if (!isIncrementalUpdate(remoteTxf, existingArchive)) {\n // It's a fork\n const stateHash = getCurrentStateHash(remoteTxf) || '';\n await this.storeForkedToken(tokenId, stateHash, remoteTxf);\n }\n }\n\n if (mergedCount > 0) {\n await this.save();\n }\n\n return mergedCount;\n }\n\n /**\n * Prune archived tokens\n */\n async pruneArchivedTokens(maxCount: number = 100): Promise<void> {\n if (this.archivedTokens.size <= maxCount) return;\n\n const originalCount = this.archivedTokens.size;\n this.archivedTokens = pruneMapByCount(this.archivedTokens, maxCount);\n\n await this.save();\n this.log(`Pruned archived tokens from ${originalCount} to ${this.archivedTokens.size}`);\n }\n\n // ===========================================================================\n // Public API - Forked Tokens\n // ===========================================================================\n\n /**\n * Get forked tokens\n */\n getForkedTokens(): Map<string, TxfToken> {\n return new Map(this.forkedTokens);\n }\n\n /**\n * Store a forked token\n */\n async storeForkedToken(tokenId: string, stateHash: string, txfToken: TxfToken): Promise<void> {\n const key = `${tokenId}_${stateHash}`;\n if (this.forkedTokens.has(key)) return;\n\n this.forkedTokens.set(key, txfToken);\n this.log(`Stored forked token ${tokenId.slice(0, 8)}... state ${stateHash.slice(0, 12)}...`);\n await this.save();\n }\n\n /**\n * Merge remote forked tokens\n * @returns number of tokens added\n */\n async mergeForkedTokens(remoteForked: Map<string, TxfToken>): Promise<number> {\n let addedCount = 0;\n\n for (const [key, remoteTxf] of remoteForked) {\n if (!this.forkedTokens.has(key)) {\n this.forkedTokens.set(key, remoteTxf);\n addedCount++;\n }\n }\n\n if (addedCount > 0) {\n await this.save();\n }\n\n return addedCount;\n }\n\n /**\n * Prune forked tokens\n */\n async pruneForkedTokens(maxCount: number = 50): Promise<void> {\n if (this.forkedTokens.size <= maxCount) return;\n\n const originalCount = this.forkedTokens.size;\n this.forkedTokens = pruneMapByCount(this.forkedTokens, maxCount);\n\n await this.save();\n this.log(`Pruned forked tokens from ${originalCount} to ${this.forkedTokens.size}`);\n }\n\n // ===========================================================================\n // Public API - Transaction History\n // ===========================================================================\n\n /**\n * Get transaction history\n */\n getHistory(): TransactionHistoryEntry[] {\n return [...this.transactionHistory].sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Add to transaction history\n */\n async addToHistory(entry: Omit<TransactionHistoryEntry, 'id'>): Promise<void> {\n this.ensureInitialized();\n\n const historyEntry: TransactionHistoryEntry = {\n id: crypto.randomUUID(),\n ...entry,\n };\n this.transactionHistory.push(historyEntry);\n\n await this.deps!.storage.set(\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n JSON.stringify(this.transactionHistory)\n );\n }\n\n // ===========================================================================\n // Public API - Nametag\n // ===========================================================================\n\n /**\n * Set nametag for current identity\n */\n async setNametag(nametag: NametagData): Promise<void> {\n this.ensureInitialized();\n this.nametag = nametag;\n await this.save();\n // Save to file storage for lottery compatibility\n await this.saveNametagToFileStorage(nametag);\n this.log(`Nametag set: ${nametag.name}`);\n }\n\n /**\n * Get nametag\n */\n getNametag(): NametagData | null {\n return this.nametag;\n }\n\n /**\n * Check if has nametag\n */\n hasNametag(): boolean {\n return this.nametag !== null;\n }\n\n /**\n * Clear nametag\n */\n async clearNametag(): Promise<void> {\n this.ensureInitialized();\n this.nametag = null;\n await this.save();\n }\n\n /**\n * Save nametag to file storage for lottery compatibility\n * Creates file: nametag-{name}.json\n */\n private async saveNametagToFileStorage(nametag: NametagData): Promise<void> {\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) return;\n\n const filename = `nametag-${nametag.name}`;\n\n // Lottery-compatible format\n const fileData = {\n nametag: nametag.name,\n token: nametag.token,\n timestamp: nametag.timestamp || Date.now(),\n };\n\n for (const [providerId, provider] of providers) {\n try {\n if (provider.saveToken) {\n await provider.saveToken(filename, fileData);\n this.log(`Saved nametag file ${filename} to ${providerId}`);\n }\n } catch (error) {\n console.warn(`[Payments] Failed to save nametag to ${providerId}:`, error);\n }\n }\n }\n\n /**\n * Load nametag from file storage (lottery compatibility)\n * Looks for file: nametag-{name}.json\n */\n private async loadNametagFromFileStorage(): Promise<void> {\n if (this.nametag) return; // Already loaded from key-value storage\n\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) return;\n\n for (const [providerId, provider] of providers) {\n if (!provider.listTokenIds || !provider.getToken) continue;\n\n try {\n const tokenIds = await provider.listTokenIds();\n const nametagFiles = tokenIds.filter(id => id.startsWith('nametag-'));\n\n for (const nametagFile of nametagFiles) {\n try {\n const fileData = await provider.getToken(nametagFile);\n if (!fileData || typeof fileData !== 'object') continue;\n\n const data = fileData as Record<string, unknown>;\n if (!data.token || !data.nametag) continue;\n\n // Convert to NametagData format\n this.nametag = {\n name: data.nametag as string,\n token: data.token as object,\n timestamp: (data.timestamp as number) || Date.now(),\n format: 'lottery',\n version: '1.0',\n };\n\n this.log(`Loaded nametag from file: ${nametagFile}`);\n return; // Found one, stop searching\n } catch (fileError) {\n console.warn(`[Payments] Failed to load nametag file ${nametagFile}:`, fileError);\n }\n }\n } catch (error) {\n console.warn(`[Payments] Failed to search nametag files in ${providerId}:`, error);\n }\n }\n }\n\n /**\n * Mint a nametag token on-chain (like Sphere wallet and lottery)\n * This creates the nametag token required for receiving tokens via PROXY addresses\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @returns MintNametagResult with success status and token if successful\n */\n async mintNametag(nametag: string): Promise<MintNametagResult> {\n this.ensureInitialized();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.();\n if (!stClient) {\n return {\n success: false,\n error: 'State transition client not available. Oracle provider must implement getStateTransitionClient()',\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n return {\n success: false,\n error: 'Trust base not available. Oracle provider must implement getTrustBase()',\n };\n }\n\n try {\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Create owner address using UnmaskedPredicateReference (same pattern as TokenSplitExecutor)\n const { UnmaskedPredicateReference } = await import('@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference');\n const { TokenType } = await import('@unicitylabs/state-transition-sdk/lib/token/TokenType');\n\n // Use a dummy token type for address creation (like Sphere wallet does)\n const UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n const tokenType = new TokenType(Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex'));\n\n const addressRef = await UnmaskedPredicateReference.create(\n tokenType,\n signingService.algorithm,\n signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const ownerAddress = await addressRef.toAddress();\n\n // Create NametagMinter\n const minter = new NametagMinter({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n debug: this.moduleConfig.debug,\n });\n\n // Mint the nametag\n const result = await minter.mintNametag(nametag, ownerAddress);\n\n if (result.success && result.nametagData) {\n // Save the nametag data\n await this.setNametag(result.nametagData);\n this.log(`Nametag minted and saved: ${result.nametagData.name}`);\n\n // Emit event (use existing nametag:registered event type)\n this.deps!.emitEvent('nametag:registered', {\n nametag: result.nametagData.name,\n addressIndex: 0, // Primary address\n });\n }\n\n return result;\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log('mintNametag failed:', errorMsg);\n return {\n success: false,\n error: errorMsg,\n };\n }\n }\n\n /**\n * Check if a nametag is available for minting\n * @param nametag - The nametag to check (e.g., \"alice\" or \"@alice\")\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n this.ensureInitialized();\n\n const stClient = this.deps!.oracle.getStateTransitionClient?.();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n\n if (!stClient || !trustBase) {\n return false;\n }\n\n try {\n const signingService = await this.createSigningService();\n const minter = new NametagMinter({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n });\n\n return await minter.isNametagAvailable(nametag);\n } catch {\n return false;\n }\n }\n\n // ===========================================================================\n // Public API - Sync & Validate\n // ===========================================================================\n\n /**\n * Sync with all token storage providers (IPFS, MongoDB, etc.)\n * Syncs with each provider and merges results\n */\n async sync(): Promise<{ added: number; removed: number }> {\n this.ensureInitialized();\n\n this.deps!.emitEvent('sync:started', { source: 'payments' });\n\n try {\n // Get all token storage providers\n const providers = this.getTokenStorageProviders();\n\n if (providers.size === 0) {\n // No providers - just save locally\n await this.save();\n this.deps!.emitEvent('sync:completed', {\n source: 'payments',\n count: this.tokens.size,\n });\n return { added: 0, removed: 0 };\n }\n\n // Create local data once\n const localData = await this.createStorageData();\n\n let totalAdded = 0;\n let totalRemoved = 0;\n\n // Sync with each provider\n for (const [providerId, provider] of providers) {\n try {\n const result = await provider.sync(localData);\n\n if (result.success && result.merged) {\n // Apply merged data from each provider\n this.loadFromStorageData(result.merged);\n totalAdded += result.added;\n totalRemoved += result.removed;\n }\n\n this.deps!.emitEvent('sync:provider', {\n providerId,\n success: result.success,\n added: result.added,\n removed: result.removed,\n });\n } catch (providerError) {\n // Log error but continue with other providers\n console.warn(`[PaymentsModule] Sync failed for provider ${providerId}:`, providerError);\n this.deps!.emitEvent('sync:provider', {\n providerId,\n success: false,\n error: providerError instanceof Error ? providerError.message : String(providerError),\n });\n }\n }\n\n this.deps!.emitEvent('sync:completed', {\n source: 'payments',\n count: this.tokens.size,\n });\n\n return { added: totalAdded, removed: totalRemoved };\n } catch (error) {\n this.deps!.emitEvent('sync:error', {\n source: 'payments',\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n /**\n * Get all active token storage providers\n */\n private getTokenStorageProviders(): Map<string, TokenStorageProvider<TxfStorageDataBase>> {\n // Prefer new multi-provider map\n if (this.deps!.tokenStorageProviders && this.deps!.tokenStorageProviders.size > 0) {\n return this.deps!.tokenStorageProviders;\n }\n\n // Fallback to deprecated single provider\n if (this.deps!.tokenStorage) {\n const map = new Map<string, TokenStorageProvider<TxfStorageDataBase>>();\n map.set(this.deps!.tokenStorage.id, this.deps!.tokenStorage);\n return map;\n }\n\n return new Map();\n }\n\n /**\n * Update token storage providers (called when providers are added/removed dynamically)\n */\n updateTokenStorageProviders(providers: Map<string, TokenStorageProvider<TxfStorageDataBase>>): void {\n if (this.deps) {\n this.deps.tokenStorageProviders = providers;\n }\n }\n\n /**\n * Validate tokens with aggregator\n */\n async validate(): Promise<{ valid: Token[]; invalid: Token[] }> {\n this.ensureInitialized();\n\n const valid: Token[] = [];\n const invalid: Token[] = [];\n\n for (const token of this.tokens.values()) {\n const result = await this.deps!.oracle.validateToken(token.sdkData);\n\n if (result.valid && !result.spent) {\n valid.push(token);\n } else {\n token.status = 'invalid';\n invalid.push(token);\n }\n }\n\n if (invalid.length > 0) {\n await this.save();\n }\n\n return { valid, invalid };\n }\n\n /**\n * Get pending transfers\n */\n getPendingTransfers(): TransferResult[] {\n return Array.from(this.pendingTransfers.values());\n }\n\n // ===========================================================================\n // Private: Transfer Operations\n // ===========================================================================\n\n /**\n * Detect if a string is an L3 address (not a nametag)\n * Returns true for: hex pubkeys (64+ chars), PROXY:, DIRECT: prefixed addresses\n */\n /**\n * Resolve recipient to transport pubkey for messaging.\n * Uses pre-resolved PeerInfo if available, otherwise resolves via transport.\n */\n private resolveTransportPubkey(recipient: string, peerInfo?: PeerInfo | null): string {\n // If we have PeerInfo, use it\n if (peerInfo?.transportPubkey) {\n return peerInfo.transportPubkey;\n }\n\n // Hex pubkey (64+ hex chars) — use as transport pubkey directly\n if (recipient.length >= 64 && /^[0-9a-fA-F]+$/.test(recipient)) {\n // 66-char with 02/03 prefix — strip to 32-byte x-only\n if (recipient.length === 66 && (recipient.startsWith('02') || recipient.startsWith('03'))) {\n return recipient.slice(2);\n }\n return recipient;\n }\n\n throw new Error(\n `Cannot resolve transport pubkey for \"${recipient}\". ` +\n `No binding event found. The recipient must publish their identity first.`\n );\n }\n\n /**\n * Create SDK TransferCommitment for a token transfer\n */\n private async createSdkCommitment(\n token: Token,\n recipientAddress: IAddress,\n signingService: SigningService\n ): Promise<TransferCommitment> {\n // Parse SDK token from stored data\n const tokenData = token.sdkData\n ? (typeof token.sdkData === 'string' ? JSON.parse(token.sdkData) : token.sdkData)\n : token;\n\n const sdkToken = await SdkToken.fromJSON(tokenData);\n\n // Generate random salt\n const salt = crypto.getRandomValues(new Uint8Array(32));\n\n // Create transfer commitment\n const commitment = await TransferCommitment.create(\n sdkToken,\n recipientAddress,\n salt,\n null, // recipientData\n null, // recipientDataHash\n signingService\n );\n\n return commitment;\n }\n\n /**\n * Create SigningService from identity private key\n */\n private async createSigningService(): Promise<SigningService> {\n const privateKeyHex = this.deps!.identity.privateKey;\n const privateKeyBytes = new Uint8Array(\n privateKeyHex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n );\n return SigningService.createFromSecret(privateKeyBytes);\n }\n\n /**\n * Create DirectAddress from a public key using UnmaskedPredicateReference\n */\n private async createDirectAddressFromPubkey(pubkeyHex: string): Promise<IAddress> {\n const { UnmaskedPredicateReference } = await import('@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference');\n const { TokenType } = await import('@unicitylabs/state-transition-sdk/lib/token/TokenType');\n\n // Same token type used for address creation throughout the SDK\n const UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n const tokenType = new TokenType(Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex'));\n\n // Convert hex pubkey to bytes\n const pubkeyBytes = new Uint8Array(\n pubkeyHex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n );\n\n // Create predicate reference with secp256k1 algorithm\n const addressRef = await UnmaskedPredicateReference.create(\n tokenType,\n 'secp256k1',\n pubkeyBytes,\n HashAlgorithm.SHA256\n );\n\n return addressRef.toAddress();\n }\n\n /**\n * Resolve recipient to IAddress for L3 transfers.\n * Uses pre-resolved PeerInfo when available to avoid redundant network queries.\n */\n private async resolveRecipientAddress(\n recipient: string,\n addressMode: 'auto' | 'direct' | 'proxy' = 'auto',\n peerInfo?: PeerInfo | null,\n ): Promise<IAddress> {\n const { AddressFactory } = await import('@unicitylabs/state-transition-sdk/lib/address/AddressFactory');\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n\n // PROXY: or DIRECT: prefixed — parse directly (explicit address overrides mode)\n if (recipient.startsWith('PROXY:') || recipient.startsWith('DIRECT:')) {\n return AddressFactory.createAddress(recipient);\n }\n\n // 66-char hex (33-byte compressed pubkey) — create DirectAddress\n if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {\n this.log(`Creating DirectAddress from 33-byte compressed pubkey`);\n return this.createDirectAddressFromPubkey(recipient);\n }\n\n // For nametag-based recipients, use PeerInfo (pre-resolved or resolve now)\n const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;\n if (!info) {\n throw new Error(\n `Recipient \"${recipient}\" not found. ` +\n `Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey.`\n );\n }\n\n // Determine nametag for PROXY address derivation\n const nametag = recipient.startsWith('@') ? recipient.slice(1)\n : info.nametag || recipient;\n\n // Force PROXY mode\n if (addressMode === 'proxy') {\n console.log(`[Payments] Using PROXY address for \"${nametag}\" (forced)`);\n return ProxyAddress.fromNameTag(nametag);\n }\n\n // Force DIRECT mode\n if (addressMode === 'direct') {\n if (!info.directAddress) {\n throw new Error(`\"${nametag}\" has no DirectAddress stored. It may be a legacy registration.`);\n }\n console.log(`[Payments] Using DirectAddress for \"${nametag}\" (forced): ${info.directAddress.slice(0, 30)}...`);\n return AddressFactory.createAddress(info.directAddress);\n }\n\n // AUTO mode: prefer directAddress, fallback to PROXY for legacy\n if (info.directAddress) {\n this.log(`Using DirectAddress for \"${nametag}\": ${info.directAddress.slice(0, 30)}...`);\n return AddressFactory.createAddress(info.directAddress);\n }\n\n this.log(`Using PROXY address for legacy nametag \"${nametag}\"`);\n return ProxyAddress.fromNameTag(nametag);\n }\n\n /**\n * Handle NOSTR-FIRST commitment-only transfer (recipient side)\n * This is called when receiving a transfer with only commitmentData and no proof yet.\n * We create the token as 'submitted', submit commitment (idempotent), and poll for proof.\n */\n private async handleCommitmentOnlyTransfer(\n transfer: IncomingTokenTransfer,\n payload: Record<string, unknown>\n ): Promise<void> {\n try {\n const sourceTokenInput = typeof payload.sourceToken === 'string'\n ? JSON.parse(payload.sourceToken as string)\n : payload.sourceToken;\n const commitmentInput = typeof payload.commitmentData === 'string'\n ? JSON.parse(payload.commitmentData as string)\n : payload.commitmentData;\n\n if (!sourceTokenInput || !commitmentInput) {\n console.warn('[Payments] Invalid NOSTR-FIRST transfer format');\n return;\n }\n\n // Parse source token info\n const tokenInfo = await parseTokenInfo(sourceTokenInput);\n\n // Create token with 'submitted' status (unconfirmed until proof received)\n const token: Token = {\n id: tokenInfo.tokenId ?? crypto.randomUUID(),\n coinId: tokenInfo.coinId,\n symbol: tokenInfo.symbol,\n name: tokenInfo.name,\n decimals: tokenInfo.decimals,\n iconUrl: tokenInfo.iconUrl,\n amount: tokenInfo.amount,\n status: 'submitted', // NOSTR-FIRST: unconfirmed until proof\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: typeof sourceTokenInput === 'string'\n ? sourceTokenInput\n : JSON.stringify(sourceTokenInput),\n };\n\n // Check tombstones - reject tokens with exact (tokenId, stateHash) match\n // This prevents spent tokens from being re-added via Nostr re-delivery\n // Tokens with the same tokenId but DIFFERENT stateHash are allowed (new state)\n const nostrTokenId = extractTokenIdFromSdkData(token.sdkData);\n const nostrStateHash = extractStateHashFromSdkData(token.sdkData);\n if (nostrTokenId && nostrStateHash && this.isStateTombstoned(nostrTokenId, nostrStateHash)) {\n this.log(`NOSTR-FIRST: Rejecting tombstoned token ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`);\n return;\n }\n\n // Add token as unconfirmed\n this.tokens.set(token.id, token);\n await this.save();\n this.log(`NOSTR-FIRST: Token ${token.id.slice(0, 8)}... added as submitted (unconfirmed)`);\n\n // Emit event for incoming transfer (even though unconfirmed)\n const incomingTransfer: IncomingTransfer = {\n id: transfer.id,\n senderPubkey: transfer.senderTransportPubkey,\n tokens: [token],\n memo: payload.memo as string | undefined,\n receivedAt: transfer.timestamp,\n };\n this.deps!.emitEvent('transfer:incoming', incomingTransfer);\n\n // Parse commitment and start proof polling\n try {\n const commitment = await TransferCommitment.fromJSON(commitmentInput);\n const requestIdBytes = commitment.requestId;\n const requestIdHex = requestIdBytes instanceof Uint8Array\n ? Array.from(requestIdBytes).map(b => b.toString(16).padStart(2, '0')).join('')\n : String(requestIdBytes);\n\n // Submit commitment to aggregator (idempotent - same as sender)\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (stClient) {\n const response = await stClient.submitTransferCommitment(commitment);\n this.log(`NOSTR-FIRST recipient commitment submit: ${response.status}`);\n }\n\n // Start polling for proof\n this.addProofPollingJob({\n tokenId: token.id,\n requestIdHex,\n commitmentJson: JSON.stringify(commitmentInput),\n startedAt: Date.now(),\n attemptCount: 0,\n lastAttemptAt: 0,\n onProofReceived: async (tokenId) => {\n // When proof arrives, finalize the token and update status\n await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, transfer.senderTransportPubkey);\n },\n });\n } catch (err) {\n console.error('[Payments] Failed to parse commitment for proof polling:', err);\n // Token remains as 'submitted' - will eventually time out\n }\n } catch (error) {\n console.error('[Payments] Failed to process NOSTR-FIRST transfer:', error);\n }\n }\n\n /**\n * Shared finalization logic for received transfers.\n * Handles both PROXY (with nametag token + address validation) and DIRECT schemes.\n */\n private async finalizeTransferToken(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n sourceToken: SdkToken<any>,\n transferTx: TransferTransaction,\n stClient: StateTransitionClient,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n trustBase: any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<SdkToken<any>> {\n const recipientAddress = transferTx.data.recipient;\n const addressScheme = recipientAddress.scheme;\n const signingService = await this.createSigningService();\n const transferSalt = transferTx.data.salt;\n\n const recipientPredicate = await UnmaskedPredicate.create(\n sourceToken.id,\n sourceToken.type,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const recipientState = new TokenState(recipientPredicate, null);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let nametagTokens: SdkToken<any>[] = [];\n\n if (addressScheme === AddressScheme.PROXY) {\n // PROXY: Validate nametag address match (per reference impl)\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n if (!this.nametag?.token) {\n throw new Error('Cannot finalize PROXY transfer - no nametag token');\n }\n const nametagToken = await SdkToken.fromJSON(this.nametag.token);\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address !== recipientAddress.address) {\n throw new Error(\n `PROXY address mismatch: nametag resolves to ${proxy.address} ` +\n `but transfer targets ${recipientAddress.address}`\n );\n }\n nametagTokens = [nametagToken];\n }\n // DIRECT: nametagTokens stays empty []\n\n return stClient.finalizeTransaction(\n trustBase,\n sourceToken,\n recipientState,\n transferTx,\n nametagTokens\n );\n }\n\n /**\n * Finalize a received token after proof is available\n */\n private async finalizeReceivedToken(\n tokenId: string,\n sourceTokenInput: unknown,\n commitmentInput: unknown,\n senderPubkey: string\n ): Promise<void> {\n try {\n const token = this.tokens.get(tokenId);\n if (!token) {\n this.log(`Token ${tokenId} not found for finalization`);\n return;\n }\n\n // Get proof from aggregator\n const commitment = await TransferCommitment.fromJSON(commitmentInput);\n if (!this.deps!.oracle.waitForProofSdk) {\n this.log('Cannot finalize - no waitForProofSdk');\n token.status = 'confirmed'; // Mark as confirmed anyway\n token.updatedAt = Date.now();\n await this.save();\n return;\n }\n\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transferTx = commitment.toTransaction(inclusionProof as any);\n\n // Parse source token\n const sourceToken = await SdkToken.fromJSON(sourceTokenInput);\n\n // Get state transition client\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n\n if (!stClient || !trustBase) {\n this.log('Cannot finalize - missing state transition client or trust base');\n token.status = 'confirmed';\n token.updatedAt = Date.now();\n await this.save();\n return;\n }\n\n // Finalize using shared helper (handles PROXY address validation)\n const finalizedSdkToken = await this.finalizeTransferToken(\n sourceToken, transferTx, stClient, trustBase\n );\n\n // Update token with finalized data (create new token with updated sdkData)\n const finalizedToken: Token = {\n ...token,\n status: 'confirmed',\n updatedAt: Date.now(),\n sdkData: JSON.stringify(finalizedSdkToken.toJSON()),\n };\n this.tokens.set(tokenId, finalizedToken);\n await this.save();\n\n // Also save as individual token file\n await this.saveTokenToFileStorage(finalizedToken);\n\n this.log(`NOSTR-FIRST: Token ${tokenId.slice(0, 8)}... finalized and confirmed`);\n\n // Emit confirmation event\n this.deps!.emitEvent('transfer:confirmed', {\n id: crypto.randomUUID(),\n status: 'completed',\n tokens: [finalizedToken],\n });\n\n // Add to history\n await this.addToHistory({\n type: 'RECEIVED',\n amount: finalizedToken.amount,\n coinId: finalizedToken.coinId,\n symbol: finalizedToken.symbol,\n timestamp: Date.now(),\n senderPubkey,\n });\n } catch (error) {\n console.error('[Payments] Failed to finalize received token:', error);\n // Mark as confirmed anyway (user has the token)\n const token = this.tokens.get(tokenId);\n if (token && token.status === 'submitted') {\n token.status = 'confirmed';\n token.updatedAt = Date.now();\n await this.save();\n }\n }\n }\n\n private async handleIncomingTransfer(transfer: IncomingTokenTransfer): Promise<void> {\n try {\n // Check payload format - Sphere wallet sends { sourceToken, transferTx }\n // SDK format is { token, proof }\n // INSTANT_SPLIT format is { type: 'INSTANT_SPLIT', version, ... }\n const payload = transfer.payload as unknown as Record<string, unknown>;\n\n // Check for INSTANT_SPLIT bundle first (V4 or V5)\n if (isInstantSplitBundle(payload)) {\n this.log('Processing INSTANT_SPLIT bundle...');\n try {\n // Ensure nametag is loaded before processing (needed for PROXY address verification)\n if (!this.nametag) {\n await this.loadNametagFromFileStorage();\n }\n\n const result = await this.processInstantSplitBundle(\n payload as InstantSplitBundle,\n transfer.senderTransportPubkey\n );\n if (result.success) {\n this.log('INSTANT_SPLIT processed successfully');\n } else {\n console.warn('[Payments] INSTANT_SPLIT processing failed:', result.error);\n }\n } catch (err) {\n console.error('[Payments] INSTANT_SPLIT processing error:', err);\n }\n return;\n }\n\n let tokenData: unknown;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let finalizedSdkToken: SdkToken<any> | null = null;\n\n if (payload.sourceToken && payload.transferTx) {\n // Sphere wallet format - needs finalization for PROXY addresses\n this.log('Processing Sphere wallet format transfer...');\n\n const sourceTokenInput = typeof payload.sourceToken === 'string'\n ? JSON.parse(payload.sourceToken as string)\n : payload.sourceToken;\n const transferTxInput = typeof payload.transferTx === 'string'\n ? JSON.parse(payload.transferTx as string)\n : payload.transferTx;\n\n if (!sourceTokenInput || !transferTxInput) {\n console.warn('[Payments] Invalid Sphere wallet transfer format');\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let sourceToken: SdkToken<any>;\n let transferTx: TransferTransaction;\n\n try {\n sourceToken = await SdkToken.fromJSON(sourceTokenInput);\n } catch (err) {\n console.error('[Payments] Failed to parse sourceToken:', err);\n return;\n }\n\n // Try multiple parsing strategies for transferTx\n // Format 1: TransferTransaction - has { data, inclusionProof }\n // Format 2: TransferCommitment - has { authenticator, requestId, transactionData }\n try {\n // Detect format based on structure\n const hasInclusionProof = transferTxInput.inclusionProof !== undefined;\n const hasData = transferTxInput.data !== undefined;\n const hasTransactionData = transferTxInput.transactionData !== undefined;\n const hasAuthenticator = transferTxInput.authenticator !== undefined;\n\n if (hasData && hasInclusionProof) {\n // Full transaction format - parse directly\n transferTx = await TransferTransaction.fromJSON(transferTxInput);\n } else if (hasTransactionData && hasAuthenticator) {\n // Commitment format - submit and wait for proof\n const commitment = await TransferCommitment.fromJSON(transferTxInput);\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n console.error('[Payments] Cannot process commitment - no state transition client');\n return;\n }\n\n const response = await stClient.submitTransferCommitment(commitment);\n if (response.status !== 'SUCCESS' && response.status !== 'REQUEST_ID_EXISTS') {\n console.error('[Payments] Transfer commitment submission failed:', response.status);\n return;\n }\n\n if (!this.deps!.oracle.waitForProofSdk) {\n console.error('[Payments] Cannot wait for proof - missing oracle method');\n return;\n }\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transferTx = commitment.toTransaction(inclusionProof as any);\n } else {\n // Unknown format - try parsing approaches\n try {\n transferTx = await TransferTransaction.fromJSON(transferTxInput);\n } catch {\n // Try commitment format as fallback\n const commitment = await TransferCommitment.fromJSON(transferTxInput);\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient || !this.deps!.oracle.waitForProofSdk) {\n throw new Error('Cannot submit commitment - missing oracle methods');\n }\n await stClient.submitTransferCommitment(commitment);\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transferTx = commitment.toTransaction(inclusionProof as any);\n }\n }\n } catch (err) {\n console.error('[Payments] Failed to parse transferTx:', err);\n return;\n }\n\n // Finalize using shared helper (handles PROXY address validation)\n try {\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!stClient || !trustBase) {\n console.error('[Payments] Cannot finalize - missing state transition client or trust base. Token rejected.');\n return;\n }\n finalizedSdkToken = await this.finalizeTransferToken(sourceToken, transferTx, stClient, trustBase);\n tokenData = finalizedSdkToken.toJSON();\n const addressScheme = transferTx.data.recipient.scheme;\n this.log(`${addressScheme === AddressScheme.PROXY ? 'PROXY' : 'DIRECT'} finalization successful`);\n } catch (finalizeError) {\n console.error(`[Payments] Finalization FAILED - token rejected:`, finalizeError);\n return;\n }\n } else if (payload.token) {\n // SDK format\n tokenData = payload.token;\n } else {\n console.warn('[Payments] Unknown transfer payload format');\n return;\n }\n\n // Validate token\n const validation = await this.deps!.oracle.validateToken(tokenData);\n if (!validation.valid) {\n console.warn('[Payments] Received invalid token');\n return;\n }\n\n // Parse token info from SDK data\n const tokenInfo = await parseTokenInfo(tokenData);\n\n // Create token entry\n const token: Token = {\n id: tokenInfo.tokenId ?? crypto.randomUUID(),\n coinId: tokenInfo.coinId,\n symbol: tokenInfo.symbol,\n name: tokenInfo.name,\n decimals: tokenInfo.decimals,\n iconUrl: tokenInfo.iconUrl,\n amount: tokenInfo.amount,\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: typeof tokenData === 'string'\n ? tokenData\n : JSON.stringify(tokenData),\n };\n\n // addToken() checks tombstones with exact (tokenId, stateHash) match\n // Tokens with same tokenId but different stateHash pass through (new state)\n await this.addToken(token);\n\n const incomingTransfer: IncomingTransfer = {\n id: transfer.id,\n senderPubkey: transfer.senderTransportPubkey,\n tokens: [token],\n memo: payload.memo as string | undefined,\n receivedAt: transfer.timestamp,\n };\n\n this.deps!.emitEvent('transfer:incoming', incomingTransfer);\n this.log(`Incoming transfer processed: ${token.id}, ${token.amount} ${token.symbol}`);\n } catch (error) {\n console.error('[Payments] Failed to process incoming transfer:', error);\n }\n }\n\n // ===========================================================================\n // Private: Archive\n // ===========================================================================\n\n private async archiveToken(token: Token): Promise<void> {\n const txf = tokenToTxf(token);\n if (!txf) return;\n\n const tokenId = txf.genesis?.data?.tokenId;\n if (!tokenId) return;\n\n const existingArchive = this.archivedTokens.get(tokenId);\n\n if (existingArchive) {\n if (isIncrementalUpdate(existingArchive, txf)) {\n this.archivedTokens.set(tokenId, txf);\n this.log(`Updated archived token ${tokenId.slice(0, 8)}...`);\n } else {\n // Fork\n const stateHash = getCurrentStateHash(txf) || '';\n await this.storeForkedToken(tokenId, stateHash, txf);\n this.log(`Archived token ${tokenId.slice(0, 8)}... is a fork`);\n }\n } else {\n this.archivedTokens.set(tokenId, txf);\n this.log(`Archived token ${tokenId.slice(0, 8)}...`);\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async save(): Promise<void> {\n // Save to TokenStorageProviders (IndexedDB/files)\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) {\n this.log('No token storage providers - tokens not persisted');\n return;\n }\n\n const data = await this.createStorageData();\n for (const [id, provider] of providers) {\n try {\n await provider.save(data);\n } catch (err) {\n console.error(`[Payments] Failed to save to provider ${id}:`, err);\n }\n }\n }\n\n private async saveToOutbox(transfer: TransferResult, recipient: string): Promise<void> {\n const outbox = await this.loadOutbox();\n outbox.push({ transfer, recipient, createdAt: Date.now() });\n await this.deps!.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(outbox));\n }\n\n private async removeFromOutbox(transferId: string): Promise<void> {\n const outbox = await this.loadOutbox();\n const filtered = outbox.filter((e) => e.transfer.id !== transferId);\n await this.deps!.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(filtered));\n }\n\n private async loadOutbox(): Promise<Array<{ transfer: TransferResult; recipient: string; createdAt: number }>> {\n const data = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.OUTBOX);\n return data ? JSON.parse(data) : [];\n }\n\n private async createStorageData(): Promise<TxfStorageDataBase> {\n // Active tokens are NOT stored in TXF format - they are saved as individual\n // token-xxx files via saveTokenToFileStorage() to avoid duplication.\n // TXF storage is only used for metadata: archived, tombstones, forked, outbox.\n // Note: nametag is also saved separately via saveNametagToFileStorage()\n // as nametag-{name}.json to avoid duplication in storage\n return await buildTxfStorageData(\n [], // Empty - active tokens stored as token-xxx files\n {\n version: 1,\n address: this.deps!.identity.l1Address,\n ipnsName: this.deps!.identity.ipnsName ?? '',\n },\n {\n tombstones: this.tombstones,\n archivedTokens: this.archivedTokens,\n forkedTokens: this.forkedTokens,\n }\n ) as unknown as TxfStorageDataBase;\n }\n\n private loadFromStorageData(data: TxfStorageDataBase): void {\n const parsed = parseTxfStorageData(data);\n\n // Load tombstones FIRST so we can filter tokens\n this.tombstones = parsed.tombstones;\n // Load tokens, filtering out tombstoned ones\n // NOTE: Only filter by exact (tokenId, stateHash) match to avoid over-blocking\n // When state hash is unavailable, we can't reliably distinguish old from new\n this.tokens.clear();\n for (const token of parsed.tokens) {\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n\n // Only filter if we have exact state match\n if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {\n this.log(`Skipping tombstoned token ${sdkTokenId.slice(0, 8)}... during load (exact state match)`);\n continue;\n }\n\n this.tokens.set(token.id, token);\n }\n\n // Load other data\n this.archivedTokens = parsed.archivedTokens;\n this.forkedTokens = parsed.forkedTokens;\n // Only overwrite nametag if TXF data explicitly includes one.\n // Nametag is stored separately as nametag-{name} files (not in TXF),\n // so parsed.nametag is normally null and must not erase the existing value.\n if (parsed.nametag !== null) {\n this.nametag = parsed.nametag;\n }\n }\n\n // ===========================================================================\n // Private: NOSTR-FIRST Proof Polling\n // ===========================================================================\n\n /**\n * Submit commitment to aggregator and start background proof polling\n * (NOSTR-FIRST pattern: fire-and-forget submission)\n */\n private async submitAndPollForProof(\n tokenId: string,\n commitment: TransferCommitment,\n requestIdHex: string,\n onProofReceived?: (tokenId: string) => void\n ): Promise<void> {\n try {\n // Submit to aggregator\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n this.log('Cannot submit commitment - no state transition client');\n return;\n }\n\n const response = await stClient.submitTransferCommitment(commitment);\n if (response.status !== 'SUCCESS' && response.status !== 'REQUEST_ID_EXISTS') {\n this.log(`Transfer commitment submission failed: ${response.status}`);\n // Mark token as invalid since submission failed\n const token = this.tokens.get(tokenId);\n if (token) {\n token.status = 'invalid';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n await this.save();\n }\n return;\n }\n\n // Add to polling queue\n this.addProofPollingJob({\n tokenId,\n requestIdHex,\n commitmentJson: JSON.stringify(commitment.toJSON()),\n startedAt: Date.now(),\n attemptCount: 0,\n lastAttemptAt: 0,\n onProofReceived,\n });\n } catch (error) {\n this.log('submitAndPollForProof error:', error);\n }\n }\n\n /**\n * Add a proof polling job to the queue\n */\n private addProofPollingJob(job: ProofPollingJob): void {\n this.proofPollingJobs.set(job.tokenId, job);\n this.log(`Added proof polling job for token ${job.tokenId.slice(0, 8)}...`);\n this.startProofPolling();\n }\n\n /**\n * Start the proof polling interval if not already running\n */\n private startProofPolling(): void {\n if (this.proofPollingInterval) return;\n if (this.proofPollingJobs.size === 0) return;\n\n this.log('Starting proof polling...');\n this.proofPollingInterval = setInterval(\n () => this.processProofPollingQueue(),\n PaymentsModule.PROOF_POLLING_INTERVAL_MS\n );\n }\n\n /**\n * Stop the proof polling interval\n */\n private stopProofPolling(): void {\n if (this.proofPollingInterval) {\n clearInterval(this.proofPollingInterval);\n this.proofPollingInterval = null;\n this.log('Stopped proof polling');\n }\n }\n\n /**\n * Process all pending proof polling jobs\n */\n private async processProofPollingQueue(): Promise<void> {\n if (this.proofPollingJobs.size === 0) {\n this.stopProofPolling();\n return;\n }\n\n const completedJobs: string[] = [];\n\n for (const [tokenId, job] of this.proofPollingJobs) {\n try {\n job.attemptCount++;\n job.lastAttemptAt = Date.now();\n\n // Check for timeout\n if (job.attemptCount >= PaymentsModule.PROOF_POLLING_MAX_ATTEMPTS) {\n this.log(`Proof polling timeout for token ${tokenId.slice(0, 8)}...`);\n // Mark token as invalid due to timeout\n const token = this.tokens.get(tokenId);\n if (token && token.status === 'submitted') {\n token.status = 'invalid';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n }\n completedJobs.push(tokenId);\n continue;\n }\n\n // Try to get proof from aggregator using a short timeout\n const commitment = await TransferCommitment.fromJSON(JSON.parse(job.commitmentJson));\n\n // Try to get proof with a quick timeout (non-blocking check)\n let inclusionProof: unknown = null;\n try {\n // Create abort controller for quick timeout\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => abortController.abort(), 500);\n\n if (this.deps!.oracle.waitForProofSdk) {\n inclusionProof = await Promise.race([\n this.deps!.oracle.waitForProofSdk(commitment, abortController.signal),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), 500)),\n ]);\n } else {\n // Fallback: use getProof with request ID hex\n const proof = await this.deps!.oracle.getProof(job.requestIdHex);\n if (proof) {\n inclusionProof = proof;\n }\n }\n\n clearTimeout(timeoutId);\n } catch (err) {\n // Proof not ready yet or timed out\n continue;\n }\n\n if (!inclusionProof) {\n // Proof not ready yet\n continue;\n }\n\n // Proof received! Update token status\n const token = this.tokens.get(tokenId);\n if (token) {\n token.status = 'spent';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n await this.save();\n this.log(`Proof received for token ${tokenId.slice(0, 8)}..., status: spent`);\n }\n\n // Call callback if provided\n job.onProofReceived?.(tokenId);\n completedJobs.push(tokenId);\n } catch (error) {\n // Most errors mean proof is not ready yet, continue polling\n this.log(`Proof polling attempt ${job.attemptCount} for ${tokenId.slice(0, 8)}...: ${error}`);\n }\n }\n\n // Remove completed jobs\n for (const tokenId of completedJobs) {\n this.proofPollingJobs.delete(tokenId);\n }\n\n // Stop polling if no more jobs\n if (this.proofPollingJobs.size === 0) {\n this.stopProofPolling();\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureInitialized(): void {\n if (!this.deps) {\n throw new Error('PaymentsModule not initialized');\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createPaymentsModule(config?: PaymentsModuleConfig): PaymentsModule {\n return new PaymentsModule(config);\n}\n","/**\n * TokenRecoveryService\n *\n * Recovers tokens from failed or incomplete instant split operations.\n *\n * Recovery Scenarios:\n * 1. Orphaned splits: Burn completed but mints never submitted\n * 2. Lost change tokens: Mints completed but change token never saved\n * 3. Sent tokens: Recover tokens from sent Nostr events\n *\n * This service works with the storage provider to persist recovered tokens.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport type {\n InstantSplitBundleV5,\n InstantSplitV5RecoveryMetadata,\n SplitRecoveryResult,\n} from '../../types/instant-split';\nimport type { TransportProvider } from '../../transport';\nimport type { StorageProvider } from '../../storage';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TokenRecoveryServiceConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n /** Dev mode skips trust base verification */\n devMode?: boolean;\n}\n\nexport interface RecoveryDependencies {\n stClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n devMode?: boolean;\n}\n\n/**\n * An outbox entry with V5 recovery metadata\n */\nexport interface V5OutboxEntry {\n id: string;\n splitGroupId: string;\n status: string;\n metadata?: InstantSplitV5RecoveryMetadata;\n bundleJson?: string;\n}\n\n/**\n * Options for recovering sent tokens\n */\nexport interface RecoverSentOptions {\n /** Unix timestamp to start scanning from (default: 30 days ago) */\n since?: number;\n /** Maximum number of events to scan (default: 100) */\n limit?: number;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenRecoveryService {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private signingService: SigningService;\n private devMode: boolean;\n\n constructor(config: TokenRecoveryServiceConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Recover change tokens from orphaned V5 splits.\n *\n * This scans outbox entries to find splits where:\n * - Nostr delivery succeeded\n * - But change token was never saved (browser crash, etc.)\n *\n * @param outboxEntries - Array of V5 outbox entries to check\n * @param onTokenRecovered - Callback when a token is recovered\n * @returns Recovery result\n */\n async recoverOrphanedSplits(\n outboxEntries: V5OutboxEntry[],\n onTokenRecovered?: (token: Token<any>, splitGroupId: string) => Promise<void>\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n for (const entry of outboxEntries) {\n // Only process entries that were sent but not completed\n if (entry.status !== 'NOSTR_SENT' && entry.status !== 'SENT') {\n continue;\n }\n\n const metadata = entry.metadata;\n if (!metadata || metadata.version !== '5.0') {\n continue;\n }\n\n try {\n console.log(`[Recovery] Processing orphaned split ${entry.splitGroupId}`);\n\n // Reconstruct the sender's mint commitment from metadata\n const senderTokenId = new TokenId(fromHex(metadata.senderTokenIdHex));\n const senderSalt = fromHex(metadata.senderSaltHex);\n\n // Try to get the mint proof from the aggregator\n // This will succeed if the background submission completed before crash\n const changeToken = await this.tryRecoverChangeToken(\n metadata.seedString,\n senderTokenId,\n senderSalt,\n metadata.changeAmount,\n entry.bundleJson\n );\n\n if (changeToken) {\n await onTokenRecovered?.(changeToken, entry.splitGroupId);\n result.changeTokensRecovered++;\n result.splitsRecovered++;\n console.log(`[Recovery] Recovered change token for split ${entry.splitGroupId}`);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push({\n splitGroupId: entry.splitGroupId,\n error: errorMessage,\n timestamp: Date.now(),\n });\n console.error(`[Recovery] Failed to recover split ${entry.splitGroupId}:`, error);\n }\n }\n\n result.durationMs = performance.now() - startTime;\n console.log(`[Recovery] Completed in ${result.durationMs.toFixed(0)}ms: ${result.changeTokensRecovered} tokens recovered`);\n\n return result;\n }\n\n /**\n * Try to recover a change token by checking if the mint proof exists.\n *\n * @param seedString - Original seed string used for split\n * @param senderTokenId - Token ID for the change token\n * @param senderSalt - Salt for the change token\n * @param changeAmount - Amount of the change token\n * @param bundleJson - Optional bundle JSON for additional context\n * @returns Recovered token or null\n */\n private async tryRecoverChangeToken(\n seedString: string,\n senderTokenId: TokenId,\n senderSalt: Uint8Array,\n changeAmount: string,\n bundleJson?: string\n ): Promise<Token<any> | null> {\n try {\n // Parse bundle for token type if available\n let tokenType: TokenType | undefined;\n let coinId: CoinId | undefined;\n\n if (bundleJson) {\n const bundle = JSON.parse(bundleJson) as InstantSplitBundleV5;\n tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n coinId = new CoinId(fromHex(bundle.coinId));\n }\n\n if (!tokenType) {\n console.log('[Recovery] Cannot recover: no token type available');\n return null;\n }\n\n // Create the predicate for the change token\n const predicate = await UnmaskedPredicate.create(\n senderTokenId,\n tokenType,\n this.signingService,\n HashAlgorithm.SHA256,\n senderSalt\n );\n const state = new TokenState(predicate, null);\n\n // Try to get the proof from the aggregator\n // The mint was submitted in background, so it might exist\n // We need to recreate the MintTransactionData to create the commitment\n // For V5 recovery, we'd need the full mint data which is in the background context\n\n // This is a simplified recovery - in production, you'd need to store\n // the full MintCommitment JSON in the outbox for complete recovery\n console.log('[Recovery] Would attempt to recover change token - mint proof lookup not implemented');\n return null;\n } catch (error) {\n console.warn('[Recovery] Failed to recover change token:', error);\n return null;\n }\n }\n\n /**\n * Recover tokens from sent Nostr events.\n *\n * This scans outgoing Nostr events to reconstruct tokens that were sent\n * but may not be properly reflected in local storage.\n *\n * @param transport - Transport provider to query events\n * @param options - Recovery options\n * @returns Recovery result\n */\n async recoverSentTokens(\n transport: TransportProvider,\n options?: RecoverSentOptions\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n // Note: Full implementation would query Nostr for sent events\n // and cross-reference with local storage to find missing tokens\n console.log('[Recovery] Sent token recovery not fully implemented');\n\n result.durationMs = performance.now() - startTime;\n return result;\n }\n\n /**\n * Recover from a split where the burn completed but mints failed.\n *\n * This is a critical recovery scenario - the original token is burned,\n * but the new tokens were never created. We attempt to recreate them.\n *\n * @param splitGroupId - The split group ID\n * @param burnRequestIdHex - The burn transaction request ID\n * @param seedString - The seed string used for split calculations\n * @param tokenType - The token type\n * @param coinId - The coin ID\n * @param splitAmount - Amount for recipient\n * @param changeAmount - Amount for sender\n * @returns Recovery result\n */\n async recoverSplitBurnFailure(\n splitGroupId: string,\n burnRequestIdHex: string,\n seedString: string,\n tokenType: TokenType,\n coinId: CoinId,\n splitAmount: bigint,\n changeAmount: bigint,\n onTokenRecovered?: (token: Token<any>, isChange: boolean) => Promise<void>\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n try {\n console.log(`[Recovery] Attempting burn failure recovery for ${splitGroupId}`);\n\n // Regenerate the token IDs and salts\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Note: Full recovery would require:\n // 1. Querying the aggregator for the burn transaction\n // 2. Recreating the mint commitments with SplitMintReason\n // 3. Submitting the mints if not already submitted\n // 4. Waiting for proofs and creating the tokens\n\n // This is a complex operation that depends on the specific failure mode\n console.log('[Recovery] Burn failure recovery not fully implemented');\n console.log(`[Recovery] Would recover: ${toHex(senderTokenId.bytes).slice(0, 16)}... (change)`);\n\n result.errors.push({\n splitGroupId,\n error: 'Burn failure recovery not fully implemented',\n timestamp: Date.now(),\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push({\n splitGroupId,\n error: errorMessage,\n timestamp: Date.now(),\n });\n }\n\n result.durationMs = performance.now() - startTime;\n return result;\n }\n\n /**\n * Verify a token still exists and is valid on the aggregator.\n *\n * @param token - The token to verify\n * @returns true if token is valid, false otherwise\n */\n async verifyTokenExists(token: Token<any>): Promise<boolean> {\n try {\n if (this.devMode) {\n return true;\n }\n\n const verification = await token.verify(this.trustBase);\n return verification.isSuccessful;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Factory function for creating TokenRecoveryService\n */\nexport function createTokenRecoveryService(config: TokenRecoveryServiceConfig): TokenRecoveryService {\n return new TokenRecoveryService(config);\n}\n","/**\n * Communications Module\n * Platform-independent messaging operations\n */\n\nimport type {\n DirectMessage,\n BroadcastMessage,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n} from '../../types';\nimport type { StorageProvider } from '../../storage';\nimport type { TransportProvider, IncomingMessage, IncomingBroadcast } from '../../transport';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface CommunicationsModuleConfig {\n /** Auto-save messages */\n autoSave?: boolean;\n /** Max messages in memory */\n maxMessages?: number;\n /** Enable read receipts */\n readReceipts?: boolean;\n}\n\n// =============================================================================\n// Dependencies Interface\n// =============================================================================\n\nexport interface CommunicationsModuleDependencies {\n identity: FullIdentity;\n storage: StorageProvider;\n transport: TransportProvider;\n emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class CommunicationsModule {\n private config: Required<CommunicationsModuleConfig>;\n private deps: CommunicationsModuleDependencies | null = null;\n\n // State\n private messages: Map<string, DirectMessage> = new Map();\n private broadcasts: Map<string, BroadcastMessage> = new Map();\n\n // Subscriptions\n private unsubscribeMessages: (() => void) | null = null;\n private broadcastSubscriptions: Map<string, () => void> = new Map();\n\n // Handlers\n private dmHandlers: Set<(message: DirectMessage) => void> = new Set();\n private broadcastHandlers: Set<(message: BroadcastMessage) => void> = new Set();\n\n constructor(config?: CommunicationsModuleConfig) {\n this.config = {\n autoSave: config?.autoSave ?? true,\n maxMessages: config?.maxMessages ?? 1000,\n readReceipts: config?.readReceipts ?? true,\n };\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Initialize module with dependencies\n */\n initialize(deps: CommunicationsModuleDependencies): void {\n this.deps = deps;\n\n // Subscribe to incoming messages\n this.unsubscribeMessages = deps.transport.onMessage((msg) => {\n this.handleIncomingMessage(msg);\n });\n }\n\n /**\n * Load messages from storage\n */\n async load(): Promise<void> {\n this.ensureInitialized();\n\n const data = await this.deps!.storage.get('direct_messages');\n if (data) {\n const messages = JSON.parse(data) as DirectMessage[];\n this.messages.clear();\n for (const msg of messages) {\n this.messages.set(msg.id, msg);\n }\n }\n }\n\n /**\n * Cleanup resources\n */\n destroy(): void {\n this.unsubscribeMessages?.();\n this.unsubscribeMessages = null;\n\n for (const unsub of this.broadcastSubscriptions.values()) {\n unsub();\n }\n this.broadcastSubscriptions.clear();\n }\n\n // ===========================================================================\n // Public API - Direct Messages\n // ===========================================================================\n\n /**\n * Send direct message\n */\n async sendDM(recipient: string, content: string): Promise<DirectMessage> {\n this.ensureInitialized();\n\n // Resolve recipient\n const recipientPubkey = await this.resolveRecipient(recipient);\n\n // Send via transport\n const eventId = await this.deps!.transport.sendMessage(recipientPubkey, content);\n\n // Create message record\n const message: DirectMessage = {\n id: eventId,\n senderPubkey: this.deps!.identity.chainPubkey,\n senderNametag: this.deps!.identity.nametag,\n recipientPubkey,\n content,\n timestamp: Date.now(),\n isRead: true,\n };\n\n // Save\n this.messages.set(message.id, message);\n if (this.config.autoSave) {\n await this.save();\n }\n\n return message;\n }\n\n /**\n * Get conversation with peer\n */\n getConversation(peerPubkey: string): DirectMessage[] {\n return Array.from(this.messages.values())\n .filter(\n (m) => m.senderPubkey === peerPubkey || m.recipientPubkey === peerPubkey\n )\n .sort((a, b) => a.timestamp - b.timestamp);\n }\n\n /**\n * Get all conversations grouped by peer\n */\n getConversations(): Map<string, DirectMessage[]> {\n const conversations = new Map<string, DirectMessage[]>();\n\n for (const message of this.messages.values()) {\n const peer =\n message.senderPubkey === this.deps?.identity.chainPubkey\n ? message.recipientPubkey\n : message.senderPubkey;\n\n if (!conversations.has(peer)) {\n conversations.set(peer, []);\n }\n conversations.get(peer)!.push(message);\n }\n\n // Sort each conversation\n for (const msgs of conversations.values()) {\n msgs.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n return conversations;\n }\n\n /**\n * Mark messages as read\n */\n async markAsRead(messageIds: string[]): Promise<void> {\n for (const id of messageIds) {\n const msg = this.messages.get(id);\n if (msg) {\n msg.isRead = true;\n }\n }\n\n if (this.config.autoSave) {\n await this.save();\n }\n }\n\n /**\n * Get unread count\n */\n getUnreadCount(peerPubkey?: string): number {\n let messages = Array.from(this.messages.values()).filter(\n (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.chainPubkey\n );\n\n if (peerPubkey) {\n messages = messages.filter((m) => m.senderPubkey === peerPubkey);\n }\n\n return messages.length;\n }\n\n /**\n * Subscribe to incoming DMs\n */\n onDirectMessage(handler: (message: DirectMessage) => void): () => void {\n this.dmHandlers.add(handler);\n return () => this.dmHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Public API - Broadcasts\n // ===========================================================================\n\n /**\n * Publish broadcast message\n */\n async broadcast(content: string, tags?: string[]): Promise<BroadcastMessage> {\n this.ensureInitialized();\n\n const eventId = await this.deps!.transport.publishBroadcast?.(content, tags);\n\n const message: BroadcastMessage = {\n id: eventId ?? crypto.randomUUID(),\n authorPubkey: this.deps!.identity.chainPubkey,\n authorNametag: this.deps!.identity.nametag,\n content,\n timestamp: Date.now(),\n tags,\n };\n\n this.broadcasts.set(message.id, message);\n return message;\n }\n\n /**\n * Subscribe to broadcasts with tags\n */\n subscribeToBroadcasts(tags: string[]): () => void {\n this.ensureInitialized();\n\n const key = tags.sort().join(':');\n if (this.broadcastSubscriptions.has(key)) {\n return () => {};\n }\n\n const unsub = this.deps!.transport.subscribeToBroadcast?.(tags, (broadcast) => {\n this.handleIncomingBroadcast(broadcast);\n });\n\n if (unsub) {\n this.broadcastSubscriptions.set(key, unsub);\n }\n\n return () => {\n const sub = this.broadcastSubscriptions.get(key);\n if (sub) {\n sub();\n this.broadcastSubscriptions.delete(key);\n }\n };\n }\n\n /**\n * Get broadcasts\n */\n getBroadcasts(limit?: number): BroadcastMessage[] {\n const messages = Array.from(this.broadcasts.values())\n .sort((a, b) => b.timestamp - a.timestamp);\n\n return limit ? messages.slice(0, limit) : messages;\n }\n\n /**\n * Subscribe to incoming broadcasts\n */\n onBroadcast(handler: (message: BroadcastMessage) => void): () => void {\n this.broadcastHandlers.add(handler);\n return () => this.broadcastHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Private: Message Handling\n // ===========================================================================\n\n private handleIncomingMessage(msg: IncomingMessage): void {\n // Skip own messages\n if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;\n\n const message: DirectMessage = {\n id: msg.id,\n senderPubkey: msg.senderTransportPubkey,\n senderNametag: msg.senderNametag,\n recipientPubkey: this.deps!.identity.chainPubkey,\n content: msg.content,\n timestamp: msg.timestamp,\n isRead: false,\n };\n\n this.messages.set(message.id, message);\n\n // Emit event\n this.deps!.emitEvent('message:dm', message);\n\n // Notify handlers\n for (const handler of this.dmHandlers) {\n try {\n handler(message);\n } catch (error) {\n console.error('[Communications] Handler error:', error);\n }\n }\n\n // Auto-save\n if (this.config.autoSave) {\n this.save();\n }\n\n // Prune if needed\n this.pruneIfNeeded();\n }\n\n private handleIncomingBroadcast(incoming: IncomingBroadcast): void {\n const message: BroadcastMessage = {\n id: incoming.id,\n authorPubkey: incoming.authorTransportPubkey,\n content: incoming.content,\n timestamp: incoming.timestamp,\n tags: incoming.tags,\n };\n\n this.broadcasts.set(message.id, message);\n\n // Emit event\n this.deps!.emitEvent('message:broadcast', message);\n\n // Notify handlers\n for (const handler of this.broadcastHandlers) {\n try {\n handler(message);\n } catch (error) {\n console.error('[Communications] Handler error:', error);\n }\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async save(): Promise<void> {\n const messages = Array.from(this.messages.values());\n await this.deps!.storage.set('direct_messages', JSON.stringify(messages));\n }\n\n private pruneIfNeeded(): void {\n if (this.messages.size <= this.config.maxMessages) return;\n\n const sorted = Array.from(this.messages.entries())\n .sort(([, a], [, b]) => a.timestamp - b.timestamp);\n\n const toRemove = sorted.slice(0, sorted.length - this.config.maxMessages);\n for (const [id] of toRemove) {\n this.messages.delete(id);\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private async resolveRecipient(recipient: string): Promise<string> {\n if (recipient.startsWith('@')) {\n const pubkey = await this.deps!.transport.resolveNametag?.(recipient.slice(1));\n if (!pubkey) {\n throw new Error(`Nametag not found: ${recipient}`);\n }\n return pubkey;\n }\n return recipient;\n }\n\n private ensureInitialized(): void {\n if (!this.deps) {\n throw new Error('CommunicationsModule not initialized');\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createCommunicationsModule(\n config?: CommunicationsModuleConfig\n): CommunicationsModule {\n return new CommunicationsModule(config);\n}\n","/**\n * Encryption utilities for SDK2\n *\n * Provides AES-256 encryption for sensitive wallet data.\n * Uses crypto-js for cross-platform compatibility.\n */\n\nimport CryptoJS from 'crypto-js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EncryptedData {\n /** Encrypted ciphertext (base64) */\n ciphertext: string;\n /** Initialization vector (hex) */\n iv: string;\n /** Salt used for key derivation (hex) */\n salt: string;\n /** Algorithm identifier */\n algorithm: 'aes-256-cbc';\n /** Key derivation function */\n kdf: 'pbkdf2';\n /** Number of PBKDF2 iterations */\n iterations: number;\n}\n\nexport interface EncryptionOptions {\n /** Number of PBKDF2 iterations (default: 100000) */\n iterations?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default number of PBKDF2 iterations */\nconst DEFAULT_ITERATIONS = 100000;\n\n/** AES key size in bits */\nconst KEY_SIZE = 256;\n\n/** Salt size in bytes */\nconst SALT_SIZE = 16;\n\n/** IV size in bytes */\nconst IV_SIZE = 16;\n\n// =============================================================================\n// Key Derivation\n// =============================================================================\n\n/**\n * Derive encryption key from password using PBKDF2\n * @param password - User password\n * @param salt - Salt as WordArray\n * @param iterations - Number of iterations\n */\nfunction deriveKey(\n password: string,\n salt: CryptoJS.lib.WordArray,\n iterations: number\n): CryptoJS.lib.WordArray {\n return CryptoJS.PBKDF2(password, salt, {\n keySize: KEY_SIZE / 32, // WordArray uses 32-bit words\n iterations,\n hasher: CryptoJS.algo.SHA256,\n });\n}\n\n// =============================================================================\n// Encryption Functions\n// =============================================================================\n\n/**\n * Encrypt data with AES-256-CBC\n * @param plaintext - Data to encrypt (string or object)\n * @param password - Encryption password\n * @param options - Encryption options\n */\nexport function encrypt(\n plaintext: string | object,\n password: string,\n options: EncryptionOptions = {}\n): EncryptedData {\n const iterations = options.iterations ?? DEFAULT_ITERATIONS;\n\n // Convert object to JSON string if needed\n const data = typeof plaintext === 'string' ? plaintext : JSON.stringify(plaintext);\n\n // Generate random salt and IV\n const salt = CryptoJS.lib.WordArray.random(SALT_SIZE);\n const iv = CryptoJS.lib.WordArray.random(IV_SIZE);\n\n // Derive key from password\n const key = deriveKey(password, salt, iterations);\n\n // Encrypt with AES-256-CBC\n const encrypted = CryptoJS.AES.encrypt(data, key, {\n iv,\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n });\n\n return {\n ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64),\n iv: iv.toString(CryptoJS.enc.Hex),\n salt: salt.toString(CryptoJS.enc.Hex),\n algorithm: 'aes-256-cbc',\n kdf: 'pbkdf2',\n iterations,\n };\n}\n\n/**\n * Decrypt AES-256-CBC encrypted data\n * @param encryptedData - Encrypted data object\n * @param password - Decryption password\n */\nexport function decrypt(encryptedData: EncryptedData, password: string): string {\n // Parse salt and IV\n const salt = CryptoJS.enc.Hex.parse(encryptedData.salt);\n const iv = CryptoJS.enc.Hex.parse(encryptedData.iv);\n\n // Derive key from password\n const key = deriveKey(password, salt, encryptedData.iterations);\n\n // Parse ciphertext\n const ciphertext = CryptoJS.enc.Base64.parse(encryptedData.ciphertext);\n\n // Create cipher params\n const cipherParams = CryptoJS.lib.CipherParams.create({\n ciphertext,\n });\n\n // Decrypt\n const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {\n iv,\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n });\n\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n\n if (!result) {\n throw new Error('Decryption failed: invalid password or corrupted data');\n }\n\n return result;\n}\n\n/**\n * Decrypt and parse JSON data\n * @param encryptedData - Encrypted data object\n * @param password - Decryption password\n */\nexport function decryptJson<T = unknown>(encryptedData: EncryptedData, password: string): T {\n const decrypted = decrypt(encryptedData, password);\n try {\n return JSON.parse(decrypted) as T;\n } catch {\n throw new Error('Decryption failed: invalid JSON data');\n }\n}\n\n// =============================================================================\n// Simple Encryption (Password-based, for localStorage)\n// =============================================================================\n\n/**\n * Simple encryption using CryptoJS built-in password-based encryption\n * Suitable for localStorage where we don't need full EncryptedData metadata\n * @param plaintext - Data to encrypt\n * @param password - Encryption password\n */\nexport function encryptSimple(plaintext: string, password: string): string {\n return CryptoJS.AES.encrypt(plaintext, password).toString();\n}\n\n/**\n * Simple decryption\n * @param ciphertext - Encrypted string\n * @param password - Decryption password\n */\nexport function decryptSimple(ciphertext: string, password: string): string {\n const decrypted = CryptoJS.AES.decrypt(ciphertext, password);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n\n if (!result) {\n throw new Error('Decryption failed: invalid password or corrupted data');\n }\n\n return result;\n}\n\n/**\n * Decrypt data encrypted with PBKDF2-derived key (legacy JSON wallet format).\n * Compatible with webwallet's encryptWithPassword/decryptWithPassword.\n */\nexport function decryptWithSalt(ciphertext: string, password: string, salt: string): string | null {\n try {\n const key = CryptoJS.PBKDF2(password, salt, {\n keySize: 256 / 32,\n iterations: 100000,\n hasher: CryptoJS.algo.SHA256,\n }).toString();\n const decrypted = CryptoJS.AES.decrypt(ciphertext, key);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n return result || null;\n } catch {\n return null;\n }\n}\n\n// =============================================================================\n// Mnemonic Encryption (Compatible with existing wallet format)\n// =============================================================================\n\n/**\n * Encrypt mnemonic phrase for storage\n * Uses simple AES encryption compatible with existing wallet format\n * @param mnemonic - BIP39 mnemonic phrase\n * @param password - Encryption password\n */\nexport function encryptMnemonic(mnemonic: string, password: string): string {\n return encryptSimple(mnemonic, password);\n}\n\n/**\n * Decrypt mnemonic phrase from storage\n * @param encryptedMnemonic - Encrypted mnemonic string\n * @param password - Decryption password\n */\nexport function decryptMnemonic(encryptedMnemonic: string, password: string): string {\n return decryptSimple(encryptedMnemonic, password);\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Check if data looks like an EncryptedData object\n */\nexport function isEncryptedData(data: unknown): data is EncryptedData {\n if (typeof data !== 'object' || data === null) {\n return false;\n }\n const obj = data as Record<string, unknown>;\n return (\n typeof obj.ciphertext === 'string' &&\n typeof obj.iv === 'string' &&\n typeof obj.salt === 'string' &&\n obj.algorithm === 'aes-256-cbc' &&\n obj.kdf === 'pbkdf2' &&\n typeof obj.iterations === 'number'\n );\n}\n\n/**\n * Serialize EncryptedData to string for storage\n */\nexport function serializeEncrypted(data: EncryptedData): string {\n return JSON.stringify(data);\n}\n\n/**\n * Deserialize EncryptedData from string\n */\nexport function deserializeEncrypted(serialized: string): EncryptedData {\n const parsed = JSON.parse(serialized);\n if (!isEncryptedData(parsed)) {\n throw new Error('Invalid encrypted data format');\n }\n return parsed;\n}\n\n/**\n * Generate a random password/key as hex string\n * @param bytes - Number of random bytes (default: 32)\n */\nexport function generateRandomKey(bytes: number = 32): string {\n return CryptoJS.lib.WordArray.random(bytes).toString(CryptoJS.enc.Hex);\n}\n","/**\n * Address Scanning — Derive HD addresses and check L1 balances via Fulcrum.\n *\n * Used after importing BIP32/.dat wallets to discover which addresses have funds.\n */\n\nimport type { AddressInfo } from './crypto';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Progress callback for address scanning */\nexport interface ScanAddressProgress {\n /** Number of addresses scanned so far */\n scanned: number;\n /** Total addresses to scan (maxAddresses * chains) */\n total: number;\n /** Current address being checked */\n currentAddress: string;\n /** Number of addresses found with balance */\n foundCount: number;\n /** Current gap count (consecutive empty addresses) */\n currentGap: number;\n /** Number of found addresses that have a nametag */\n nametagsFoundCount: number;\n}\n\n/** Single scanned address result */\nexport interface ScannedAddressResult {\n /** HD derivation index */\n index: number;\n /** L1 bech32 address (alpha1...) */\n address: string;\n /** Full BIP32 derivation path */\n path: string;\n /** L1 balance in ALPHA */\n balance: number;\n /** Whether this is a change address (chain 1) */\n isChange: boolean;\n /** Nametag associated with this address (resolved during scan) */\n nametag?: string;\n}\n\n/** Options for scanning addresses */\nexport interface ScanAddressesOptions {\n /** Maximum number of addresses to scan per chain (default: 50) */\n maxAddresses?: number;\n /** Stop after this many consecutive 0-balance addresses (default: 20) */\n gapLimit?: number;\n /** Also scan change addresses (chain 1) (default: true) */\n includeChange?: boolean;\n /** Progress callback */\n onProgress?: (progress: ScanAddressProgress) => void;\n /** Abort signal for cancellation */\n signal?: AbortSignal;\n /** Resolve nametag for a found address. Return nametag string or null. */\n resolveNametag?: (l1Address: string) => Promise<string | null>;\n}\n\n/** Result of scanning */\nexport interface ScanAddressesResult {\n /** All addresses found with non-zero balance */\n addresses: ScannedAddressResult[];\n /** Total balance across all found addresses (in ALPHA) */\n totalBalance: number;\n /** Number of addresses actually scanned */\n scannedCount: number;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Scan blockchain addresses to discover used addresses with balances.\n * Derives addresses sequentially and checks L1 balance via Fulcrum.\n * Uses gap limit to stop after N consecutive empty addresses.\n *\n * @param deriveAddress - Function to derive an address at a given index\n * @param options - Scanning options\n * @returns Scan results with found addresses and total balance\n */\nexport async function scanAddressesImpl(\n deriveAddress: (index: number, isChange: boolean) => AddressInfo,\n options: ScanAddressesOptions = {},\n): Promise<ScanAddressesResult> {\n const maxAddresses = options.maxAddresses ?? 50;\n const gapLimit = options.gapLimit ?? 20;\n const includeChange = options.includeChange ?? true;\n const { onProgress, signal, resolveNametag } = options;\n\n // Dynamic import to avoid hard dependency on L1 for non-L1 consumers\n const { connect, getBalance } = await import('../l1/network');\n await connect();\n\n const foundAddresses: ScannedAddressResult[] = [];\n let totalBalance = 0;\n let totalScanned = 0;\n let nametagsFoundCount = 0;\n\n const chains: boolean[] = includeChange ? [false, true] : [false];\n const totalToScan = maxAddresses * chains.length;\n\n for (const isChange of chains) {\n let consecutiveEmpty = 0;\n\n for (let index = 0; index < maxAddresses; index++) {\n if (signal?.aborted) break;\n\n const addrInfo = deriveAddress(index, isChange);\n totalScanned++;\n\n onProgress?.({\n scanned: totalScanned,\n total: totalToScan,\n currentAddress: addrInfo.address,\n foundCount: foundAddresses.length,\n currentGap: consecutiveEmpty,\n nametagsFoundCount,\n });\n\n try {\n const balance = await getBalance(addrInfo.address);\n\n if (balance > 0) {\n // Resolve nametag for addresses with balance\n let nametag: string | undefined;\n if (resolveNametag) {\n try {\n const tag = await resolveNametag(addrInfo.address);\n if (tag) {\n nametag = tag;\n nametagsFoundCount++;\n }\n } catch {\n // Nametag resolution failure is non-fatal\n }\n }\n\n foundAddresses.push({\n index,\n address: addrInfo.address,\n path: addrInfo.path,\n balance,\n isChange,\n nametag,\n });\n totalBalance += balance;\n consecutiveEmpty = 0;\n } else {\n consecutiveEmpty++;\n }\n } catch (err) {\n // Network error — count as empty to avoid hanging\n console.warn(`[scanAddresses] Error checking ${addrInfo.address}:`, err);\n consecutiveEmpty++;\n }\n\n if (consecutiveEmpty >= gapLimit) {\n break;\n }\n\n // Yield every 5 addresses to keep UI responsive\n if (totalScanned % 5 === 0) {\n await new Promise(resolve => setTimeout(resolve, 0));\n }\n }\n\n if (signal?.aborted) break;\n }\n\n return {\n addresses: foundAddresses,\n totalBalance,\n scannedCount: totalScanned,\n };\n}\n","/**\n * Wallet Text Format Parsing\n *\n * Parses text-based wallet backup files (UNICITY WALLET DETAILS format)\n */\n\nimport CryptoJS from 'crypto-js';\nimport type { LegacyFileParseResult, LegacyFileParsedData } from './types';\nimport { extractFromText } from '../core/utils';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst WALLET_HEADER = 'UNICITY WALLET DETAILS';\n\n// Legacy webwallet encryption parameters (for backwards compatibility)\nconst LEGACY_SALT = 'alpha_wallet_salt';\nconst LEGACY_ITERATIONS = 100000;\n\n// =============================================================================\n// Encryption/Decryption\n// =============================================================================\n\n/**\n * Derive encryption key using original webwallet parameters\n */\nfunction deriveLegacyKey(password: string): string {\n return CryptoJS.PBKDF2(password, LEGACY_SALT, {\n keySize: 256 / 32,\n iterations: LEGACY_ITERATIONS,\n hasher: CryptoJS.algo.SHA1,\n }).toString();\n}\n\n/**\n * Decrypt master key from text format\n */\nexport function decryptTextFormatKey(encryptedKey: string, password: string): string | null {\n try {\n const key = deriveLegacyKey(password);\n const decrypted = CryptoJS.AES.decrypt(encryptedKey, key);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n return result || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt master key for text format export\n */\nexport function encryptForTextFormat(masterPrivateKey: string, password: string): string {\n const key = deriveLegacyKey(password);\n return CryptoJS.AES.encrypt(masterPrivateKey, key).toString();\n}\n\n// =============================================================================\n// Export Types\n// =============================================================================\n\nexport interface WalletTextExportParams {\n masterPrivateKey: string;\n masterPrivateKeyWIF?: string;\n chainCode?: string;\n descriptorPath?: string;\n isBIP32: boolean;\n addresses: Array<{\n index: number;\n address: string;\n path?: string;\n isChange?: boolean;\n }>;\n}\n\nexport interface WalletTextExportOptions {\n password?: string;\n}\n\n// =============================================================================\n// Serialization Functions\n// =============================================================================\n\n/**\n * Format addresses for text export\n */\nfunction formatAddresses(\n addresses: WalletTextExportParams['addresses'],\n isBIP32: boolean\n): string {\n return addresses\n .map((addr, index) => {\n const path = addr.path || (isBIP32\n ? `m/84'/1'/0'/${addr.isChange ? 1 : 0}/${addr.index}`\n : `m/44'/0'/${addr.index}'`);\n return `Address ${index + 1}: ${addr.address} (Path: ${path})`;\n })\n .join('\\n');\n}\n\n/**\n * Serialize wallet to text format (unencrypted)\n */\nexport function serializeWalletToText(params: WalletTextExportParams): string {\n const {\n masterPrivateKey,\n masterPrivateKeyWIF,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n } = params;\n\n const addressesText = formatAddresses(addresses, isBIP32);\n\n let masterKeySection: string;\n\n if (isBIP32 && chainCode) {\n masterKeySection = `MASTER PRIVATE KEY (keep secret!):\n${masterPrivateKey}\n${masterPrivateKeyWIF ? `\\nMASTER PRIVATE KEY IN WIF FORMAT (for importprivkey command):\\n${masterPrivateKeyWIF}\\n` : ''}\nMASTER CHAIN CODE (for BIP32 HD wallet compatibility):\n${chainCode}\n\nDESCRIPTOR PATH: ${descriptorPath || \"84'/1'/0'\"}\n\nWALLET TYPE: BIP32 hierarchical deterministic wallet\n\nENCRYPTION STATUS: Not encrypted\nThis key is in plaintext and not protected. Anyone with this file can access your wallet.`;\n } else {\n masterKeySection = `MASTER PRIVATE KEY (keep secret!):\n${masterPrivateKey}\n${masterPrivateKeyWIF ? `\\nMASTER PRIVATE KEY IN WIF FORMAT (for importprivkey command):\\n${masterPrivateKeyWIF}\\n` : ''}\nWALLET TYPE: Standard wallet (HMAC-based)\n\nENCRYPTION STATUS: Not encrypted\nThis key is in plaintext and not protected. Anyone with this file can access your wallet.`;\n }\n\n return `${WALLET_HEADER}\n===========================\n\n${masterKeySection}\n\nYOUR ADDRESSES:\n${addressesText}\n\nGenerated on: ${new Date().toLocaleString()}\n\nWARNING: Keep your master private key safe and secure.\nAnyone with your master private key can access all your funds.`;\n}\n\n/**\n * Serialize wallet to text format (encrypted)\n */\nexport function serializeEncryptedWalletToText(params: {\n encryptedMasterKey: string;\n chainCode?: string;\n descriptorPath?: string;\n isBIP32: boolean;\n addresses: WalletTextExportParams['addresses'];\n}): string {\n const { encryptedMasterKey, chainCode, descriptorPath, isBIP32, addresses } = params;\n\n const addressesText = formatAddresses(addresses, isBIP32);\n\n let encryptedContent = `ENCRYPTED MASTER KEY (password protected):\n${encryptedMasterKey}`;\n\n if (isBIP32 && chainCode) {\n encryptedContent += `\n\nMASTER CHAIN CODE (for BIP32 HD wallet compatibility):\n${chainCode}\n\nDESCRIPTOR PATH: ${descriptorPath || \"84'/1'/0'\"}\n\nWALLET TYPE: BIP32 hierarchical deterministic wallet`;\n } else {\n encryptedContent += `\n\nWALLET TYPE: Standard wallet (HMAC-based)`;\n }\n\n return `${WALLET_HEADER}\n===========================\n\n${encryptedContent}\n\nENCRYPTION STATUS: Encrypted with password\nTo use this key, you will need the password you set in the wallet.\n\nYOUR ADDRESSES:\n${addressesText}\n\nGenerated on: ${new Date().toLocaleString()}\n\nWARNING: Keep your master private key safe and secure.\nAnyone with your master private key can access all your funds.`;\n}\n\n// =============================================================================\n// Detection\n// =============================================================================\n\n/**\n * Check if content is wallet text format\n */\nexport function isWalletTextFormat(content: string): boolean {\n return (\n content.includes(WALLET_HEADER) &&\n (content.includes('MASTER PRIVATE KEY') || content.includes('ENCRYPTED MASTER KEY'))\n );\n}\n\n/**\n * Check if text wallet is encrypted\n */\nexport function isTextWalletEncrypted(content: string): boolean {\n return content.includes('ENCRYPTED MASTER KEY');\n}\n\n// =============================================================================\n// Parse Functions\n// =============================================================================\n\n/**\n * Parse wallet from text format (unencrypted)\n */\nexport function parseWalletText(content: string): LegacyFileParseResult {\n try {\n const isEncrypted = isTextWalletEncrypted(content);\n\n if (isEncrypted) {\n // Extract encrypted key - caller needs to decrypt\n const encryptedKey = extractFromText(\n content,\n /ENCRYPTED MASTER KEY \\(password protected\\):\\s*([^\\n]+)/\n );\n\n if (!encryptedKey) {\n return {\n success: false,\n error: 'Could not find encrypted master key in backup file',\n };\n }\n\n // Return with needsPassword flag\n return {\n success: false,\n needsPassword: true,\n error: 'Password required for encrypted wallet',\n };\n }\n\n // Extract unencrypted master key\n const masterKey = extractFromText(\n content,\n /MASTER PRIVATE KEY \\(keep secret!\\):\\s*([^\\n]+)/\n );\n\n if (!masterKey) {\n return {\n success: false,\n error: 'Could not find master private key in backup file',\n };\n }\n\n // Extract chain code\n const chainCode = extractFromText(\n content,\n /MASTER CHAIN CODE \\(for (?:BIP32 HD|Alpha) wallet compatibility\\):\\s*([^\\n]+)/\n );\n\n // Extract descriptor path\n const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\\s*([^\\n]+)/);\n\n // Determine derivation mode\n const isBIP32 =\n content.includes('WALLET TYPE: BIP32 hierarchical deterministic wallet') ||\n content.includes('WALLET TYPE: Alpha descriptor wallet') ||\n !!chainCode;\n\n // BIP32 wallets without explicit descriptor path default to 84'/1'/0' (Alpha network standard).\n // The webwallet exports omit DESCRIPTOR PATH for encrypted files, so we must infer it.\n const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? \"84'/1'/0'\" : undefined);\n\n const data: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: effectiveDescriptorPath,\n derivationMode: isBIP32 ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data,\n };\n } catch (e) {\n return {\n success: false,\n error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n\n/**\n * Parse and decrypt wallet from text format\n */\nexport function parseAndDecryptWalletText(\n content: string,\n password: string\n): LegacyFileParseResult {\n try {\n const isEncrypted = isTextWalletEncrypted(content);\n\n if (!isEncrypted) {\n // Not encrypted, parse directly\n return parseWalletText(content);\n }\n\n // Extract encrypted key\n const encryptedKey = extractFromText(\n content,\n /ENCRYPTED MASTER KEY \\(password protected\\):\\s*([^\\n]+)/\n );\n\n if (!encryptedKey) {\n return {\n success: false,\n error: 'Could not find encrypted master key in backup file',\n };\n }\n\n // Decrypt\n const masterKey = decryptTextFormatKey(encryptedKey, password);\n\n if (!masterKey) {\n return {\n success: false,\n error: 'Failed to decrypt - incorrect password?',\n };\n }\n\n // Extract chain code (not encrypted)\n const chainCode = extractFromText(\n content,\n /MASTER CHAIN CODE \\(for (?:BIP32 HD|Alpha) wallet compatibility\\):\\s*([^\\n]+)/\n );\n\n // Extract descriptor path\n const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\\s*([^\\n]+)/);\n\n // Determine derivation mode\n const isBIP32 =\n content.includes('WALLET TYPE: BIP32 hierarchical deterministic wallet') ||\n content.includes('WALLET TYPE: Alpha descriptor wallet') ||\n !!chainCode;\n\n // BIP32 wallets without explicit descriptor path default to 84'/1'/0' (Alpha network standard).\n // The webwallet exports omit DESCRIPTOR PATH for encrypted files, so we must infer it.\n const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? \"84'/1'/0'\" : undefined);\n\n const data: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: effectiveDescriptorPath,\n derivationMode: isBIP32 ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data,\n };\n } catch (e) {\n return {\n success: false,\n error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n","/**\n * SDK Utility Functions\n * Pure utility functions for the wallet SDK\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** secp256k1 curve order */\nconst SECP256K1_ORDER = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141');\n\n/** Base58 alphabet */\nconst BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\n\n// =============================================================================\n// Private Key Validation\n// =============================================================================\n\n/**\n * Validate if a hex string is a valid secp256k1 private key\n * Must be 0 < key < n (curve order)\n */\nexport function isValidPrivateKey(hex: string): boolean {\n try {\n if (!/^[0-9a-fA-F]{64}$/.test(hex)) {\n return false;\n }\n const key = BigInt('0x' + hex);\n return key > 0n && key < SECP256K1_ORDER;\n } catch {\n return false;\n }\n}\n\n// =============================================================================\n// Base58 Encoding/Decoding\n// =============================================================================\n\n/**\n * Base58 encode hex string\n *\n * @example\n * ```ts\n * base58Encode('00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31')\n * // '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs'\n * ```\n */\nexport function base58Encode(hex: string): string {\n let num = BigInt('0x' + hex);\n let encoded = '';\n\n while (num > 0n) {\n const remainder = Number(num % 58n);\n num = num / 58n;\n encoded = BASE58_ALPHABET[remainder] + encoded;\n }\n\n // Add leading 1s for leading 0s in hex\n for (let i = 0; i < hex.length && hex.substring(i, i + 2) === '00'; i += 2) {\n encoded = '1' + encoded;\n }\n\n return encoded;\n}\n\n/**\n * Base58 decode string to Uint8Array\n *\n * @example\n * ```ts\n * base58Decode('1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')\n * // Uint8Array [0, 245, 74, ...]\n * ```\n */\nexport function base58Decode(str: string): Uint8Array {\n const ALPHABET_MAP: Record<string, number> = {};\n for (let i = 0; i < BASE58_ALPHABET.length; i++) {\n ALPHABET_MAP[BASE58_ALPHABET[i]] = i;\n }\n\n // Count leading zeros (represented as '1' in base58)\n let zeros = 0;\n for (let i = 0; i < str.length && str[i] === '1'; i++) {\n zeros++;\n }\n\n // Decode from base58 to number\n let num = BigInt(0);\n for (let i = 0; i < str.length; i++) {\n const char = str[i];\n if (!(char in ALPHABET_MAP)) {\n throw new Error('Invalid base58 character: ' + char);\n }\n num = num * BigInt(58) + BigInt(ALPHABET_MAP[char]);\n }\n\n // Convert to bytes\n const bytes: number[] = [];\n while (num > 0) {\n bytes.unshift(Number(num % BigInt(256)));\n num = num / BigInt(256);\n }\n\n // Add leading zeros\n for (let i = 0; i < zeros; i++) {\n bytes.unshift(0);\n }\n\n return new Uint8Array(bytes);\n}\n\n// =============================================================================\n// Binary Pattern Search\n// =============================================================================\n\n/**\n * Find pattern in Uint8Array\n *\n * @param data - Data to search in\n * @param pattern - Pattern to find\n * @param startIndex - Start index for search\n * @returns Index of pattern or -1 if not found\n */\nexport function findPattern(\n data: Uint8Array,\n pattern: Uint8Array,\n startIndex: number = 0\n): number {\n for (let i = startIndex; i <= data.length - pattern.length; i++) {\n let found = true;\n for (let j = 0; j < pattern.length; j++) {\n if (data[i + j] !== pattern[j]) {\n found = false;\n break;\n }\n }\n if (found) return i;\n }\n return -1;\n}\n\n// =============================================================================\n// Text Parsing\n// =============================================================================\n\n/**\n * Extract value from text using regex pattern\n *\n * @param text - Text to search\n * @param pattern - Regex pattern with capture group\n * @returns Captured value or null\n */\nexport function extractFromText(text: string, pattern: RegExp): string | null {\n const match = text.match(pattern);\n return match?.[1]?.trim() ?? null;\n}\n\n// =============================================================================\n// Delay/Sleep\n// =============================================================================\n\n/**\n * Sleep for specified milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// =============================================================================\n// Random\n// =============================================================================\n\n/**\n * Generate random hex string of specified byte length\n */\nexport function randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n if (typeof globalThis.crypto !== 'undefined') {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n // Fallback for Node.js without global crypto\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require('crypto');\n const buf = nodeCrypto.randomBytes(byteLength);\n bytes.set(buf);\n }\n return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Generate random UUID v4\n */\nexport function randomUUID(): string {\n if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.randomUUID) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require('crypto');\n return nodeCrypto.randomUUID();\n}\n","/**\n * Wallet.dat Parsing\n *\n * Parses Bitcoin Core wallet.dat files (SQLite format)\n * Extracts keys, chain codes, and descriptors\n */\n\nimport CryptoJS from 'crypto-js';\nimport type { LegacyFileParseResult, LegacyFileParsedData, DecryptionProgressCallback } from './types';\nimport { findPattern, base58Decode, isValidPrivateKey } from '../core/utils';\nimport { bytesToHex } from '../core/crypto';\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nfunction uint8ArrayToWordArray(u8arr: Uint8Array): CryptoJS.lib.WordArray {\n const hex = bytesToHex(u8arr);\n return CryptoJS.enc.Hex.parse(hex);\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface CMasterKeyData {\n encryptedKey: Uint8Array;\n salt: Uint8Array;\n derivationMethod: number;\n iterations: number;\n position: number;\n}\n\nexport interface WalletDatInfo {\n isSQLite: boolean;\n isEncrypted: boolean;\n isDescriptorWallet: boolean;\n hasHDChain: boolean;\n descriptorKeys: string[];\n legacyKeys: string[];\n chainCode: string | null;\n descriptorPath: string | null;\n cmasterKeys: CMasterKeyData[];\n descriptorId: Uint8Array | null;\n xpubString: string | null;\n}\n\n// =============================================================================\n// Detection Functions\n// =============================================================================\n\n/**\n * Check if data is a valid SQLite database\n */\nexport function isSQLiteDatabase(data: Uint8Array): boolean {\n if (data.length < 16) return false;\n const header = new TextDecoder().decode(data.slice(0, 16));\n return header.startsWith('SQLite format 3');\n}\n\n/**\n * Check if wallet.dat is encrypted\n */\nexport function isWalletDatEncrypted(data: Uint8Array): boolean {\n const mkeyPattern = new TextEncoder().encode('mkey');\n return findPattern(data, mkeyPattern, 0) !== -1;\n}\n\n// =============================================================================\n// CMasterKey Detection\n// =============================================================================\n\nfunction findAllCMasterKeys(data: Uint8Array): CMasterKeyData[] {\n const results: CMasterKeyData[] = [];\n\n for (let pos = 0; pos < data.length - 70; pos++) {\n if (data[pos] === 0x30) {\n const saltLenPos = pos + 1 + 48;\n if (saltLenPos < data.length && data[saltLenPos] === 0x08) {\n const iterPos = saltLenPos + 1 + 8 + 4;\n if (iterPos + 4 <= data.length) {\n const iterations = data[iterPos] | (data[iterPos + 1] << 8) |\n (data[iterPos + 2] << 16) | (data[iterPos + 3] << 24);\n if (iterations >= 1000 && iterations <= 10000000) {\n const encryptedKey = data.slice(pos + 1, pos + 1 + 48);\n const salt = data.slice(saltLenPos + 1, saltLenPos + 1 + 8);\n const derivationMethod = data[saltLenPos + 1 + 8] | (data[saltLenPos + 1 + 8 + 1] << 8) |\n (data[saltLenPos + 1 + 8 + 2] << 16) | (data[saltLenPos + 1 + 8 + 3] << 24);\n\n results.push({ encryptedKey, salt, derivationMethod, iterations, position: pos });\n }\n }\n }\n }\n }\n\n return results;\n}\n\n// =============================================================================\n// Descriptor Extraction\n// =============================================================================\n\nfunction findWpkhDescriptor(data: Uint8Array): {\n descriptorId: Uint8Array | null;\n xpubString: string | null;\n descriptorPath: string | null;\n} {\n const descriptorPattern = new TextEncoder().encode('walletdescriptor');\n let descriptorIndex = 0;\n\n while ((descriptorIndex = findPattern(data, descriptorPattern, descriptorIndex)) !== -1) {\n let scanPos = descriptorIndex + descriptorPattern.length + 32;\n\n const descLen = data[scanPos];\n scanPos++;\n\n const descBytes = data.slice(scanPos, scanPos + Math.min(descLen, 200));\n let descStr = '';\n for (let i = 0; i < descBytes.length && descBytes[i] >= 32 && descBytes[i] <= 126; i++) {\n descStr += String.fromCharCode(descBytes[i]);\n }\n\n if (descStr.startsWith('wpkh(xpub') && descStr.includes('/0/*)')) {\n const xpubMatch = descStr.match(/xpub[1-9A-HJ-NP-Za-km-z]{100,}/);\n if (xpubMatch) {\n const descIdStart = descriptorIndex + descriptorPattern.length;\n const descriptorId = data.slice(descIdStart, descIdStart + 32);\n const pathMatch = descStr.match(/\\[[\\da-f]+\\/(\\d+'\\/\\d+'\\/\\d+')\\]/);\n const descriptorPath = pathMatch ? pathMatch[1] : \"84'/1'/0'\";\n\n return {\n descriptorId,\n xpubString: xpubMatch[0],\n descriptorPath,\n };\n }\n }\n\n descriptorIndex++;\n }\n\n return { descriptorId: null, xpubString: null, descriptorPath: null };\n}\n\nfunction extractChainCodeFromXpub(xpubString: string): string {\n const decoded = base58Decode(xpubString);\n return bytesToHex(decoded.slice(13, 45));\n}\n\nfunction findMasterChainCode(data: Uint8Array): string | null {\n const xpubPattern = new TextEncoder().encode('xpub');\n const base58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\n let searchPos = 0;\n\n while (searchPos < data.length) {\n const xpubIndex = findPattern(data, xpubPattern, searchPos);\n if (xpubIndex === -1) break;\n\n let xpubStr = 'xpub';\n let pos = xpubIndex + 4;\n\n while (pos < data.length && xpubStr.length < 120) {\n const char = String.fromCharCode(data[pos]);\n if (base58Chars.includes(char)) {\n xpubStr += char;\n pos++;\n } else {\n break;\n }\n }\n\n if (xpubStr.length > 100) {\n try {\n const decoded = base58Decode(xpubStr);\n const depth = decoded[4];\n if (depth === 0) {\n return bytesToHex(decoded.slice(13, 45));\n }\n } catch {\n // Invalid xpub, continue\n }\n }\n\n searchPos = xpubIndex + 4;\n }\n\n return null;\n}\n\n// =============================================================================\n// Key Extraction (Unencrypted)\n// =============================================================================\n\nfunction extractDescriptorKeys(data: Uint8Array): string[] {\n const keys: string[] = [];\n const descriptorKeyPattern = new TextEncoder().encode('walletdescriptorkey');\n\n let index = 0;\n while ((index = findPattern(data, descriptorKeyPattern, index)) !== -1) {\n for (let checkPos = index + descriptorKeyPattern.length;\n checkPos < Math.min(index + descriptorKeyPattern.length + 200, data.length - 40);\n checkPos++) {\n if (data[checkPos] === 0xd3 &&\n data[checkPos + 1] === 0x02 &&\n data[checkPos + 2] === 0x01 &&\n data[checkPos + 3] === 0x01 &&\n data[checkPos + 4] === 0x04 &&\n data[checkPos + 5] === 0x20) {\n const privKey = data.slice(checkPos + 6, checkPos + 38);\n const privKeyHex = bytesToHex(privKey);\n\n if (isValidPrivateKey(privKeyHex)) {\n keys.push(privKeyHex);\n break;\n }\n }\n }\n index++;\n }\n\n return keys;\n}\n\nfunction extractLegacyKeys(data: Uint8Array): string[] {\n const keys: string[] = [];\n const keyPattern = new TextEncoder().encode('key');\n\n let index = 0;\n while ((index = findPattern(data, keyPattern, index)) !== -1) {\n const searchPattern = new Uint8Array([0x04, 0x20]);\n for (let i = index; i < Math.min(index + 200, data.length - 34); i++) {\n if (data[i] === searchPattern[0] && data[i + 1] === searchPattern[1]) {\n const privKey = data.slice(i + 2, i + 34);\n const privKeyHex = bytesToHex(privKey);\n\n if (isValidPrivateKey(privKeyHex)) {\n keys.push(privKeyHex);\n break;\n }\n }\n }\n index++;\n }\n\n return keys;\n}\n\n// =============================================================================\n// Encrypted Key Lookup\n// =============================================================================\n\nfunction findEncryptedKeyForDescriptor(\n data: Uint8Array,\n descriptorId: Uint8Array\n): { pubkey: Uint8Array; encryptedKey: Uint8Array } | null {\n const ckeyPattern = new TextEncoder().encode('walletdescriptorckey');\n let ckeyIndex = findPattern(data, ckeyPattern, 0);\n\n while (ckeyIndex !== -1) {\n const recordDescId = data.slice(ckeyIndex + ckeyPattern.length, ckeyIndex + ckeyPattern.length + 32);\n\n if (Array.from(recordDescId).every((b, i) => b === descriptorId[i])) {\n let keyPos = ckeyIndex + ckeyPattern.length + 32;\n const pubkeyLen = data[keyPos];\n keyPos++;\n const pubkey = data.slice(keyPos, keyPos + pubkeyLen);\n\n for (let searchPos = keyPos + pubkeyLen;\n searchPos < Math.min(keyPos + pubkeyLen + 100, data.length - 50);\n searchPos++) {\n const valueLen = data[searchPos];\n if (valueLen >= 32 && valueLen <= 64) {\n const encryptedKey = data.slice(searchPos + 1, searchPos + 1 + valueLen);\n return { pubkey, encryptedKey };\n }\n }\n }\n\n ckeyIndex = findPattern(data, ckeyPattern, ckeyIndex + 1);\n }\n\n return null;\n}\n\n// =============================================================================\n// Decryption Functions\n// =============================================================================\n\n/**\n * Decrypt master key from CMasterKey structure\n */\nexport async function decryptCMasterKey(\n cmk: CMasterKeyData,\n password: string,\n onProgress?: DecryptionProgressCallback\n): Promise<string> {\n const { encryptedKey, salt, iterations } = cmk;\n\n const passwordHex = bytesToHex(new TextEncoder().encode(password));\n const saltHex = bytesToHex(salt);\n const inputHex = passwordHex + saltHex;\n\n let hash = CryptoJS.SHA512(CryptoJS.enc.Hex.parse(inputHex));\n\n const BATCH_SIZE = 1000;\n for (let i = 0; i < iterations - 1; i++) {\n hash = CryptoJS.SHA512(hash);\n if (onProgress && i % BATCH_SIZE === 0) {\n await onProgress(i, iterations);\n }\n }\n\n const derivedKey = CryptoJS.lib.WordArray.create(hash.words.slice(0, 8));\n const derivedIv = CryptoJS.lib.WordArray.create(hash.words.slice(8, 12));\n\n const encryptedWords = uint8ArrayToWordArray(encryptedKey);\n\n const decrypted = CryptoJS.AES.decrypt(\n { ciphertext: encryptedWords } as CryptoJS.lib.CipherParams,\n derivedKey,\n { iv: derivedIv, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC }\n );\n\n const result = CryptoJS.enc.Hex.stringify(decrypted);\n\n if (!result || result.length !== 64) {\n throw new Error('Master key decryption failed - incorrect password');\n }\n\n return result;\n}\n\n/**\n * Decrypt private key using decrypted master key\n */\nexport function decryptPrivateKey(\n encryptedKey: Uint8Array,\n pubkey: Uint8Array,\n masterKeyHex: string\n): string {\n const pubkeyWords = uint8ArrayToWordArray(pubkey);\n const pubkeyHashWords = CryptoJS.SHA256(CryptoJS.SHA256(pubkeyWords));\n const ivWords = CryptoJS.lib.WordArray.create(pubkeyHashWords.words.slice(0, 4));\n\n const masterKeyWords = CryptoJS.enc.Hex.parse(masterKeyHex);\n const encryptedWords = uint8ArrayToWordArray(encryptedKey);\n\n const decrypted = CryptoJS.AES.decrypt(\n { ciphertext: encryptedWords } as CryptoJS.lib.CipherParams,\n masterKeyWords,\n { iv: ivWords, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC }\n );\n\n return CryptoJS.enc.Hex.stringify(decrypted);\n}\n\n// =============================================================================\n// Main Parse Function\n// =============================================================================\n\n/**\n * Parse wallet.dat file\n * For encrypted wallets, returns info for decryption\n */\nexport function parseWalletDat(data: Uint8Array): LegacyFileParseResult {\n if (!isSQLiteDatabase(data)) {\n return {\n success: false,\n error: 'Invalid wallet.dat file - not an SQLite database',\n };\n }\n\n const isEncrypted = isWalletDatEncrypted(data);\n const cmasterKeys = isEncrypted ? findAllCMasterKeys(data) : [];\n const descriptorKeys = isEncrypted ? [] : extractDescriptorKeys(data);\n const legacyKeys = isEncrypted ? [] : extractLegacyKeys(data);\n\n // descriptorId is used in parseAndDecryptWalletDat for encrypted wallets\n const { descriptorId: _descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);\n void _descriptorId; // Suppress unused warning\n\n let chainCode: string | null = null;\n if (xpubString) {\n try {\n chainCode = extractChainCodeFromXpub(xpubString);\n } catch {\n chainCode = findMasterChainCode(data);\n }\n } else {\n chainCode = findMasterChainCode(data);\n }\n\n // For unencrypted wallets\n if (!isEncrypted) {\n let masterKey: string | null = null;\n\n if (descriptorKeys.length > 0) {\n masterKey = descriptorKeys[0];\n } else if (legacyKeys.length > 0) {\n masterKey = legacyKeys[0];\n }\n\n if (masterKey) {\n const parsedData: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: descriptorPath ?? \"84'/1'/0'\",\n derivationMode: chainCode ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data: parsedData,\n };\n }\n\n return {\n success: false,\n error: 'No valid private keys found in wallet.dat file',\n };\n }\n\n // For encrypted wallets\n if (cmasterKeys.length === 0) {\n return {\n success: false,\n error: 'Encrypted wallet but no CMasterKey structures found',\n };\n }\n\n // Return encryption info for decryption\n const firstCmk = cmasterKeys[0];\n return {\n success: false,\n needsPassword: true,\n encryptionInfo: {\n iterations: firstCmk.iterations,\n salt: firstCmk.salt,\n encryptedKey: firstCmk.encryptedKey,\n },\n error: 'Password required for encrypted wallet',\n };\n}\n\n/**\n * Parse and decrypt wallet.dat file\n */\nexport async function parseAndDecryptWalletDat(\n data: Uint8Array,\n password: string,\n onProgress?: DecryptionProgressCallback\n): Promise<LegacyFileParseResult> {\n if (!isSQLiteDatabase(data)) {\n return {\n success: false,\n error: 'Invalid wallet.dat file - not an SQLite database',\n };\n }\n\n const isEncrypted = isWalletDatEncrypted(data);\n\n if (!isEncrypted) {\n // Not encrypted, parse directly\n return parseWalletDat(data);\n }\n\n const cmasterKeys = findAllCMasterKeys(data);\n if (cmasterKeys.length === 0) {\n return {\n success: false,\n error: 'Encrypted wallet but no CMasterKey structures found',\n };\n }\n\n // Try to decrypt each CMasterKey\n let masterKeyHex: string | null = null;\n for (const cmk of cmasterKeys) {\n try {\n masterKeyHex = await decryptCMasterKey(cmk, password, onProgress);\n if (masterKeyHex && masterKeyHex.length === 64) {\n break;\n }\n } catch {\n continue;\n }\n }\n\n if (!masterKeyHex || masterKeyHex.length !== 64) {\n return {\n success: false,\n error: 'Master key decryption failed - incorrect password',\n };\n }\n\n // Find descriptor info\n const { descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);\n\n let chainCode: string | null = null;\n if (xpubString) {\n try {\n chainCode = extractChainCodeFromXpub(xpubString);\n } catch {\n chainCode = findMasterChainCode(data);\n }\n } else {\n chainCode = findMasterChainCode(data);\n }\n\n // Find and decrypt BIP32 master private key\n if (!descriptorId) {\n return {\n success: false,\n error: 'Could not find native SegWit receive descriptor',\n };\n }\n\n const encryptedKeyData = findEncryptedKeyForDescriptor(data, descriptorId);\n if (!encryptedKeyData) {\n return {\n success: false,\n error: 'Could not find encrypted private key for descriptor',\n };\n }\n\n const bip32MasterKey = decryptPrivateKey(\n encryptedKeyData.encryptedKey,\n encryptedKeyData.pubkey,\n masterKeyHex\n );\n\n if (!bip32MasterKey || bip32MasterKey.length !== 64) {\n return {\n success: false,\n error: 'Could not decrypt BIP32 master private key',\n };\n }\n\n const parsedData: LegacyFileParsedData = {\n masterKey: bip32MasterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: descriptorPath ?? \"84'/1'/0'\",\n derivationMode: 'bip32',\n };\n\n return {\n success: true,\n data: parsedData,\n };\n}\n","/**\n * Sphere - Main SDK Entry Point\n *\n * Handles wallet existence checking, creation, and loading.\n *\n * @example\n * ```ts\n * import { Sphere } from '@unicitylabs/sphere-sdk';\n * import { createLocalStorageProvider, createNostrTransportProvider, createUnicityAggregatorProvider } from '@unicitylabs/sphere-sdk/impl/browser';\n *\n * const storage = createLocalStorageProvider();\n * const transport = createNostrTransportProvider();\n * const oracle = createUnicityAggregatorProvider({ url: '/rpc' });\n *\n * // Option 1: Unified init (recommended)\n * const { sphere, created, generatedMnemonic } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * mnemonic: 'your twelve words...', // optional - will load if wallet exists\n * autoGenerate: true, // generate new mnemonic if needed\n * });\n *\n * if (created && generatedMnemonic) {\n * console.log('Save this mnemonic:', generatedMnemonic);\n * }\n *\n * // Option 2: Manual create/load\n * if (await Sphere.exists(storage)) {\n * const sphere = await Sphere.load({ storage, transport, oracle });\n * } else {\n * const sphere = await Sphere.create({ mnemonic, storage, transport, oracle });\n * }\n *\n * // Use the wallet\n * await sphere.payments.send({ coinId: 'ALPHA', amount: '1000', recipient: '@alice' });\n * ```\n */\n\nimport type {\n Identity,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n SphereEventHandler,\n DerivationMode,\n WalletSource,\n WalletInfo,\n WalletJSON,\n WalletJSONExportOptions,\n TrackedAddress,\n TrackedAddressEntry,\n} from '../types';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../storage';\nimport type { TransportProvider, PeerInfo } from '../transport';\nimport type { OracleProvider } from '../oracle';\nimport type { PriceProvider } from '../price';\nimport { PaymentsModule, createPaymentsModule } from '../modules/payments';\nimport { CommunicationsModule, createCommunicationsModule } from '../modules/communications';\nimport {\n STORAGE_KEYS_GLOBAL,\n STORAGE_KEYS_ADDRESS,\n getAddressId,\n DEFAULT_BASE_PATH,\n LIMITS,\n DEFAULT_ENCRYPTION_KEY,\n type NetworkType,\n} from '../constants';\nimport {\n generateMnemonic as generateBip39Mnemonic,\n validateMnemonic as validateBip39Mnemonic,\n identityFromMnemonicSync,\n deriveKeyAtPath,\n deriveAddressInfo,\n getPublicKey,\n sha256,\n publicKeyToAddress,\n type MasterKey,\n type AddressInfo,\n} from './crypto';\nimport { encryptSimple, decryptSimple, decryptWithSalt } from './encryption';\nimport { scanAddressesImpl } from './scan';\nimport type { ScanAddressesOptions, ScanAddressesResult } from './scan';\nimport { vestingClassifier } from '../l1/vesting';\nimport { generateAddressFromMasterKey } from '../l1/address';\nimport {\n parseWalletText,\n parseAndDecryptWalletText,\n isWalletTextFormat,\n isTextWalletEncrypted,\n serializeWalletToText,\n serializeEncryptedWalletToText,\n encryptForTextFormat,\n} from '../serialization/wallet-text';\nimport {\n parseWalletDat,\n parseAndDecryptWalletDat,\n isSQLiteDatabase,\n isWalletDatEncrypted,\n} from '../serialization/wallet-dat';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\n\nimport type {\n LegacyFileType,\n DecryptionProgressCallback,\n} from '../serialization/types';\n\n// =============================================================================\n// Options Types\n// =============================================================================\n\n/** Options for creating a new wallet */\nexport interface SphereCreateOptions {\n /** BIP39 mnemonic (12 or 24 words) */\n mnemonic: string;\n /** Custom derivation path (default: m/44'/0'/0') */\n derivationPath?: string;\n /** Optional nametag to register for this wallet (e.g., 'alice' for @alice). Token is auto-minted. */\n nametag?: string;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n}\n\n/** Options for loading existing wallet */\nexport interface SphereLoadOptions {\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n}\n\n/** Options for importing a wallet */\nexport interface SphereImportOptions {\n /** BIP39 mnemonic to import */\n mnemonic?: string;\n /** Or master private key (hex) */\n masterKey?: string;\n /** Chain code for BIP32 (optional) */\n chainCode?: string;\n /** Custom derivation path */\n derivationPath?: string;\n /** Base path for BIP32 derivation (e.g., \"m/84'/1'/0'\" from wallet.dat) */\n basePath?: string;\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n derivationMode?: DerivationMode;\n /** Optional nametag to register for this wallet (e.g., 'alice' for @alice). Token is auto-minted. */\n nametag?: string;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n}\n\n/** L1 (ALPHA blockchain) configuration */\nexport interface L1Config {\n /** Fulcrum WebSocket URL (default: wss://fulcrum.alpha.unicity.network:50004) */\n electrumUrl?: string;\n /** Default fee rate in sat/byte (default: 10) */\n defaultFeeRate?: number;\n /** Enable vesting classification (default: true) */\n enableVesting?: boolean;\n}\n\n/** Options for unified init (auto-create or load) */\nexport interface SphereInitOptions {\n /** Storage provider instance */\n storage: StorageProvider;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** BIP39 mnemonic - if wallet doesn't exist, use this to create */\n mnemonic?: string;\n /** Auto-generate mnemonic if wallet doesn't exist and no mnemonic provided */\n autoGenerate?: boolean;\n /** Custom derivation path (default: m/44'/0'/0') */\n derivationPath?: string;\n /** Optional nametag to register (only on create). Token is auto-minted. */\n nametag?: string;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n}\n\n/** Result of init operation */\nexport interface SphereInitResult {\n /** The initialized Sphere instance */\n sphere: Sphere;\n /** Whether wallet was newly created */\n created: boolean;\n /** Generated mnemonic (only if autoGenerate was used) */\n generatedMnemonic?: string;\n}\n\n// =============================================================================\n// L3 Predicate Address Derivation\n// =============================================================================\n\n/** Token type for Unicity network (used for L3 predicate address derivation) */\nconst UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n\n/**\n * Derive L3 predicate address (DIRECT://...) from private key\n * Uses UnmaskedPredicateReference for stable wallet address\n */\nasync function deriveL3PredicateAddress(privateKey: string): Promise<string> {\n const secret = Buffer.from(privateKey, 'hex');\n const signingService = await SigningService.createFromSecret(secret);\n\n const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex');\n const tokenType = new TokenType(tokenTypeBytes);\n\n const predicateRef = UnmaskedPredicateReference.create(\n tokenType,\n signingService.algorithm,\n signingService.publicKey,\n HashAlgorithm.SHA256\n );\n\n return (await (await predicateRef).toAddress()).toString();\n}\n\n// =============================================================================\n// Mutable Identity (internal use only)\n// =============================================================================\n\n/** Mutable version of FullIdentity for internal state management */\ntype MutableFullIdentity = {\n -readonly [K in keyof FullIdentity]: FullIdentity[K];\n};\n\n// =============================================================================\n// Sphere Class\n// =============================================================================\n\nexport class Sphere {\n // Singleton\n private static instance: Sphere | null = null;\n\n // State\n private _initialized = false;\n private _identity: MutableFullIdentity | null = null;\n private _masterKey: MasterKey | null = null;\n private _mnemonic: string | null = null;\n private _source: WalletSource = 'unknown';\n private _derivationMode: DerivationMode = 'bip32';\n private _basePath: string = DEFAULT_BASE_PATH;\n private _currentAddressIndex: number = 0;\n /** Registry of all tracked (activated) addresses, keyed by HD index */\n private _trackedAddresses: Map<number, TrackedAddress> = new Map();\n /** Reverse lookup: addressId -> HD index */\n private _addressIdToIndex: Map<string, number> = new Map();\n /** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */\n private _addressNametags: Map<string, Map<number, string>> = new Map();\n /** Cached PROXY address (computed once when nametag is set) */\n private _cachedProxyAddress: string | undefined = undefined;\n\n // Providers\n private _storage: StorageProvider;\n private _tokenStorageProviders: Map<string, TokenStorageProvider<TxfStorageDataBase>> = new Map();\n private _transport: TransportProvider;\n private _oracle: OracleProvider;\n private _priceProvider: PriceProvider | null;\n\n // Modules\n private _payments: PaymentsModule;\n private _communications: CommunicationsModule;\n\n // Events\n private eventHandlers: Map<SphereEventType, Set<SphereEventHandler<SphereEventType>>> = new Map();\n\n // ===========================================================================\n // Constructor (private)\n // ===========================================================================\n\n private constructor(\n storage: StorageProvider,\n transport: TransportProvider,\n oracle: OracleProvider,\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>,\n l1Config?: L1Config,\n priceProvider?: PriceProvider,\n ) {\n this._storage = storage;\n this._transport = transport;\n this._oracle = oracle;\n this._priceProvider = priceProvider ?? null;\n\n // Initialize token storage providers map\n if (tokenStorage) {\n this._tokenStorageProviders.set(tokenStorage.id, tokenStorage);\n }\n\n this._payments = createPaymentsModule({ l1: l1Config });\n this._communications = createCommunicationsModule();\n }\n\n // ===========================================================================\n // Static Methods - Wallet Management\n // ===========================================================================\n\n /**\n * Check if wallet exists in storage\n */\n static async exists(storage: StorageProvider): Promise<boolean> {\n try {\n // Ensure storage is connected before checking\n if (!storage.isConnected()) {\n await storage.connect();\n }\n\n // Check for mnemonic or master_key directly\n // These are saved with 'default' address before identity is set\n const mnemonic = await storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);\n if (mnemonic) return true;\n\n const masterKey = await storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n if (masterKey) return true;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Initialize wallet - auto-loads existing or creates new\n *\n * @example\n * ```ts\n * // Load existing or create with provided mnemonic\n * const { sphere, created } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * mnemonic: 'your twelve words...',\n * });\n *\n * // Load existing or auto-generate new mnemonic\n * const { sphere, created, generatedMnemonic } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * autoGenerate: true,\n * });\n * if (generatedMnemonic) {\n * console.log('Save this mnemonic:', generatedMnemonic);\n * }\n * ```\n */\n static async init(options: SphereInitOptions): Promise<SphereInitResult> {\n const walletExists = await Sphere.exists(options.storage);\n\n if (walletExists) {\n // Load existing wallet\n const sphere = await Sphere.load({\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n price: options.price,\n });\n return { sphere, created: false };\n }\n\n // Need to create new wallet\n let mnemonic = options.mnemonic;\n let generatedMnemonic: string | undefined;\n\n if (!mnemonic) {\n if (options.autoGenerate) {\n // Auto-generate mnemonic\n mnemonic = Sphere.generateMnemonic();\n generatedMnemonic = mnemonic;\n } else {\n throw new Error(\n 'No wallet exists and no mnemonic provided. ' +\n 'Provide a mnemonic or set autoGenerate: true.'\n );\n }\n }\n\n const sphere = await Sphere.create({\n mnemonic,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n derivationPath: options.derivationPath,\n nametag: options.nametag,\n l1: options.l1,\n price: options.price,\n });\n\n return { sphere, created: true, generatedMnemonic };\n }\n\n /**\n * Create new wallet with mnemonic\n */\n static async create(options: SphereCreateOptions): Promise<Sphere> {\n // Validate mnemonic\n if (!options.mnemonic || !Sphere.validateMnemonic(options.mnemonic)) {\n throw new Error('Invalid mnemonic');\n }\n\n // Check if wallet already exists\n if (await Sphere.exists(options.storage)) {\n throw new Error('Wallet already exists. Use Sphere.load() or Sphere.clear() first.');\n }\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n );\n\n // Store encrypted mnemonic\n await sphere.storeMnemonic(options.mnemonic, options.derivationPath);\n\n // Initialize identity from mnemonic\n await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);\n\n // Initialize everything\n await sphere.initializeProviders();\n await sphere.initializeModules();\n\n // Mark wallet as created only after successful initialization\n // This prevents \"Wallet already exists\" errors if init fails partway through\n await sphere.finalizeWalletCreation();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // Track address 0 in the registry\n await sphere.ensureAddressTracked(0);\n\n // Register nametag if provided, otherwise try recovery then publish\n if (options.nametag) {\n // registerNametag publishes identity binding WITH nametag atomically\n // (calling syncIdentityWithTransport before this would race — both replaceable\n // events get the same created_at second and relay keeps the one without nametag)\n await sphere.registerNametag(options.nametag);\n } else {\n // Try to recover nametag BEFORE publishing — publishIdentityBinding uses\n // kind 30078 (replaceable event), so a bare binding would overwrite the\n // existing one that contains encrypted_nametag, making recovery impossible.\n await sphere.recoverNametagFromTransport();\n // Now publish identity binding (with recovered nametag if found)\n await sphere.syncIdentityWithTransport();\n }\n\n return sphere;\n }\n\n /**\n * Load existing wallet from storage\n */\n static async load(options: SphereLoadOptions): Promise<Sphere> {\n // Check if wallet exists\n if (!(await Sphere.exists(options.storage))) {\n throw new Error('No wallet found. Use Sphere.create() to create a new wallet.');\n }\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n );\n\n // Load identity from storage\n await sphere.loadIdentityFromStorage();\n\n // Initialize everything\n await sphere.initializeProviders();\n await sphere.initializeModules();\n\n // Publish identity binding via transport\n await sphere.syncIdentityWithTransport();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // If nametag name exists but token is missing, try to mint it.\n // This handles the case where the token was lost from IndexedDB.\n if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {\n console.log(`[Sphere] Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);\n try {\n const result = await sphere.mintNametag(sphere._identity.nametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully on load`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed:`, err);\n }\n }\n\n return sphere;\n }\n\n /**\n * Import wallet from mnemonic or master key\n */\n static async import(options: SphereImportOptions): Promise<Sphere> {\n if (!options.mnemonic && !options.masterKey) {\n throw new Error('Either mnemonic or masterKey is required');\n }\n\n console.log('[Sphere.import] Starting import...');\n\n // Clear existing wallet if any (including token data)\n console.log('[Sphere.import] Clearing existing wallet data...');\n await Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });\n console.log('[Sphere.import] Clear done');\n\n // Reconnect storage after clear (clear may have called destroy() on the\n // previous instance which disconnects the shared storage provider)\n if (!options.storage.isConnected()) {\n console.log('[Sphere.import] Reconnecting storage...');\n await options.storage.connect();\n console.log('[Sphere.import] Storage reconnected');\n }\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n );\n\n if (options.mnemonic) {\n // Validate and store mnemonic\n if (!Sphere.validateMnemonic(options.mnemonic)) {\n throw new Error('Invalid mnemonic');\n }\n console.log('[Sphere.import] Storing mnemonic...');\n await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);\n console.log('[Sphere.import] Initializing identity from mnemonic...');\n await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);\n } else if (options.masterKey) {\n // Store master key directly\n console.log('[Sphere.import] Storing master key...');\n await sphere.storeMasterKey(\n options.masterKey,\n options.chainCode,\n options.derivationPath,\n options.basePath,\n options.derivationMode\n );\n console.log('[Sphere.import] Initializing identity from master key...');\n await sphere.initializeIdentityFromMasterKey(\n options.masterKey,\n options.chainCode,\n options.derivationPath\n );\n }\n\n // Initialize everything\n console.log('[Sphere.import] Initializing providers...');\n await sphere.initializeProviders();\n console.log('[Sphere.import] Providers initialized. Initializing modules...');\n await sphere.initializeModules();\n console.log('[Sphere.import] Modules initialized');\n\n // Try to recover nametag from transport (if no nametag provided and wallet previously had one)\n if (!options.nametag) {\n console.log('[Sphere.import] Recovering nametag from transport...');\n await sphere.recoverNametagFromTransport();\n console.log('[Sphere.import] Nametag recovery done');\n // Publish identity binding (with recovered nametag if found)\n await sphere.syncIdentityWithTransport();\n }\n\n // Mark wallet as created only after successful initialization\n console.log('[Sphere.import] Finalizing wallet creation...');\n await sphere.finalizeWalletCreation();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // Track address 0 in the registry\n console.log('[Sphere.import] Tracking address 0...');\n await sphere.ensureAddressTracked(0);\n\n // Register nametag if provided (this overrides any recovered nametag)\n if (options.nametag) {\n console.log('[Sphere.import] Registering nametag...');\n await sphere.registerNametag(options.nametag);\n }\n\n console.log('[Sphere.import] Import complete');\n return sphere;\n }\n\n /**\n * Clear all SDK-owned wallet data from storage.\n *\n * Removes wallet keys, per-address data, and optionally token storage.\n * Does NOT affect application-level data stored outside the SDK.\n *\n * @param storageOrOptions - StorageProvider (backward compatible) or options object\n *\n * @example\n * // New usage (recommended) - clears wallet keys AND token data\n * await Sphere.clear({\n * storage: providers.storage,\n * tokenStorage: providers.tokenStorage,\n * });\n *\n * @example\n * // Legacy usage - clears only wallet keys\n * await Sphere.clear(storage);\n */\n static async clear(\n storageOrOptions: StorageProvider | { storage: StorageProvider; tokenStorage?: TokenStorageProvider<TxfStorageDataBase> },\n ): Promise<void> {\n const storage = 'get' in storageOrOptions ? storageOrOptions as StorageProvider : storageOrOptions.storage;\n const tokenStorage = 'get' in storageOrOptions ? undefined : storageOrOptions.tokenStorage;\n\n // Ensure storage is connected (may have been disconnected by a previous destroy() cycle)\n if (!storage.isConnected()) {\n await storage.connect();\n }\n\n // Clear global wallet data\n console.log('[Sphere.clear] Removing storage keys...');\n await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);\n await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);\n await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);\n await storage.remove(STORAGE_KEYS_GLOBAL.BASE_PATH);\n await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);\n await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);\n await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_EXISTS);\n await storage.remove(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);\n await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n // Per-address data\n await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);\n await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);\n console.log('[Sphere.clear] Storage keys removed');\n\n // Clear token storage if provided (with timeout to prevent IndexedDB deadlock)\n if (tokenStorage?.clear) {\n console.log('[Sphere.clear] Clearing token storage...');\n try {\n await Promise.race([\n tokenStorage.clear(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('tokenStorage.clear() timed out after 2s')), 2000),\n ),\n ]);\n console.log('[Sphere.clear] Token storage cleared');\n } catch (err) {\n console.warn('[Sphere.clear] Token storage clear failed/timed out:', err);\n }\n }\n\n // Clear L1 vesting cache\n console.log('[Sphere.clear] Destroying vesting classifier...');\n await vestingClassifier.destroy();\n console.log('[Sphere.clear] Vesting classifier destroyed');\n\n if (Sphere.instance) {\n console.log('[Sphere.clear] Destroying Sphere instance...');\n await Sphere.instance.destroy();\n console.log('[Sphere.clear] Sphere instance destroyed');\n } else {\n console.log('[Sphere.clear] No Sphere instance to destroy');\n }\n }\n\n /**\n * Get current instance\n */\n static getInstance(): Sphere | null {\n return Sphere.instance;\n }\n\n /**\n * Check if initialized\n */\n static isInitialized(): boolean {\n return Sphere.instance?._initialized ?? false;\n }\n\n /**\n * Validate mnemonic using BIP39\n */\n static validateMnemonic(mnemonic: string): boolean {\n return validateBip39Mnemonic(mnemonic);\n }\n\n /**\n * Generate new BIP39 mnemonic\n * @param strength - 128 for 12 words, 256 for 24 words\n */\n static generateMnemonic(strength: 128 | 256 = 128): string {\n return generateBip39Mnemonic(strength);\n }\n\n // ===========================================================================\n // Public Properties - Modules\n // ===========================================================================\n\n /** Payments module (L3 + L1) */\n get payments(): PaymentsModule {\n this.ensureReady();\n return this._payments;\n }\n\n /** Communications module */\n get communications(): CommunicationsModule {\n this.ensureReady();\n return this._communications;\n }\n\n // ===========================================================================\n // Public Properties - State\n // ===========================================================================\n\n /** Current identity (public info only) */\n get identity(): Identity | null {\n if (!this._identity) return null;\n return {\n chainPubkey: this._identity.chainPubkey,\n l1Address: this._identity.l1Address,\n directAddress: this._identity.directAddress,\n ipnsName: this._identity.ipnsName,\n nametag: this._identity.nametag,\n };\n }\n\n /** Is ready */\n get isReady(): boolean {\n return this._initialized;\n }\n\n // ===========================================================================\n // Public Methods - Providers Access\n // ===========================================================================\n\n getStorage(): StorageProvider {\n return this._storage;\n }\n\n /**\n * Get first token storage provider (for backward compatibility)\n * @deprecated Use getTokenStorageProviders() for multiple providers\n */\n getTokenStorage(): TokenStorageProvider<TxfStorageDataBase> | undefined {\n const providers = Array.from(this._tokenStorageProviders.values());\n return providers.length > 0 ? providers[0] : undefined;\n }\n\n /**\n * Get all token storage providers\n */\n getTokenStorageProviders(): Map<string, TokenStorageProvider<TxfStorageDataBase>> {\n return new Map(this._tokenStorageProviders);\n }\n\n /**\n * Add a token storage provider dynamically (e.g., from UI)\n * Provider will be initialized and connected automatically\n */\n async addTokenStorageProvider(provider: TokenStorageProvider<TxfStorageDataBase>): Promise<void> {\n if (this._tokenStorageProviders.has(provider.id)) {\n throw new Error(`Token storage provider '${provider.id}' already exists`);\n }\n\n // Set identity if wallet is initialized\n if (this._identity) {\n provider.setIdentity(this._identity);\n await provider.initialize();\n }\n\n this._tokenStorageProviders.set(provider.id, provider);\n\n // Update payments module with new providers\n if (this._initialized) {\n this._payments.updateTokenStorageProviders(this._tokenStorageProviders);\n }\n }\n\n /**\n * Remove a token storage provider dynamically\n */\n async removeTokenStorageProvider(providerId: string): Promise<boolean> {\n const provider = this._tokenStorageProviders.get(providerId);\n if (!provider) {\n return false;\n }\n\n // Shutdown provider gracefully\n await provider.shutdown();\n\n this._tokenStorageProviders.delete(providerId);\n\n // Update payments module\n if (this._initialized) {\n this._payments.updateTokenStorageProviders(this._tokenStorageProviders);\n }\n\n return true;\n }\n\n /**\n * Check if a token storage provider is registered\n */\n hasTokenStorageProvider(providerId: string): boolean {\n return this._tokenStorageProviders.has(providerId);\n }\n\n /**\n * Set or update the price provider after initialization\n */\n setPriceProvider(provider: PriceProvider): void {\n this._priceProvider = provider;\n this._payments.setPriceProvider(provider);\n }\n\n getTransport(): TransportProvider {\n return this._transport;\n }\n\n getAggregator(): OracleProvider {\n return this._oracle;\n }\n\n /**\n * Check if wallet has BIP32 master key for HD derivation\n */\n hasMasterKey(): boolean {\n return this._masterKey !== null;\n }\n\n // ===========================================================================\n // Public Methods - Multi-Address Derivation\n // ===========================================================================\n\n /**\n * Get the base derivation path used by this wallet (e.g., \"m/44'/0'/0'\")\n */\n getBasePath(): string {\n return this._basePath;\n }\n\n /**\n * Get the default address path (first external address)\n * Returns path like \"m/44'/0'/0'/0/0\"\n */\n getDefaultAddressPath(): string {\n return `${this._basePath}/0/0`;\n }\n\n /**\n * Get current derivation mode\n */\n getDerivationMode(): DerivationMode {\n return this._derivationMode;\n }\n\n /**\n * Get the mnemonic phrase (for backup purposes)\n * Returns null if wallet was imported from file (masterKey only)\n */\n getMnemonic(): string | null {\n return this._mnemonic;\n }\n\n /**\n * Get wallet info for backup/export purposes\n */\n getWalletInfo(): WalletInfo {\n let address0: string | null = null;\n try {\n if (this._masterKey) {\n address0 = this.deriveAddress(0).address;\n } else if (this._identity) {\n address0 = this._identity.l1Address;\n }\n } catch {\n // Ignore errors\n }\n\n return {\n source: this._source,\n hasMnemonic: this._mnemonic !== null,\n hasChainCode: !!this._masterKey?.chainCode,\n derivationMode: this._derivationMode,\n basePath: this._basePath,\n address0,\n };\n }\n\n /**\n * Export wallet to JSON format for backup\n *\n * @example\n * ```ts\n * // Export with mnemonic (if available)\n * const json = sphere.exportToJSON();\n *\n * // Export with encryption\n * const encrypted = sphere.exportToJSON({ password: 'secret' });\n *\n * // Export multiple addresses\n * const multi = sphere.exportToJSON({ addressCount: 5 });\n * ```\n */\n exportToJSON(options: WalletJSONExportOptions = {}): WalletJSON {\n this.ensureReady();\n\n if (!this._masterKey && !this._identity) {\n throw new Error('Wallet not initialized');\n }\n\n // Build addresses array\n const addressCount = options.addressCount || 1;\n const addresses: Array<{\n address: string;\n publicKey: string;\n path: string;\n index: number;\n }> = [];\n\n for (let i = 0; i < addressCount; i++) {\n try {\n const addr = this.deriveAddress(i, false);\n addresses.push({\n address: addr.address,\n publicKey: addr.publicKey,\n path: addr.path,\n index: addr.index,\n });\n } catch {\n // Stop if we can't derive more addresses (e.g., no masterKey)\n if (i === 0 && this._identity) {\n addresses.push({\n address: this._identity.l1Address,\n publicKey: this._identity.chainPubkey,\n path: this.getDefaultAddressPath(),\n index: 0,\n });\n }\n break;\n }\n }\n\n // Build wallet data\n let masterPrivateKey: string | undefined;\n let chainCode: string | undefined;\n\n if (this._masterKey) {\n masterPrivateKey = this._masterKey.privateKey;\n chainCode = this._masterKey.chainCode || undefined;\n }\n\n // Prepare mnemonic (optionally encrypt)\n let mnemonic: string | undefined;\n let encrypted = false;\n\n if (this._mnemonic && options.includeMnemonic !== false) {\n if (options.password) {\n mnemonic = encryptSimple(this._mnemonic, options.password);\n encrypted = true;\n } else {\n mnemonic = this._mnemonic;\n }\n }\n\n // Encrypt master key if password provided\n if (masterPrivateKey && options.password) {\n masterPrivateKey = encryptSimple(masterPrivateKey, options.password);\n encrypted = true;\n }\n\n return {\n version: '1.0',\n type: 'sphere-wallet',\n createdAt: new Date().toISOString(),\n wallet: {\n masterPrivateKey,\n chainCode,\n addresses,\n isBIP32: this._derivationMode === 'bip32',\n descriptorPath: this._basePath.replace(/^m\\//, ''),\n },\n mnemonic,\n encrypted,\n source: this._source,\n derivationMode: this._derivationMode,\n };\n }\n\n /**\n * Export wallet to text format for backup\n *\n * @example\n * ```ts\n * // Export unencrypted\n * const text = sphere.exportToTxt();\n *\n * // Export with encryption\n * const encrypted = sphere.exportToTxt({ password: 'secret' });\n *\n * // Export multiple addresses\n * const multi = sphere.exportToTxt({ addressCount: 5 });\n * ```\n */\n exportToTxt(options: { password?: string; addressCount?: number } = {}): string {\n this.ensureReady();\n\n if (!this._masterKey && !this._identity) {\n throw new Error('Wallet not initialized');\n }\n\n // Build addresses array\n const addressCount = options.addressCount || 1;\n const addresses: Array<{\n index: number;\n address: string;\n path: string;\n isChange: boolean;\n }> = [];\n\n for (let i = 0; i < addressCount; i++) {\n try {\n const addr = this.deriveAddress(i, false);\n addresses.push({\n address: addr.address,\n path: addr.path,\n index: addr.index,\n isChange: false,\n });\n } catch {\n // Stop if we can't derive more addresses\n if (i === 0 && this._identity) {\n addresses.push({\n address: this._identity.l1Address,\n path: this.getDefaultAddressPath(),\n index: 0,\n isChange: false,\n });\n }\n break;\n }\n }\n\n const masterPrivateKey = this._masterKey?.privateKey || '';\n const chainCode = this._masterKey?.chainCode || undefined;\n const isBIP32 = this._derivationMode === 'bip32';\n const descriptorPath = this._basePath.replace(/^m\\//, '');\n\n // If password provided, encrypt\n if (options.password) {\n const encryptedMasterKey = encryptForTextFormat(masterPrivateKey, options.password);\n return serializeEncryptedWalletToText({\n encryptedMasterKey,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n });\n }\n\n // Unencrypted export\n return serializeWalletToText({\n masterPrivateKey,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n });\n }\n\n /**\n * Import wallet from JSON backup\n *\n * @returns Object with success status and optionally recovered mnemonic\n *\n * @example\n * ```ts\n * const json = '{\"version\":\"1.0\",...}';\n * const { success, mnemonic } = await Sphere.importFromJSON({\n * jsonContent: json,\n * password: 'secret', // if encrypted\n * storage, transport, oracle,\n * });\n * ```\n */\n static async importFromJSON(options: {\n jsonContent: string;\n password?: string;\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n l1?: L1Config;\n }): Promise<{ success: boolean; mnemonic?: string; error?: string }> {\n try {\n const data = JSON.parse(options.jsonContent) as WalletJSON;\n\n if (data.version !== '1.0' || data.type !== 'sphere-wallet') {\n return { success: false, error: 'Invalid wallet format' };\n }\n\n // Decrypt if needed\n let mnemonic = data.mnemonic;\n let masterKey = data.wallet.masterPrivateKey;\n\n if (data.encrypted && options.password) {\n if (mnemonic) {\n const decrypted = decryptSimple(mnemonic, options.password);\n if (!decrypted) {\n return { success: false, error: 'Failed to decrypt mnemonic - wrong password?' };\n }\n mnemonic = decrypted;\n }\n if (masterKey) {\n const decrypted = decryptSimple(masterKey, options.password);\n if (!decrypted) {\n return { success: false, error: 'Failed to decrypt master key - wrong password?' };\n }\n masterKey = decrypted;\n }\n } else if (data.encrypted && !options.password) {\n return { success: false, error: 'Password required for encrypted wallet' };\n }\n\n // Determine base path\n const basePath = data.wallet.descriptorPath\n ? `m/${data.wallet.descriptorPath}`\n : DEFAULT_BASE_PATH;\n\n // Import using mnemonic if available (preferred)\n if (mnemonic) {\n await Sphere.import({\n mnemonic,\n basePath,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n return { success: true, mnemonic };\n }\n\n // Otherwise import using master key\n if (masterKey) {\n await Sphere.import({\n masterKey,\n chainCode: data.wallet.chainCode,\n basePath,\n derivationMode: data.derivationMode || (data.wallet.isBIP32 ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n return { success: true };\n }\n\n return { success: false, error: 'No mnemonic or master key in wallet data' };\n } catch (e) {\n return {\n success: false,\n error: e instanceof Error ? e.message : 'Failed to parse wallet JSON',\n };\n }\n }\n\n /**\n * Import wallet from legacy file (.dat, .txt, or mnemonic text)\n *\n * Supports:\n * - Bitcoin Core wallet.dat files (SQLite format, encrypted or unencrypted)\n * - Text backup files (UNICITY WALLET DETAILS format)\n * - Plain mnemonic text (12 or 24 words)\n *\n * @returns Object with success status, created Sphere instance, and optionally recovered mnemonic\n *\n * @example\n * ```ts\n * // Import from .dat file\n * const fileBuffer = await file.arrayBuffer();\n * const result = await Sphere.importFromLegacyFile({\n * fileContent: new Uint8Array(fileBuffer),\n * fileName: 'wallet.dat',\n * password: 'wallet-password', // if encrypted\n * storage, transport, oracle,\n * });\n *\n * // Import from .txt file\n * const textContent = await file.text();\n * const result = await Sphere.importFromLegacyFile({\n * fileContent: textContent,\n * fileName: 'backup.txt',\n * storage, transport, oracle,\n * });\n * ```\n */\n static async importFromLegacyFile(options: {\n /** File content - Uint8Array for .dat, string for .txt */\n fileContent: string | Uint8Array;\n /** File name (used for type detection) */\n fileName: string;\n /** Password for encrypted files */\n password?: string;\n /** Progress callback for long decryption operations */\n onDecryptProgress?: DecryptionProgressCallback;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** Optional token storage provider */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Optional nametag to register */\n nametag?: string;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n }): Promise<{\n success: boolean;\n sphere?: Sphere;\n mnemonic?: string;\n needsPassword?: boolean;\n error?: string;\n }> {\n const { fileContent, fileName, password, onDecryptProgress } = options;\n\n // Detect file type\n const fileType = Sphere.detectLegacyFileType(fileName, fileContent);\n\n if (fileType === 'unknown') {\n return { success: false, error: 'Unknown file format' };\n }\n\n // Handle mnemonic text\n if (fileType === 'mnemonic') {\n const mnemonic = (fileContent as string).trim().toLowerCase().split(/\\s+/).join(' ');\n if (!Sphere.validateMnemonic(mnemonic)) {\n return { success: false, error: 'Invalid mnemonic phrase' };\n }\n\n const sphere = await Sphere.import({\n mnemonic,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere, mnemonic };\n }\n\n // Handle .dat file\n if (fileType === 'dat') {\n const data = fileContent instanceof Uint8Array\n ? fileContent\n : new TextEncoder().encode(fileContent);\n\n let parseResult;\n\n if (password) {\n parseResult = await parseAndDecryptWalletDat(data, password, onDecryptProgress);\n } else {\n parseResult = parseWalletDat(data);\n }\n\n if (parseResult.needsPassword && !password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n\n if (!parseResult.success || !parseResult.data) {\n return { success: false, error: parseResult.error };\n }\n\n const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;\n\n // Build base path from descriptor path\n const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: derivationMode || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere };\n }\n\n // Handle .txt file\n if (fileType === 'txt') {\n const content = typeof fileContent === 'string'\n ? fileContent\n : new TextDecoder().decode(fileContent);\n\n let parseResult;\n\n if (password) {\n parseResult = parseAndDecryptWalletText(content, password);\n } else if (isTextWalletEncrypted(content)) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n } else {\n parseResult = parseWalletText(content);\n }\n\n if (parseResult.needsPassword && !password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n\n if (!parseResult.success || !parseResult.data) {\n return { success: false, error: parseResult.error };\n }\n\n const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;\n\n const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: derivationMode || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere };\n }\n\n // Handle JSON\n if (fileType === 'json') {\n const content = typeof fileContent === 'string'\n ? fileContent\n : new TextDecoder().decode(fileContent);\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content);\n } catch {\n return { success: false, error: 'Invalid JSON file' };\n }\n\n // sphere-wallet format — delegate to importFromJSON\n if (parsed.type === 'sphere-wallet') {\n const result = await Sphere.importFromJSON({\n jsonContent: content,\n password,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n\n if (result.success) {\n const sphere = Sphere.getInstance();\n return { success: true, sphere: sphere!, mnemonic: result.mnemonic };\n }\n\n if (!password && result.error?.includes('Password required')) {\n return { success: false, needsPassword: true, error: result.error };\n }\n\n return { success: false, error: result.error };\n }\n\n // Legacy flat JSON format (webwallet export)\n let masterKey: string | undefined;\n let mnemonic: string | undefined;\n\n if (parsed.encrypted && typeof parsed.encrypted === 'object') {\n // Encrypted legacy JSON — needs password + salt-based PBKDF2 decryption\n if (!password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n const enc = parsed.encrypted as { masterPrivateKey?: string; mnemonic?: string; salt?: string };\n if (!enc.salt || !enc.masterPrivateKey) {\n return { success: false, error: 'Invalid encrypted wallet format' };\n }\n const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);\n if (!decryptedKey) {\n return { success: false, error: 'Failed to decrypt - incorrect password?' };\n }\n masterKey = decryptedKey;\n if (enc.mnemonic) {\n mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? undefined;\n }\n } else {\n // Unencrypted legacy JSON\n masterKey = parsed.masterPrivateKey as string | undefined;\n mnemonic = parsed.mnemonic as string | undefined;\n }\n\n if (!masterKey) {\n return { success: false, error: 'No master key found in wallet JSON' };\n }\n\n const chainCode = parsed.chainCode as string | undefined;\n const descriptorPath = parsed.descriptorPath as string | undefined;\n const derivationMode = (parsed.derivationMode as string | undefined);\n const isBIP32 = derivationMode === 'bip32' || !!chainCode;\n const basePath = descriptorPath\n ? `m/${descriptorPath}`\n : (isBIP32 ? \"m/84'/1'/0'\" : DEFAULT_BASE_PATH);\n\n if (mnemonic) {\n const sphere = await Sphere.import({\n mnemonic,\n basePath,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n return { success: true, sphere, mnemonic };\n }\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: (derivationMode as DerivationMode) || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n return { success: true, sphere };\n }\n\n return { success: false, error: 'Unsupported file type' };\n }\n\n /**\n * Detect legacy file type from filename and content\n */\n static detectLegacyFileType(fileName: string, content: string | Uint8Array): LegacyFileType {\n // .dat files are binary\n if (fileName.endsWith('.dat')) {\n return 'dat';\n }\n\n // Check content for type detection\n const textContent = typeof content === 'string'\n ? content\n : (content.length < 1000 ? new TextDecoder().decode(content) : '');\n\n // Check for JSON\n if (fileName.endsWith('.json')) {\n return 'json';\n }\n\n try {\n const trimmed = textContent.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n JSON.parse(trimmed);\n return 'json';\n }\n } catch {\n // Not JSON\n }\n\n // Check for mnemonic (12 or 24 words)\n const words = textContent.trim().split(/\\s+/);\n if (\n (words.length === 12 || words.length === 24) &&\n words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))\n ) {\n return 'mnemonic';\n }\n\n // Check for text wallet format\n if (isWalletTextFormat(textContent)) {\n return 'txt';\n }\n\n // Check for SQLite (binary .dat)\n if (content instanceof Uint8Array && isSQLiteDatabase(content)) {\n return 'dat';\n }\n\n return 'unknown';\n }\n\n /**\n * Check if a legacy file is encrypted\n */\n static isLegacyFileEncrypted(fileName: string, content: string | Uint8Array): boolean {\n const fileType = Sphere.detectLegacyFileType(fileName, content);\n\n if (fileType === 'dat' && content instanceof Uint8Array) {\n return isWalletDatEncrypted(content);\n }\n\n if (fileType === 'txt') {\n const textContent = typeof content === 'string'\n ? content\n : new TextDecoder().decode(content);\n return isTextWalletEncrypted(textContent);\n }\n\n if (fileType === 'json') {\n try {\n const textContent = typeof content === 'string'\n ? content\n : new TextDecoder().decode(content);\n const data = JSON.parse(textContent);\n return !!data.encrypted;\n } catch {\n return false;\n }\n }\n\n return false;\n }\n\n /**\n * Get the current active address index\n *\n * @example\n * ```ts\n * const currentIndex = sphere.getCurrentAddressIndex();\n * console.log(currentIndex); // 0\n *\n * await sphere.switchToAddress(2);\n * console.log(sphere.getCurrentAddressIndex()); // 2\n * ```\n */\n getCurrentAddressIndex(): number {\n return this._currentAddressIndex;\n }\n\n /**\n * Get primary nametag for a specific address\n *\n * @param addressId - Address identifier (DIRECT://xxx), defaults to current address\n * @returns Primary nametag (index 0) or undefined if not registered\n */\n getNametagForAddress(addressId?: string): string | undefined {\n const id = addressId ?? this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (!id) return undefined;\n return this._addressNametags.get(id)?.get(0);\n }\n\n /**\n * Get all nametags for a specific address\n *\n * @param addressId - Address identifier (DIRECT://xxx), defaults to current address\n * @returns Map of nametagIndex to nametag, or undefined if no nametags\n */\n getNametagsForAddress(addressId?: string): Map<number, string> | undefined {\n const id = addressId ?? this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (!id) return undefined;\n const nametags = this._addressNametags.get(id);\n return nametags && nametags.size > 0 ? new Map(nametags) : undefined;\n }\n\n /**\n * Get all registered address nametags\n * @deprecated Use getActiveAddresses() or getAllTrackedAddresses() instead\n * @returns Map of addressId to (nametagIndex -> nametag)\n */\n getAllAddressNametags(): Map<string, Map<number, string>> {\n const result = new Map<string, Map<number, string>>();\n for (const [addressId, nametags] of this._addressNametags.entries()) {\n if (nametags.size > 0) {\n result.set(addressId, new Map(nametags));\n }\n }\n return result;\n }\n\n /**\n * Get all active (non-hidden) tracked addresses.\n * Returns addresses that have been activated through create, switchToAddress,\n * registerNametag, or nametag recovery.\n *\n * @returns Array of TrackedAddress entries sorted by index, excluding hidden ones\n */\n getActiveAddresses(): TrackedAddress[] {\n this.ensureReady();\n const result: TrackedAddress[] = [];\n for (const entry of this._trackedAddresses.values()) {\n if (!entry.hidden) {\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n result.push({ ...entry, nametag });\n }\n }\n return result.sort((a, b) => a.index - b.index);\n }\n\n /**\n * Get all tracked addresses, including hidden ones.\n *\n * @returns Array of all TrackedAddress entries sorted by index\n */\n getAllTrackedAddresses(): TrackedAddress[] {\n this.ensureReady();\n const result: TrackedAddress[] = [];\n for (const entry of this._trackedAddresses.values()) {\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n result.push({ ...entry, nametag });\n }\n return result.sort((a, b) => a.index - b.index);\n }\n\n /**\n * Get tracked address info by index.\n *\n * @param index - Address index\n * @returns TrackedAddress or undefined if not tracked\n */\n getTrackedAddress(index: number): TrackedAddress | undefined {\n this.ensureReady();\n const entry = this._trackedAddresses.get(index);\n if (!entry) return undefined;\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n return { ...entry, nametag };\n }\n\n /**\n * Set visibility of a tracked address.\n * Hidden addresses are not returned by getActiveAddresses() but remain tracked.\n *\n * @param index - Address index to hide/unhide\n * @param hidden - true to hide, false to show\n * @throws Error if address index is not tracked\n */\n async setAddressHidden(index: number, hidden: boolean): Promise<void> {\n this.ensureReady();\n const entry = this._trackedAddresses.get(index);\n if (!entry) {\n throw new Error(`Address at index ${index} is not tracked. Switch to it first.`);\n }\n if (entry.hidden === hidden) return;\n\n (entry as { hidden: boolean }).hidden = hidden;\n await this.persistTrackedAddresses();\n\n const eventType = hidden ? 'address:hidden' : 'address:unhidden';\n this.emitEvent(eventType, { index, addressId: entry.addressId });\n }\n\n /**\n * Switch to a different address by index\n * This changes the active identity to the derived address at the specified index.\n *\n * @param index - Address index to switch to (0, 1, 2, ...)\n *\n * @example\n * ```ts\n * // Switch to second address\n * await sphere.switchToAddress(1);\n * console.log(sphere.identity?.address); // alpha1... (address at index 1)\n *\n * // Register nametag for this address\n * await sphere.registerNametag('bob');\n *\n * // Switch back to first address\n * await sphere.switchToAddress(0);\n * ```\n */\n async switchToAddress(index: number, options?: { nametag?: string }): Promise<void> {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code. Cannot switch addresses.');\n }\n\n if (index < 0) {\n throw new Error('Address index must be non-negative');\n }\n\n // If nametag requested, validate format early\n const newNametag = options?.nametag?.startsWith('@')\n ? options.nametag.slice(1)\n : options?.nametag;\n if (newNametag && !this.validateNametag(newNametag)) {\n throw new Error('Invalid nametag format. Use alphanumeric characters, 3-20 chars.');\n }\n\n // Derive the address at the given index\n const addressInfo = this.deriveAddress(index, false);\n\n // Generate IPNS name from public key hash\n const ipnsHash = sha256(addressInfo.publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);\n\n // Ensure address is tracked in the registry\n await this.ensureAddressTracked(index);\n const addressId = getAddressId(predicateAddress);\n\n // If nametag requested, check availability and store it BEFORE building identity\n if (newNametag) {\n const existing = await this._transport.resolveNametag?.(newNametag);\n if (existing) {\n throw new Error(`Nametag @${newNametag} is already taken`);\n }\n\n // Pre-populate nametag cache so identity is built WITH nametag\n let nametags = this._addressNametags.get(addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(addressId, nametags);\n }\n nametags.set(0, newNametag);\n }\n\n const nametag = this._addressNametags.get(addressId)?.get(0);\n\n // Update identity\n this._identity = {\n privateKey: addressInfo.privateKey,\n chainPubkey: addressInfo.publicKey,\n l1Address: addressInfo.address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n nametag,\n };\n\n // Update current index\n this._currentAddressIndex = index;\n await this._updateCachedProxyAddress();\n\n // Persist current index\n await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());\n\n // Re-initialize providers with new identity\n this._storage.setIdentity(this._identity);\n await this._transport.setIdentity(this._identity);\n\n // Update token storage providers and re-open databases for new address\n for (const provider of this._tokenStorageProviders.values()) {\n provider.setIdentity(this._identity);\n await provider.initialize();\n }\n\n // Re-initialize modules with new identity\n await this.reinitializeModulesForNewAddress();\n\n // Publish identity binding (with nametag if present — single atomic publish)\n if (this._identity.nametag) {\n await this.syncIdentityWithTransport();\n }\n\n // If new nametag was registered, persist cache and mint token\n if (newNametag) {\n await this.persistAddressNametags();\n\n if (!this._payments.hasNametag()) {\n console.log(`[Sphere] Minting nametag token for @${newNametag}...`);\n try {\n const result = await this.mintNametag(newNametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed:`, err);\n }\n }\n\n this.emitEvent('nametag:registered', {\n nametag: newNametag,\n addressIndex: index,\n });\n } else if (this._identity.nametag && !this._payments.hasNametag()) {\n // Existing address with nametag but missing token — mint it\n console.log(`[Sphere] Nametag @${this._identity.nametag} has no token after switch, minting...`);\n try {\n const result = await this.mintNametag(this._identity.nametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully after switch`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token after switch: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed after switch:`, err);\n }\n }\n\n this.emitEvent('identity:changed', {\n l1Address: this._identity.l1Address,\n directAddress: this._identity.directAddress,\n chainPubkey: this._identity.chainPubkey,\n nametag: this._identity.nametag,\n addressIndex: index,\n });\n\n console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);\n }\n\n /**\n * Re-initialize modules after address switch\n */\n private async reinitializeModulesForNewAddress(): Promise<void> {\n const emitEvent = this.emitEvent.bind(this);\n\n this._payments.initialize({\n identity: this._identity!,\n storage: this._storage,\n tokenStorageProviders: this._tokenStorageProviders,\n transport: this._transport,\n oracle: this._oracle,\n emitEvent,\n chainCode: this._masterKey?.chainCode || undefined,\n price: this._priceProvider ?? undefined,\n });\n\n this._communications.initialize({\n identity: this._identity!,\n storage: this._storage,\n transport: this._transport,\n emitEvent,\n });\n\n await this._payments.load();\n await this._communications.load();\n }\n\n /**\n * Derive address at a specific index\n *\n * @param index - Address index (0, 1, 2, ...)\n * @param isChange - Whether this is a change address (default: false)\n * @returns Address info with privateKey, publicKey, address, path, index\n *\n * @example\n * ```ts\n * // Derive first receiving address\n * const addr0 = sphere.deriveAddress(0);\n * console.log(addr0.address); // alpha1...\n *\n * // Derive second receiving address\n * const addr1 = sphere.deriveAddress(1);\n *\n * // Derive change address\n * const change = sphere.deriveAddress(0, true);\n * ```\n */\n deriveAddress(index: number, isChange: boolean = false): AddressInfo {\n this.ensureReady();\n return this._deriveAddressInternal(index, isChange);\n }\n\n /**\n * Internal address derivation without ensureReady() check.\n * Used during initialization (loadTrackedAddresses, ensureAddressTracked)\n * when _initialized is still false.\n */\n private _deriveAddressInternal(index: number, isChange: boolean = false): AddressInfo {\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code');\n }\n\n // WIF/HMAC mode: legacy HMAC-SHA512 derivation (no chain code, no change addresses)\n if (this._derivationMode === 'wif_hmac') {\n return generateAddressFromMasterKey(this._masterKey.privateKey, index);\n }\n\n const info = deriveAddressInfo(\n this._masterKey,\n this._basePath,\n index,\n isChange\n );\n\n // Convert to proper bech32 address format\n return {\n ...info,\n address: publicKeyToAddress(info.publicKey, 'alpha'),\n };\n }\n\n /**\n * Derive address at a full BIP32 path\n *\n * @param path - Full BIP32 path like \"m/44'/0'/0'/0/5\"\n * @returns Address info\n *\n * @example\n * ```ts\n * const addr = sphere.deriveAddressAtPath(\"m/44'/0'/0'/0/5\");\n * ```\n */\n deriveAddressAtPath(path: string): AddressInfo {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code');\n }\n\n // Parse path to extract index\n const match = path.match(/\\/(\\d+)$/);\n const index = match ? parseInt(match[1], 10) : 0;\n\n const derived = deriveKeyAtPath(\n this._masterKey.privateKey,\n this._masterKey.chainCode,\n path\n );\n\n const publicKey = getPublicKey(derived.privateKey);\n\n return {\n privateKey: derived.privateKey,\n publicKey,\n address: publicKeyToAddress(publicKey, 'alpha'),\n path,\n index,\n };\n }\n\n /**\n * Derive multiple addresses starting from index 0\n *\n * @param count - Number of addresses to derive\n * @param includeChange - Include change addresses (default: false)\n * @returns Array of address info\n *\n * @example\n * ```ts\n * // Get first 5 receiving addresses\n * const addresses = sphere.deriveAddresses(5);\n *\n * // Get 5 receiving + 5 change addresses\n * const allAddresses = sphere.deriveAddresses(5, true);\n * ```\n */\n deriveAddresses(count: number, includeChange: boolean = false): AddressInfo[] {\n const addresses: AddressInfo[] = [];\n\n for (let i = 0; i < count; i++) {\n addresses.push(this.deriveAddress(i, false));\n }\n\n if (includeChange) {\n for (let i = 0; i < count; i++) {\n addresses.push(this.deriveAddress(i, true));\n }\n }\n\n return addresses;\n }\n\n /**\n * Scan blockchain addresses to discover used addresses with balances.\n * Derives addresses sequentially and checks L1 balance via Fulcrum.\n * Uses gap limit to stop after N consecutive empty addresses.\n *\n * @param options - Scanning options\n * @returns Scan results with found addresses and total balance\n *\n * @example\n * ```ts\n * const result = await sphere.scanAddresses({\n * maxAddresses: 100,\n * gapLimit: 20,\n * onProgress: (p) => console.log(`Scanned ${p.scanned}/${p.total}, found ${p.foundCount}`),\n * });\n * console.log(`Found ${result.addresses.length} addresses, total: ${result.totalBalance} ALPHA`);\n * ```\n */\n async scanAddresses(options: ScanAddressesOptions = {}): Promise<ScanAddressesResult> {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('Address scanning requires HD master key');\n }\n\n // Auto-provide nametag resolver from transport if caller didn't supply one\n const resolveNametag = options.resolveNametag ?? (\n this._transport.resolveAddressInfo\n ? async (l1Address: string): Promise<string | null> => {\n try {\n const info = await this._transport.resolveAddressInfo!(l1Address);\n return info?.nametag ?? null;\n } catch { return null; }\n }\n : undefined\n );\n\n return scanAddressesImpl(\n (index, isChange) => this._deriveAddressInternal(index, isChange),\n { ...options, resolveNametag },\n );\n }\n\n /**\n * Bulk-track scanned addresses with visibility and nametag data.\n * Selected addresses get `hidden: false`, unselected get `hidden: true`.\n * Performs only 2 storage writes total (tracked addresses + nametags).\n */\n async trackScannedAddresses(\n entries: Array<{ index: number; hidden: boolean; nametag?: string }>,\n ): Promise<void> {\n this.ensureReady();\n\n for (const { index, hidden, nametag } of entries) {\n const tracked = await this.ensureAddressTracked(index);\n\n if (nametag) {\n let nametags = this._addressNametags.get(tracked.addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(tracked.addressId, nametags);\n }\n if (!nametags.has(0)) nametags.set(0, nametag);\n }\n\n if (tracked.hidden !== hidden) {\n (tracked as { hidden: boolean }).hidden = hidden;\n }\n }\n\n await this.persistTrackedAddresses();\n await this.persistAddressNametags();\n }\n\n // ===========================================================================\n // Public Methods - Status\n // ===========================================================================\n\n getStatus(): {\n storage: { connected: boolean };\n transport: { connected: boolean };\n oracle: { connected: boolean };\n } {\n return {\n storage: { connected: this._storage.isConnected() },\n transport: { connected: this._transport.isConnected() },\n oracle: { connected: this._oracle.isConnected() },\n };\n }\n\n async reconnect(): Promise<void> {\n await this._transport.disconnect();\n await this._transport.connect();\n\n this.emitEvent('connection:changed', {\n provider: 'transport',\n connected: true,\n });\n }\n\n // ===========================================================================\n // Public Methods - Events\n // ===========================================================================\n\n on<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): () => void {\n if (!this.eventHandlers.has(type)) {\n this.eventHandlers.set(type, new Set());\n }\n this.eventHandlers.get(type)!.add(handler as SphereEventHandler<SphereEventType>);\n\n return () => {\n this.eventHandlers.get(type)?.delete(handler as SphereEventHandler<SphereEventType>);\n };\n }\n\n off<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): void {\n this.eventHandlers.get(type)?.delete(handler as SphereEventHandler<SphereEventType>);\n }\n\n // ===========================================================================\n // Public Methods - Sync\n // ===========================================================================\n\n async sync(): Promise<void> {\n this.ensureReady();\n await this._payments.sync();\n }\n\n // ===========================================================================\n // Public Methods - Nametag\n // ===========================================================================\n\n /**\n * Get current nametag (if registered)\n */\n getNametag(): string | undefined {\n return this._identity?.nametag;\n }\n\n /**\n * Check if nametag is registered\n */\n hasNametag(): boolean {\n return !!this._identity?.nametag;\n }\n\n /**\n * Get the PROXY address for the current nametag\n * PROXY addresses are derived from the nametag hash and require\n * the nametag token to claim funds sent to them\n * @returns PROXY address string or undefined if no nametag\n */\n getProxyAddress(): string | undefined {\n return this._cachedProxyAddress;\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.\n *\n * @example\n * ```ts\n * const peer = await sphere.resolve('@alice');\n * const peer = await sphere.resolve('DIRECT://...');\n * const peer = await sphere.resolve('alpha1...');\n * const peer = await sphere.resolve('ab12cd...'); // 64-char hex transport pubkey\n * ```\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n this.ensureReady();\n return this._transport.resolve?.(identifier) ?? null;\n }\n\n /** Compute and cache the PROXY address from the current nametag */\n private async _updateCachedProxyAddress(): Promise<void> {\n const nametag = this._identity?.nametag;\n if (!nametag) {\n this._cachedProxyAddress = undefined;\n return;\n }\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n this._cachedProxyAddress = proxyAddr.toString();\n }\n\n /**\n * Register a nametag for the current active address\n * Each address can have its own independent nametag\n *\n * @example\n * ```ts\n * // Register nametag for first address (index 0)\n * await sphere.registerNametag('alice');\n *\n * // Switch to second address and register different nametag\n * await sphere.switchToAddress(1);\n * await sphere.registerNametag('bob');\n *\n * // Now:\n * // - Address 0 has nametag @alice\n * // - Address 1 has nametag @bob\n * ```\n */\n async registerNametag(nametag: string): Promise<void> {\n this.ensureReady();\n\n // Validate nametag format\n const cleanNametag = nametag.startsWith('@') ? nametag.slice(1) : nametag;\n if (!this.validateNametag(cleanNametag)) {\n throw new Error('Invalid nametag format. Use alphanumeric characters, 3-20 chars.');\n }\n\n // Check if current address already has a nametag\n if (this._identity?.nametag) {\n throw new Error(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`);\n }\n\n // Publish identity binding with nametag (updates existing binding event)\n if (this._transport.publishIdentityBinding) {\n const success = await this._transport.publishIdentityBinding(\n this._identity!.chainPubkey,\n this._identity!.l1Address,\n this._identity!.directAddress || '',\n cleanNametag,\n );\n if (!success) {\n throw new Error('Failed to register nametag. It may already be taken.');\n }\n }\n\n // Update identity\n this._identity!.nametag = cleanNametag;\n await this._updateCachedProxyAddress();\n\n // Update nametag cache\n const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (currentAddressId) {\n let nametags = this._addressNametags.get(currentAddressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(currentAddressId, nametags);\n }\n nametags.set(0, cleanNametag);\n }\n\n // Persist nametag cache\n await this.persistAddressNametags();\n\n // Mint nametag token on-chain if not already minted\n // Required for receiving tokens via @nametag (PROXY address finalization)\n if (!this._payments.hasNametag()) {\n console.log(`[Sphere] Minting nametag token for @${cleanNametag}...`);\n const result = await this.mintNametag(cleanNametag);\n if (!result.success) {\n console.warn(`[Sphere] Failed to mint nametag token: ${result.error}`);\n // Don't throw - nametag is published via transport, token can be minted later\n } else {\n console.log(`[Sphere] Nametag token minted successfully`);\n }\n }\n\n this.emitEvent('nametag:registered', {\n nametag: cleanNametag,\n addressIndex: this._currentAddressIndex,\n });\n console.log(`[Sphere] Nametag registered for address ${this._currentAddressIndex}:`, cleanNametag);\n }\n\n /**\n * Persist tracked addresses to storage (only minimal fields via StorageProvider)\n */\n private async persistTrackedAddresses(): Promise<void> {\n const entries: TrackedAddressEntry[] = [];\n for (const entry of this._trackedAddresses.values()) {\n entries.push({\n index: entry.index,\n hidden: entry.hidden,\n createdAt: entry.createdAt,\n updatedAt: entry.updatedAt,\n });\n }\n await this._storage.saveTrackedAddresses(entries);\n }\n\n /**\n * Mint a nametag token on-chain (like Sphere wallet and lottery)\n * This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @returns MintNametagResult with success status and token if successful\n *\n * @example\n * ```typescript\n * // Mint nametag token for receiving via @alice\n * const result = await sphere.mintNametag('alice');\n * if (result.success) {\n * console.log('Nametag minted:', result.nametagData?.name);\n * } else {\n * console.error('Mint failed:', result.error);\n * }\n * ```\n */\n async mintNametag(nametag: string): Promise<import('../modules/payments').MintNametagResult> {\n this.ensureReady();\n return this._payments.mintNametag(nametag);\n }\n\n /**\n * Check if a nametag is available for minting\n * @param nametag - The nametag to check (e.g., \"alice\" or \"@alice\")\n * @returns true if available, false if taken or error\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n this.ensureReady();\n return this._payments.isNametagAvailable(nametag);\n }\n\n /**\n * Load tracked addresses from storage.\n * Falls back to migrating from old ADDRESS_NAMETAGS format.\n */\n private async loadTrackedAddresses(): Promise<void> {\n this._trackedAddresses.clear();\n this._addressIdToIndex.clear();\n\n try {\n // Load minimal entries from storage\n const entries = await this._storage.loadTrackedAddresses();\n if (entries.length > 0) {\n for (const stored of entries) {\n // Derive address fields from index (internal: no ensureReady check)\n const addrInfo = this._deriveAddressInternal(stored.index, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n const entry: TrackedAddress = {\n ...stored,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n };\n this._trackedAddresses.set(entry.index, entry);\n this._addressIdToIndex.set(addressId, entry.index);\n }\n return;\n }\n\n // Fall back to old ADDRESS_NAMETAGS format and migrate\n const oldData = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n if (oldData) {\n const parsed = JSON.parse(oldData) as Record<string, unknown>;\n await this.migrateFromOldNametagFormat(parsed);\n await this.persistTrackedAddresses();\n }\n } catch {\n // Ignore parse errors - start fresh\n }\n }\n\n /**\n * Migrate from old ADDRESS_NAMETAGS format to tracked addresses.\n * Scans HD indices 0..19 to match addressIds from the old format.\n * Populates both _trackedAddresses and _addressNametags.\n */\n private async migrateFromOldNametagFormat(\n parsed: Record<string, unknown>\n ): Promise<void> {\n const addressIdToNametags = new Map<string, Record<string, string>>();\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'object' && value !== null) {\n addressIdToNametags.set(key, value as Record<string, string>);\n }\n }\n\n if (addressIdToNametags.size === 0 || !this._masterKey) return;\n\n const SCAN_LIMIT = 20;\n for (let i = 0; i < SCAN_LIMIT && addressIdToNametags.size > 0; i++) {\n try {\n const addrInfo = this._deriveAddressInternal(i, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n if (addressIdToNametags.has(addressId)) {\n const nametagsObj = addressIdToNametags.get(addressId)!;\n\n // Populate nametag cache\n const nametagMap = new Map<number, string>();\n for (const [idx, tag] of Object.entries(nametagsObj)) {\n nametagMap.set(parseInt(idx, 10), tag);\n }\n if (nametagMap.size > 0) {\n this._addressNametags.set(addressId, nametagMap);\n }\n\n // Create tracked address entry\n const now = Date.now();\n const entry: TrackedAddress = {\n index: i,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n nametag: nametagMap.get(0),\n hidden: false,\n createdAt: now,\n updatedAt: now,\n };\n\n this._trackedAddresses.set(i, entry);\n this._addressIdToIndex.set(addressId, i);\n addressIdToNametags.delete(addressId);\n }\n } catch {\n // Skip indices that fail to derive\n }\n }\n\n // Persist nametag cache separately\n await this.persistAddressNametags();\n }\n\n /**\n * Ensure an address is tracked in the registry.\n * If not yet tracked, derives full info and creates the entry.\n */\n private async ensureAddressTracked(index: number): Promise<TrackedAddress> {\n const existing = this._trackedAddresses.get(index);\n if (existing) return existing;\n\n const addrInfo = this._deriveAddressInternal(index, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n const now = Date.now();\n const nametag = this._addressNametags.get(addressId)?.get(0);\n const entry: TrackedAddress = {\n index,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n nametag,\n hidden: false,\n createdAt: now,\n updatedAt: now,\n };\n\n this._trackedAddresses.set(index, entry);\n this._addressIdToIndex.set(addressId, index);\n await this.persistTrackedAddresses();\n\n this.emitEvent('address:activated', { address: { ...entry } });\n return entry;\n }\n\n /**\n * Persist nametag cache to storage.\n * Format: { addressId: { \"0\": \"alice\", \"1\": \"alice2\" } }\n */\n private async persistAddressNametags(): Promise<void> {\n const result: Record<string, Record<string, string>> = {};\n for (const [addressId, nametags] of this._addressNametags.entries()) {\n const obj: Record<string, string> = {};\n for (const [idx, tag] of nametags.entries()) {\n obj[idx.toString()] = tag;\n }\n result[addressId] = obj;\n }\n await this._storage.set(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS, JSON.stringify(result));\n }\n\n /**\n * Load nametag cache from storage.\n */\n private async loadAddressNametags(): Promise<void> {\n this._addressNametags.clear();\n try {\n const data = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n if (!data) return;\n const parsed = JSON.parse(data) as Record<string, Record<string, string>>;\n for (const [addressId, nametags] of Object.entries(parsed)) {\n const map = new Map<number, string>();\n for (const [idx, tag] of Object.entries(nametags)) {\n map.set(parseInt(idx, 10), tag);\n }\n this._addressNametags.set(addressId, map);\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n /**\n * Publish identity binding via transport.\n * Always publishes base identity (chainPubkey, l1Address, directAddress).\n * If nametag is set, also publishes nametag hash, proxy address, encrypted nametag.\n */\n private async syncIdentityWithTransport(): Promise<void> {\n if (!this._transport.publishIdentityBinding) {\n return; // Transport doesn't support identity binding\n }\n\n try {\n const nametag = this._identity?.nametag;\n const success = await this._transport.publishIdentityBinding(\n this._identity!.chainPubkey,\n this._identity!.l1Address,\n this._identity!.directAddress || '',\n nametag || undefined,\n );\n if (success) {\n console.log(`[Sphere] Identity binding published${nametag ? ` with nametag @${nametag}` : ''}`);\n } else if (nametag) {\n console.warn(`[Sphere] Nametag @${nametag} is taken by another pubkey`);\n }\n } catch (error) {\n // Don't fail wallet load on identity sync errors\n console.warn(`[Sphere] Identity binding sync failed:`, error);\n }\n }\n\n /**\n * Recover nametag from transport after wallet import.\n * Searches for encrypted nametag events authored by this wallet's pubkey\n * and decrypts them to restore the nametag association.\n */\n private async recoverNametagFromTransport(): Promise<void> {\n // Skip if already has a nametag\n if (this._identity?.nametag) {\n return;\n }\n\n let recoveredNametag: string | null = null;\n\n // Strategy 1: Decrypt nametag from own Nostr binding events (private-key based)\n if (this._transport.recoverNametag) {\n try {\n recoveredNametag = await this._transport.recoverNametag();\n } catch {\n // Non-fatal — try fallback\n }\n }\n\n // Strategy 2: Forward lookup by L1 address hash (public, same as scanAddresses).\n // Covers edge cases where the encrypted binding event was lost from relay.\n if (!recoveredNametag && this._transport.resolveAddressInfo && this._identity?.l1Address) {\n try {\n const info = await this._transport.resolveAddressInfo(this._identity.l1Address);\n if (info?.nametag) {\n recoveredNametag = info.nametag;\n }\n } catch {\n // Non-fatal\n }\n }\n\n if (!recoveredNametag) {\n return;\n }\n\n try {\n // Update identity with recovered nametag\n if (this._identity) {\n (this._identity as MutableFullIdentity).nametag = recoveredNametag;\n await this._updateCachedProxyAddress();\n }\n\n // Update nametag cache\n const entry = await this.ensureAddressTracked(this._currentAddressIndex);\n let nametags = this._addressNametags.get(entry.addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(entry.addressId, nametags);\n }\n const nextIndex = nametags.size;\n nametags.set(nextIndex, recoveredNametag);\n await this.persistAddressNametags();\n\n // Note: no need to re-publish here — callers follow up with\n // syncIdentityWithTransport() which will publish WITH the recovered nametag.\n\n this.emitEvent('nametag:recovered', { nametag: recoveredNametag });\n } catch {\n // Don't fail wallet import on nametag recovery errors\n }\n }\n\n /**\n * Validate nametag format\n */\n private validateNametag(nametag: string): boolean {\n // Alphanumeric characters, underscores and hyphens allowed\n const pattern = new RegExp(\n `^[a-zA-Z0-9_-]{${LIMITS.NAMETAG_MIN_LENGTH},${LIMITS.NAMETAG_MAX_LENGTH}}$`\n );\n return pattern.test(nametag);\n }\n\n // ===========================================================================\n // Public Methods - Lifecycle\n // ===========================================================================\n\n async destroy(): Promise<void> {\n this._payments.destroy();\n this._communications.destroy();\n\n await this._transport.disconnect();\n await this._storage.disconnect();\n await this._oracle.disconnect();\n\n this._initialized = false;\n this._identity = null;\n this._trackedAddresses.clear();\n this._addressIdToIndex.clear();\n this._addressNametags.clear();\n this.eventHandlers.clear();\n\n if (Sphere.instance === this) {\n Sphere.instance = null;\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async storeMnemonic(mnemonic: string, derivationPath?: string, basePath?: string): Promise<void> {\n // TODO: Encrypt with user password/PIN\n const encrypted = this.encrypt(mnemonic);\n await this._storage.set(STORAGE_KEYS_GLOBAL.MNEMONIC, encrypted);\n\n // Store mnemonic in memory for getMnemonic()\n this._mnemonic = mnemonic;\n this._source = 'mnemonic';\n this._derivationMode = 'bip32';\n\n if (derivationPath) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);\n }\n\n const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;\n this._basePath = effectiveBasePath;\n await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);\n // Note: WALLET_EXISTS is set in finalizeWalletCreation() after successful initialization\n }\n\n private async storeMasterKey(\n masterKey: string,\n chainCode?: string,\n derivationPath?: string,\n basePath?: string,\n derivationMode?: DerivationMode\n ): Promise<void> {\n const encrypted = this.encrypt(masterKey);\n await this._storage.set(STORAGE_KEYS_GLOBAL.MASTER_KEY, encrypted);\n\n // Set source and derivation mode\n this._source = 'file';\n this._mnemonic = null;\n\n // Determine derivation mode from chain code if not specified\n if (derivationMode) {\n this._derivationMode = derivationMode;\n } else {\n this._derivationMode = chainCode ? 'bip32' : 'wif_hmac';\n }\n\n if (chainCode) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.CHAIN_CODE, chainCode);\n }\n\n if (derivationPath) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);\n }\n\n const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;\n this._basePath = effectiveBasePath;\n await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);\n // Note: WALLET_EXISTS is set in finalizeWalletCreation() after successful initialization\n }\n\n /**\n * Mark wallet as fully created (after successful initialization)\n * This is called at the end of create()/import() to ensure wallet is only\n * marked as existing after all initialization steps succeed.\n */\n private async finalizeWalletCreation(): Promise<void> {\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_EXISTS, 'true');\n }\n\n // ===========================================================================\n // Private: Identity Initialization\n // ===========================================================================\n\n private async loadIdentityFromStorage(): Promise<void> {\n // Load keys that are saved with 'default' address (before identity is set)\n const encryptedMnemonic = await this._storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);\n const encryptedMasterKey = await this._storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n const chainCode = await this._storage.get(STORAGE_KEYS_GLOBAL.CHAIN_CODE);\n const derivationPath = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);\n const savedBasePath = await this._storage.get(STORAGE_KEYS_GLOBAL.BASE_PATH);\n const savedDerivationMode = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);\n const savedSource = await this._storage.get(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);\n const savedAddressIndex = await this._storage.get(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX);\n\n // Restore wallet metadata\n this._basePath = savedBasePath ?? DEFAULT_BASE_PATH;\n this._derivationMode = (savedDerivationMode as DerivationMode) ?? 'bip32';\n this._source = (savedSource as WalletSource) ?? 'unknown';\n this._currentAddressIndex = savedAddressIndex ? parseInt(savedAddressIndex, 10) : 0;\n\n if (encryptedMnemonic) {\n const mnemonic = this.decrypt(encryptedMnemonic);\n if (!mnemonic) {\n throw new Error('Failed to decrypt mnemonic');\n }\n this._mnemonic = mnemonic;\n this._source = 'mnemonic';\n await this.initializeIdentityFromMnemonic(mnemonic, derivationPath ?? undefined);\n } else if (encryptedMasterKey) {\n const masterKey = this.decrypt(encryptedMasterKey);\n if (!masterKey) {\n throw new Error('Failed to decrypt master key');\n }\n this._mnemonic = null;\n if (this._source === 'unknown') {\n this._source = 'file';\n }\n await this.initializeIdentityFromMasterKey(\n masterKey,\n chainCode ?? undefined,\n derivationPath ?? undefined\n );\n } else {\n throw new Error('No wallet data found in storage');\n }\n\n // Now that identity is restored, set it on storage so subsequent reads use correct address\n if (this._identity) {\n this._storage.setIdentity(this._identity);\n }\n\n // Load tracked addresses registry (with migration from old format)\n await this.loadTrackedAddresses();\n // Load nametag cache\n await this.loadAddressNametags();\n\n // Ensure current address is tracked\n const trackedEntry = await this.ensureAddressTracked(this._currentAddressIndex);\n const nametag = this._addressNametags.get(trackedEntry.addressId)?.get(0);\n\n // If we have a saved address index > 0 and master key, re-derive identity\n if (this._currentAddressIndex > 0 && this._masterKey) {\n const addressInfo = this._deriveAddressInternal(this._currentAddressIndex, false);\n const ipnsHash = sha256(addressInfo.publicKey, 'hex').slice(0, 40);\n const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);\n\n this._identity = {\n privateKey: addressInfo.privateKey,\n chainPubkey: addressInfo.publicKey,\n l1Address: addressInfo.address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n nametag,\n };\n this._storage.setIdentity(this._identity);\n console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);\n } else if (this._identity && nametag) {\n // Restore nametag from cache\n this._identity.nametag = nametag;\n }\n await this._updateCachedProxyAddress();\n }\n\n private async initializeIdentityFromMnemonic(\n mnemonic: string,\n derivationPath?: string\n ): Promise<void> {\n // Use base path (e.g., m/44'/0'/0') and append chain/index\n const basePath = derivationPath ?? DEFAULT_BASE_PATH;\n const fullPath = `${basePath}/0/0`;\n\n // Generate master key from mnemonic using BIP39/BIP32\n const masterKey = identityFromMnemonicSync(mnemonic);\n\n // Derive key at full path (e.g., m/44'/0'/0'/0/0)\n const derivedKey = deriveKeyAtPath(\n masterKey.privateKey,\n masterKey.chainCode,\n fullPath\n );\n\n // Get public key from derived private key\n const publicKey = getPublicKey(derivedKey.privateKey);\n\n // Generate proper bech32 address\n const address = publicKeyToAddress(publicKey, 'alpha');\n\n // Generate IPNS name from public key hash\n const ipnsHash = sha256(publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(derivedKey.privateKey);\n\n this._identity = {\n privateKey: derivedKey.privateKey,\n chainPubkey: publicKey,\n l1Address: address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n };\n\n // Store master key info for future derivations\n this._masterKey = masterKey;\n }\n\n private async initializeIdentityFromMasterKey(\n masterKey: string,\n chainCode?: string,\n _derivationPath?: string\n ): Promise<void> {\n // Use _basePath (already set by storeMasterKey) for consistency with deriveAddress/scan.\n // Previously used derivationPath param which was undefined for file imports,\n // causing identity to derive at DEFAULT_BASE_PATH instead of the wallet's actual path.\n const basePath = this._basePath;\n const fullPath = `${basePath}/0/0`;\n\n let privateKey: string;\n\n if (chainCode) {\n // Full BIP32 derivation with chain code\n const derivedKey = deriveKeyAtPath(masterKey, chainCode, fullPath);\n privateKey = derivedKey.privateKey;\n\n this._masterKey = {\n privateKey: masterKey,\n chainCode,\n };\n } else {\n // WIF/HMAC derivation without chain code\n // Uses HMAC-SHA512(masterKey, path) to derive child keys (legacy webwallet format)\n const addr0 = generateAddressFromMasterKey(masterKey, 0);\n privateKey = addr0.privateKey;\n\n // Store masterKey for future deriveAddress() calls (chainCode unused in wif_hmac mode)\n this._masterKey = {\n privateKey: masterKey,\n chainCode: '',\n };\n }\n\n const publicKey = getPublicKey(privateKey);\n const address = publicKeyToAddress(publicKey, 'alpha');\n const ipnsHash = sha256(publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(privateKey);\n\n this._identity = {\n privateKey,\n chainPubkey: publicKey,\n l1Address: address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n };\n }\n\n // ===========================================================================\n // Private: Provider & Module Initialization\n // ===========================================================================\n\n private async initializeProviders(): Promise<void> {\n // Set identity on providers\n this._storage.setIdentity(this._identity!);\n await this._transport.setIdentity(this._identity!);\n\n // Set identity on all token storage providers\n for (const provider of this._tokenStorageProviders.values()) {\n provider.setIdentity(this._identity!);\n }\n\n // Connect providers (skip if already connected, e.g. after setIdentity reconnect)\n if (!this._storage.isConnected()) {\n await this._storage.connect();\n }\n if (!this._transport.isConnected()) {\n await this._transport.connect();\n }\n await this._oracle.initialize();\n\n // Initialize all token storage providers\n for (const provider of this._tokenStorageProviders.values()) {\n await provider.initialize();\n }\n }\n\n private async initializeModules(): Promise<void> {\n const emitEvent = this.emitEvent.bind(this);\n\n this._payments.initialize({\n identity: this._identity!,\n storage: this._storage,\n tokenStorageProviders: this._tokenStorageProviders,\n transport: this._transport,\n oracle: this._oracle,\n emitEvent,\n // Pass chain code for L1 HD derivation\n chainCode: this._masterKey?.chainCode || undefined,\n price: this._priceProvider ?? undefined,\n });\n\n this._communications.initialize({\n identity: this._identity!,\n storage: this._storage,\n transport: this._transport,\n emitEvent,\n });\n\n await this._payments.load();\n await this._communications.load();\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureReady(): void {\n if (!this._initialized) {\n throw new Error('Sphere not initialized');\n }\n }\n\n private emitEvent<T extends SphereEventType>(type: T, data: SphereEventMap[T]): void {\n const handlers = this.eventHandlers.get(type);\n if (!handlers) return;\n\n for (const handler of handlers) {\n try {\n (handler as SphereEventHandler<T>)(data);\n } catch (error) {\n console.error('[Sphere] Event handler error:', error);\n }\n }\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private encrypt(data: string): string {\n // Use AES-256 encryption with default key\n // TODO: Add password parameter to create/load for user-provided encryption\n return encryptSimple(data, DEFAULT_ENCRYPTION_KEY);\n }\n\n private decrypt(encrypted: string): string | null {\n try {\n return decryptSimple(encrypted, DEFAULT_ENCRYPTION_KEY);\n } catch {\n return null;\n }\n }\n}\n\n// =============================================================================\n// Convenience Exports\n// =============================================================================\n\nexport const createSphere = Sphere.create.bind(Sphere);\nexport const loadSphere = Sphere.load.bind(Sphere);\nexport const importSphere = Sphere.import.bind(Sphere);\nexport const initSphere = Sphere.init.bind(Sphere);\nexport const getSphere = Sphere.getInstance.bind(Sphere);\nexport const sphereExists = Sphere.exists.bind(Sphere);\n","/**\n * Currency Utilities\n * Conversion between human-readable amounts and smallest units (bigint)\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default token decimals (18 for most tokens) */\nexport const DEFAULT_TOKEN_DECIMALS = 18;\n\n// =============================================================================\n// Conversion Functions\n// =============================================================================\n\n/**\n * Convert human-readable amount to smallest unit (bigint)\n *\n * @example\n * ```ts\n * toSmallestUnit('1.5', 18) // 1500000000000000000n\n * toSmallestUnit('100', 6) // 100000000n\n * ```\n */\nexport function toSmallestUnit(amount: number | string, decimals: number = DEFAULT_TOKEN_DECIMALS): bigint {\n if (!amount) return 0n;\n\n try {\n const str = amount.toString();\n const [integer, fraction = ''] = str.split('.');\n\n // Pad fraction to exact decimal places, truncate if longer\n const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals);\n\n return BigInt(integer + paddedFraction);\n } catch {\n return 0n;\n }\n}\n\n/**\n * Convert smallest unit (bigint) to human-readable string\n *\n * @example\n * ```ts\n * toHumanReadable(1500000000000000000n, 18) // '1.5'\n * toHumanReadable(100000000n, 6) // '100'\n * ```\n */\nexport function toHumanReadable(amount: bigint | string, decimals: number = DEFAULT_TOKEN_DECIMALS): string {\n const str = amount.toString().padStart(decimals + 1, '0');\n const integer = str.slice(0, -decimals) || '0';\n const fraction = str.slice(-decimals).replace(/0+$/, '');\n\n return fraction ? `${integer}.${fraction}` : integer;\n}\n\n/**\n * Format amount for display with optional symbol\n *\n * @example\n * ```ts\n * formatAmount(1500000000000000000n, { decimals: 18, symbol: 'ALPHA' })\n * // '1.5 ALPHA'\n * ```\n */\nexport function formatAmount(\n amount: bigint | string,\n options: {\n decimals?: number;\n symbol?: string;\n maxFractionDigits?: number;\n } = {}\n): string {\n const { decimals = DEFAULT_TOKEN_DECIMALS, symbol, maxFractionDigits } = options;\n\n let readable = toHumanReadable(amount, decimals);\n\n // Limit fraction digits if specified\n if (maxFractionDigits !== undefined) {\n const [int, frac] = readable.split('.');\n if (frac && frac.length > maxFractionDigits) {\n readable = maxFractionDigits > 0 ? `${int}.${frac.slice(0, maxFractionDigits)}` : int;\n }\n }\n\n return symbol ? `${readable} ${symbol}` : readable;\n}\n\n// =============================================================================\n// Export as namespace for convenience\n// =============================================================================\n\nexport const CurrencyUtils = {\n toSmallestUnit,\n toHumanReadable,\n format: formatAmount,\n};\n","export * from './Sphere';\nexport * from './scan';\nexport * from './crypto';\nexport * from './encryption';\nexport * from './currency';\nexport * from './bech32';\nexport * from './utils';\n"],"mappings":"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","names":["CryptoJS","generateMnemonic","validateMnemonic","mnemonicToSeed","mnemonicToSeedSync","mnemonicToEntropy","entropyToMnemonic","bytesToHex","CryptoJS","CryptoJS","CryptoJS","CryptoJS","elliptic","ec","elliptic","CryptoJS","CoinId","sha256","Token","TokenId","TokenState","HashAlgorithm","UnmaskedPredicate","waitInclusionProof","DEFAULT_DERIVATION_PATH","bytesToHex","Token","TokenId","TokenState","CoinId","TokenCoinData","TokenSplitBuilder","HashAlgorithm","UnmaskedPredicate","UnmaskedPredicateReference","TransferCommitment","waitInclusionProof","sha256","toHex","fromHex","Token","TokenState","TokenType","HashAlgorithm","UnmaskedPredicate","TransferCommitment","MintCommitment","MintTransactionData","waitInclusionProof","fromHex","MintTransactionData","MintCommitment","waitInclusionProof","TokenType","Token","TransferCommitment","UnmaskedPredicate","HashAlgorithm","TokenState","tokenJson","SdkToken","CoinId","TransferCommitment","TransferTransaction","UnmaskedPredicate","TokenState","HashAlgorithm","pending","result","requestId","UnmaskedPredicateReference","TokenType","UNICITY_TOKEN_TYPE_HEX","token","TokenId","TokenState","TokenType","CoinId","HashAlgorithm","UnmaskedPredicate","broadcast","CryptoJS","encrypt","decrypt","connect","getBalance","CryptoJS","CryptoJS","CryptoJS","bytesToHex","CryptoJS","SigningService","TokenType","HashAlgorithm","UnmaskedPredicateReference","UNICITY_TOKEN_TYPE_HEX","sphere","validateMnemonic","generateMnemonic"]}
1
+ {"version":3,"sources":["../../core/bech32.ts","../../l1/addressToScriptHash.ts","../../l1/network.ts","../../l1/index.ts","../../core/crypto.ts","../../l1/crypto.ts","../../l1/address.ts","../../l1/tx.ts","../../l1/vesting.ts","../../l1/vestingState.ts","../../l1/addressHelpers.ts","../../modules/payments/L1PaymentsModule.ts","../../modules/payments/TokenSplitCalculator.ts","../../modules/payments/TokenSplitExecutor.ts","../../modules/payments/NametagMinter.ts","../../constants.ts","../../types/txf.ts","../../registry/token-registry.testnet.json","../../registry/TokenRegistry.ts","../../serialization/txf-serializer.ts","../../modules/payments/InstantSplitExecutor.ts","../../modules/payments/InstantSplitProcessor.ts","../../types/instant-split.ts","../../modules/payments/PaymentsModule.ts","../../modules/payments/TokenRecoveryService.ts","../../modules/communications/CommunicationsModule.ts","../../modules/groupchat/GroupChatModule.ts","../../modules/groupchat/types.ts","../../core/encryption.ts","../../core/scan.ts","../../serialization/wallet-text.ts","../../core/utils.ts","../../serialization/wallet-dat.ts","../../core/Sphere.ts","../../core/currency.ts","../../core/index.ts"],"sourcesContent":["/**\n * Bech32 Encoding/Decoding\n * BIP-173 implementation for address encoding\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Bech32 character set from BIP-173 */\nexport const CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\n/** Generator polynomial for checksum */\nconst GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];\n\n// =============================================================================\n// Bit Conversion\n// =============================================================================\n\n/**\n * Convert between bit arrays (8→5 or 5→8)\n */\nexport function convertBits(\n data: number[],\n fromBits: number,\n toBits: number,\n pad: boolean\n): number[] | null {\n let acc = 0;\n let bits = 0;\n const ret: number[] = [];\n const maxv = (1 << toBits) - 1;\n\n for (let i = 0; i < data.length; i++) {\n const value = data[i];\n if (value < 0 || value >> fromBits !== 0) return null;\n acc = (acc << fromBits) | value;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n ret.push((acc >> bits) & maxv);\n }\n }\n\n if (pad) {\n if (bits > 0) {\n ret.push((acc << (toBits - bits)) & maxv);\n }\n } else if (bits >= fromBits || (acc << (toBits - bits)) & maxv) {\n return null;\n }\n\n return ret;\n}\n\n// =============================================================================\n// Internal Functions\n// =============================================================================\n\n/**\n * Expand HRP for checksum calculation\n */\nfunction hrpExpand(hrp: string): number[] {\n const ret: number[] = [];\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);\n ret.push(0);\n for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);\n return ret;\n}\n\n/**\n * Calculate polymod checksum\n */\nfunction bech32Polymod(values: number[]): number {\n let chk = 1;\n for (let p = 0; p < values.length; p++) {\n const top = chk >> 25;\n chk = ((chk & 0x1ffffff) << 5) ^ values[p];\n for (let i = 0; i < 5; i++) {\n if ((top >> i) & 1) chk ^= GENERATOR[i];\n }\n }\n return chk;\n}\n\n/**\n * Create checksum for bech32\n */\nfunction bech32Checksum(hrp: string, data: number[]): number[] {\n const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);\n const mod = bech32Polymod(values) ^ 1;\n\n const ret: number[] = [];\n for (let p = 0; p < 6; p++) {\n ret.push((mod >> (5 * (5 - p))) & 31);\n }\n return ret;\n}\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Encode data to bech32 address\n *\n * @example\n * ```ts\n * const address = encodeBech32('alpha', 1, pubkeyHash);\n * // 'alpha1qw...'\n * ```\n */\nexport function encodeBech32(\n hrp: string,\n version: number,\n program: Uint8Array\n): string {\n if (version < 0 || version > 16) {\n throw new Error('Invalid witness version');\n }\n\n const converted = convertBits(Array.from(program), 8, 5, true);\n if (!converted) {\n throw new Error('Failed to convert bits');\n }\n\n const data = [version].concat(converted);\n const checksum = bech32Checksum(hrp, data);\n const combined = data.concat(checksum);\n\n let out = hrp + '1';\n for (let i = 0; i < combined.length; i++) {\n out += CHARSET[combined[i]];\n }\n\n return out;\n}\n\n/**\n * Decode bech32 address\n *\n * @example\n * ```ts\n * const result = decodeBech32('alpha1qw...');\n * // { hrp: 'alpha', witnessVersion: 1, data: Uint8Array }\n * ```\n */\nexport function decodeBech32(\n addr: string\n): { hrp: string; witnessVersion: number; data: Uint8Array } | null {\n addr = addr.toLowerCase();\n\n const pos = addr.lastIndexOf('1');\n if (pos < 1) return null;\n\n const hrp = addr.substring(0, pos);\n const dataStr = addr.substring(pos + 1);\n\n const data: number[] = [];\n for (let i = 0; i < dataStr.length; i++) {\n const val = CHARSET.indexOf(dataStr[i]);\n if (val === -1) return null;\n data.push(val);\n }\n\n // Validate checksum\n const checksum = bech32Checksum(hrp, data.slice(0, -6));\n for (let i = 0; i < 6; i++) {\n if (checksum[i] !== data[data.length - 6 + i]) {\n return null;\n }\n }\n\n const version = data[0];\n const program = convertBits(data.slice(1, -6), 5, 8, false);\n if (!program) return null;\n\n return {\n hrp,\n witnessVersion: version,\n data: Uint8Array.from(program),\n };\n}\n\n/**\n * Create address from public key hash\n *\n * @example\n * ```ts\n * const address = createAddress('alpha', pubkeyHash);\n * // 'alpha1...'\n * ```\n */\nexport function createAddress(hrp: string, pubkeyHash: Uint8Array | string): string {\n const hashBytes = typeof pubkeyHash === 'string'\n ? Uint8Array.from(Buffer.from(pubkeyHash, 'hex'))\n : pubkeyHash;\n\n return encodeBech32(hrp, 1, hashBytes);\n}\n\n/**\n * Validate bech32 address\n */\nexport function isValidBech32(addr: string): boolean {\n return decodeBech32(addr) !== null;\n}\n\n/**\n * Get HRP from address\n */\nexport function getAddressHrp(addr: string): string | null {\n const result = decodeBech32(addr);\n return result?.hrp ?? null;\n}\n\n// =============================================================================\n// Aliases for L1 SDK compatibility\n// =============================================================================\n\n/**\n * Alias for encodeBech32 (L1 SDK compatibility)\n */\nexport const createBech32 = encodeBech32;\n","import { decodeBech32 } from \"../core/bech32\";\nimport CryptoJS from \"crypto-js\";\n\n/** Convert bytes to hex */\nfunction bytesToHex(buf: Uint8Array) {\n return Array.from(buf)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Convert \"alpha1xxxx\" Bech32 → Electrum script hash\n * Required for:\n * - blockchain.scripthash.get_history\n * - blockchain.scripthash.listunspent\n */\nexport function addressToScriptHash(address: string): string {\n const decoded = decodeBech32(address);\n if (!decoded) throw new Error(\"Invalid bech32 address: \" + address);\n\n // witness program always starts with OP_0 + PUSH20 (for P2WPKH)\n const scriptHex = \"0014\" + bytesToHex(decoded.data);\n\n // SHA256\n const sha = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(scriptHex)).toString();\n\n // Electrum requires reversed byte order\n return sha.match(/../g)!.reverse().join(\"\");\n}\n","// L1 Network - Fulcrum WebSocket client\n\nimport { addressToScriptHash } from './addressToScriptHash';\nimport type { UTXO } from './types';\n\nconst DEFAULT_ENDPOINT = 'wss://fulcrum.unicity.network:50004';\n\ninterface PendingRequest {\n resolve: (result: unknown) => void;\n reject: (err: unknown) => void;\n}\n\nexport interface BlockHeader {\n height: number;\n hex: string;\n [key: string]: unknown;\n}\n\ninterface BalanceResult {\n confirmed: number;\n unconfirmed: number;\n}\n\nlet ws: WebSocket | null = null;\nlet isConnected = false;\nlet isConnecting = false;\nlet requestId = 0;\nlet intentionalClose = false;\nlet reconnectAttempts = 0;\nlet isBlockSubscribed = false;\nlet lastBlockHeader: BlockHeader | null = null;\n\n// Store timeout IDs for pending requests\ninterface PendingRequestWithTimeout extends PendingRequest {\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\nconst pending: Record<number, PendingRequestWithTimeout> = {};\nconst blockSubscribers: ((header: BlockHeader) => void)[] = [];\n\n// Connection state callbacks with cleanup support\ninterface ConnectionCallback {\n resolve: () => void;\n reject: (err: Error) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\nconst connectionCallbacks: ConnectionCallback[] = [];\n\n// Reconnect configuration\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst BASE_DELAY = 2000;\nconst MAX_DELAY = 60000; // 1 minute\n\n// Timeout configuration\nconst RPC_TIMEOUT = 30000; // 30 seconds\nconst CONNECTION_TIMEOUT = 30000; // 30 seconds\n\n// ----------------------------------------\n// CONNECTION STATE\n// ----------------------------------------\nexport function isWebSocketConnected(): boolean {\n return isConnected && ws !== null && ws.readyState === WebSocket.OPEN;\n}\n\nexport function waitForConnection(): Promise<void> {\n if (isWebSocketConnected()) {\n return Promise.resolve();\n }\n\n return new Promise((resolve, reject) => {\n const callback: ConnectionCallback = {\n resolve: () => {\n if (callback.timeoutId) clearTimeout(callback.timeoutId);\n resolve();\n },\n reject: (err: Error) => {\n if (callback.timeoutId) clearTimeout(callback.timeoutId);\n reject(err);\n },\n };\n\n callback.timeoutId = setTimeout(() => {\n // Remove from callbacks array\n const idx = connectionCallbacks.indexOf(callback);\n if (idx > -1) connectionCallbacks.splice(idx, 1);\n reject(new Error('Connection timeout'));\n }, CONNECTION_TIMEOUT);\n\n connectionCallbacks.push(callback);\n });\n}\n\n// ----------------------------------------\n// SINGLETON CONNECT — prevents double connect\n// ----------------------------------------\nexport function connect(endpoint: string = DEFAULT_ENDPOINT): Promise<void> {\n if (isConnected) {\n return Promise.resolve();\n }\n\n if (isConnecting) {\n return waitForConnection();\n }\n\n isConnecting = true;\n\n return new Promise((resolve, reject) => {\n let hasResolved = false;\n\n try {\n ws = new WebSocket(endpoint);\n } catch (err) {\n console.error('[L1] WebSocket constructor threw exception:', err);\n isConnecting = false;\n reject(err);\n return;\n }\n\n ws.onopen = () => {\n isConnected = true;\n isConnecting = false;\n reconnectAttempts = 0; // Reset reconnect counter on successful connection\n hasResolved = true;\n resolve();\n\n // Notify all waiting callbacks (clear their timeouts first)\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n cb.resolve();\n });\n connectionCallbacks.length = 0;\n };\n\n ws.onclose = () => {\n isConnected = false;\n isBlockSubscribed = false; // Reset block subscription on disconnect\n\n // Reject all pending requests and clear their timeouts\n Object.values(pending).forEach((req) => {\n if (req.timeoutId) clearTimeout(req.timeoutId);\n req.reject(new Error('WebSocket connection closed'));\n });\n Object.keys(pending).forEach((key) => delete pending[Number(key)]);\n\n // Don't reconnect if this was an intentional close\n if (intentionalClose) {\n intentionalClose = false;\n isConnecting = false;\n reconnectAttempts = 0;\n\n // Reject if we haven't resolved yet\n if (!hasResolved) {\n hasResolved = true;\n reject(new Error('WebSocket connection closed intentionally'));\n }\n return;\n }\n\n // Check if we've exceeded max reconnect attempts\n if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {\n console.error('[L1] Max reconnect attempts reached. Giving up.');\n isConnecting = false;\n\n // Reject all waiting callbacks\n const error = new Error('Max reconnect attempts reached');\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n cb.reject(error);\n });\n connectionCallbacks.length = 0;\n\n // Reject if we haven't resolved yet\n if (!hasResolved) {\n hasResolved = true;\n reject(error);\n }\n return;\n }\n\n // Calculate exponential backoff delay\n const delay = Math.min(BASE_DELAY * Math.pow(2, reconnectAttempts), MAX_DELAY);\n\n reconnectAttempts++;\n console.warn(\n `[L1] WebSocket closed unexpectedly. Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`\n );\n\n // Keep isConnecting true so callers know reconnection is in progress\n // The resolve/reject will happen when reconnection succeeds or fails\n setTimeout(() => {\n connect(endpoint)\n .then(() => {\n if (!hasResolved) {\n hasResolved = true;\n resolve();\n }\n })\n .catch((err) => {\n if (!hasResolved) {\n hasResolved = true;\n reject(err);\n }\n });\n }, delay);\n };\n\n ws.onerror = (err: Event) => {\n console.error('[L1] WebSocket error:', err);\n // Note: Browser WebSocket errors don't provide detailed error info for security reasons\n // The actual connection error details are only visible in browser DevTools Network tab\n // Error alone doesn't mean connection failed - onclose will be called\n };\n\n ws.onmessage = (msg) => handleMessage(msg);\n });\n}\n\nfunction handleMessage(event: MessageEvent) {\n const data = JSON.parse(event.data);\n\n if (data.id && pending[data.id]) {\n const request = pending[data.id];\n delete pending[data.id];\n if (data.error) {\n request.reject(data.error);\n } else {\n request.resolve(data.result);\n }\n }\n\n if (data.method === 'blockchain.headers.subscribe') {\n const header = data.params[0] as BlockHeader;\n lastBlockHeader = header; // Cache for late subscribers\n blockSubscribers.forEach((cb) => cb(header));\n }\n}\n\n// ----------------------------------------\n// SAFE RPC - Auto-connects and waits if needed\n// ----------------------------------------\nexport async function rpc(method: string, params: unknown[] = []): Promise<unknown> {\n // Auto-connect if not connected\n if (!isConnected && !isConnecting) {\n await connect();\n }\n\n // Wait for connection if connecting\n if (!isWebSocketConnected()) {\n await waitForConnection();\n }\n\n return new Promise((resolve, reject) => {\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n return reject(new Error('WebSocket not connected (OPEN)'));\n }\n\n const id = ++requestId;\n\n // Set up timeout for this request\n const timeoutId = setTimeout(() => {\n if (pending[id]) {\n delete pending[id];\n reject(new Error(`RPC timeout: ${method}`));\n }\n }, RPC_TIMEOUT);\n\n pending[id] = {\n resolve: (result) => {\n clearTimeout(timeoutId);\n resolve(result);\n },\n reject: (err) => {\n clearTimeout(timeoutId);\n reject(err);\n },\n timeoutId,\n };\n\n ws.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }));\n });\n}\n\n// ----------------------------------------\n// API METHODS\n// ----------------------------------------\n\nexport async function getUtxo(address: string) {\n const scripthash = addressToScriptHash(address);\n\n const result = await rpc('blockchain.scripthash.listunspent', [scripthash]);\n\n if (!Array.isArray(result)) {\n console.warn('listunspent returned non-array:', result);\n return [];\n }\n\n return result.map((u: UTXO) => ({\n tx_hash: u.tx_hash,\n tx_pos: u.tx_pos,\n value: u.value,\n height: u.height,\n address,\n }));\n}\n\nexport async function getBalance(address: string) {\n const scriptHash = addressToScriptHash(address);\n const result = (await rpc('blockchain.scripthash.get_balance', [scriptHash])) as BalanceResult;\n\n const confirmed = result.confirmed || 0;\n const unconfirmed = result.unconfirmed || 0;\n\n const totalSats = confirmed + unconfirmed;\n\n // Convert sats → ALPHA\n const alpha = totalSats / 100_000_000;\n\n return alpha;\n}\n\nexport async function broadcast(rawHex: string) {\n return await rpc('blockchain.transaction.broadcast', [rawHex]);\n}\n\nexport async function subscribeBlocks(cb: (header: BlockHeader) => void): Promise<() => void> {\n // Auto-connect if not connected (same as rpc())\n if (!isConnected && !isConnecting) {\n await connect();\n }\n\n // Wait for connection to be established\n if (!isWebSocketConnected()) {\n await waitForConnection();\n }\n\n blockSubscribers.push(cb);\n\n // Only send RPC subscription if not already subscribed\n // This prevents duplicate server-side subscriptions\n if (!isBlockSubscribed) {\n isBlockSubscribed = true;\n const header = (await rpc('blockchain.headers.subscribe', [])) as BlockHeader;\n if (header) {\n lastBlockHeader = header;\n // Notify ALL current subscribers with the initial header\n blockSubscribers.forEach((subscriber) => subscriber(header));\n }\n } else if (lastBlockHeader) {\n // For late subscribers, immediately notify with cached header\n cb(lastBlockHeader);\n }\n\n // Return unsubscribe function\n return () => {\n const index = blockSubscribers.indexOf(cb);\n if (index > -1) {\n blockSubscribers.splice(index, 1);\n }\n };\n}\n\nexport interface TransactionHistoryItem {\n tx_hash: string;\n height: number;\n fee?: number;\n}\n\nexport interface TransactionDetail {\n txid: string;\n version: number;\n locktime: number;\n vin: Array<{\n txid: string;\n vout: number;\n scriptSig?: {\n hex: string;\n };\n sequence: number;\n }>;\n vout: Array<{\n value: number;\n n: number;\n scriptPubKey: {\n hex: string;\n type: string;\n addresses?: string[];\n address?: string;\n };\n }>;\n blockhash?: string;\n confirmations?: number;\n time?: number;\n blocktime?: number;\n}\n\nexport async function getTransactionHistory(address: string): Promise<TransactionHistoryItem[]> {\n const scriptHash = addressToScriptHash(address);\n const result = await rpc('blockchain.scripthash.get_history', [scriptHash]);\n\n if (!Array.isArray(result)) {\n console.warn('get_history returned non-array:', result);\n return [];\n }\n\n return result as TransactionHistoryItem[];\n}\n\nexport async function getTransaction(txid: string) {\n return await rpc('blockchain.transaction.get', [txid, true]);\n}\n\nexport async function getBlockHeader(height: number) {\n return await rpc('blockchain.block.header', [height, height]);\n}\n\nexport async function getCurrentBlockHeight(): Promise<number> {\n try {\n const header = (await rpc('blockchain.headers.subscribe', [])) as BlockHeader;\n return header?.height || 0;\n } catch (err) {\n console.error('Error getting current block height:', err);\n return 0;\n }\n}\n\nexport function disconnect() {\n if (ws) {\n intentionalClose = true;\n ws.close();\n ws = null;\n }\n isConnected = false;\n isConnecting = false;\n reconnectAttempts = 0;\n isBlockSubscribed = false;\n\n // Clear all pending request timeouts\n Object.values(pending).forEach((req) => {\n if (req.timeoutId) clearTimeout(req.timeoutId);\n });\n Object.keys(pending).forEach((key) => delete pending[Number(key)]);\n\n // Clear connection callback timeouts\n connectionCallbacks.forEach((cb) => {\n if (cb.timeoutId) clearTimeout(cb.timeoutId);\n });\n connectionCallbacks.length = 0;\n}\n","/**\n * L1 SDK - ALPHA blockchain operations\n */\n\n// Types\nexport * from './types';\n\n// Bech32 encoding (from core)\nexport {\n createBech32,\n encodeBech32,\n decodeBech32,\n convertBits,\n CHARSET,\n} from '../core/bech32';\n\n// Address utilities\nexport { addressToScriptHash } from './addressToScriptHash';\n\n// Crypto utilities (from core)\nexport {\n computeHash160,\n hash160,\n hash160ToBytes,\n publicKeyToAddress,\n privateKeyToAddressInfo,\n generateAddressInfo,\n ec,\n} from '../core/crypto';\n\n// Encryption and WIF\nexport {\n encrypt,\n decrypt,\n encryptWallet,\n decryptWallet,\n generatePrivateKey,\n hexToWIF,\n} from './crypto';\n\n// Address derivation\nexport {\n deriveChildKeyBIP32,\n deriveKeyAtPath,\n generateMasterKeyFromSeed,\n generateHDAddressBIP32,\n generateAddressFromMasterKey,\n deriveChildKey,\n generateHDAddress,\n} from './address';\n\n// Network (Fulcrum WebSocket)\nexport {\n connect,\n disconnect,\n rpc,\n isWebSocketConnected,\n waitForConnection,\n getUtxo,\n getBalance,\n broadcast,\n subscribeBlocks,\n getTransactionHistory,\n getTransaction,\n getBlockHeader,\n getCurrentBlockHeight,\n} from './network';\nexport type { BlockHeader, TransactionHistoryItem, TransactionDetail } from './network';\n\n// Transaction building\nexport {\n createScriptPubKey,\n buildSegWitTransaction,\n createAndSignTransaction,\n collectUtxosForAmount,\n createTransactionPlan,\n sendAlpha,\n} from './tx';\n\n// Vesting classification\nexport { vestingClassifier, VESTING_THRESHOLD } from './vesting';\nexport { vestingState } from './vestingState';\n\n// Address helpers\nexport { WalletAddressHelper } from './addressHelpers';\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';\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 Error('Invalid master key generated');\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 Error('Invalid key: IL >= curve order');\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 Error('Invalid key: child key is zero');\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 Error('Invalid mnemonic phrase');\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 Error('Invalid mnemonic phrase');\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// Re-export elliptic instance for advanced use cases\nexport { ec };\n","import CryptoJS from \"crypto-js\";\n\nconst SALT = \"alpha_wallet_salt\";\nconst PBKDF2_ITERATIONS = 100000;\n\nexport function encrypt(text: string, password: string): string {\n return CryptoJS.AES.encrypt(text, password).toString();\n}\n\nexport function decrypt(encrypted: string, password: string): string {\n const bytes = CryptoJS.AES.decrypt(encrypted, password);\n return bytes.toString(CryptoJS.enc.Utf8);\n}\n\nexport function generatePrivateKey(): string {\n return CryptoJS.lib.WordArray.random(32).toString();\n}\n\n/**\n * Encrypt wallet master key with password using PBKDF2 + AES\n */\nexport function encryptWallet(\n masterPrivateKey: string,\n password: string\n): string {\n const passwordKey = CryptoJS.PBKDF2(password, SALT, {\n keySize: 256 / 32,\n iterations: PBKDF2_ITERATIONS,\n }).toString();\n\n const encrypted = CryptoJS.AES.encrypt(\n masterPrivateKey,\n passwordKey\n ).toString();\n\n return encrypted;\n}\n\n/**\n * Decrypt wallet master key with password\n */\nexport function decryptWallet(\n encryptedData: string,\n password: string\n): string {\n const passwordKey = CryptoJS.PBKDF2(password, SALT, {\n keySize: 256 / 32,\n iterations: PBKDF2_ITERATIONS,\n }).toString();\n\n const decrypted = CryptoJS.AES.decrypt(encryptedData, passwordKey);\n return decrypted.toString(CryptoJS.enc.Utf8);\n}\n\n/**\n * Convert hex private key to WIF format\n */\nexport function hexToWIF(hexKey: string): string {\n // Alpha mainnet version byte is 0x80\n const versionByte = \"80\";\n const extendedKey = versionByte + hexKey;\n\n // Calculate checksum\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(extendedKey)).toString();\n const hash2 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(hash1)).toString();\n const checksum = hash2.substring(0, 8);\n\n // Combine and encode\n const finalHex = extendedKey + checksum;\n\n // Convert to Base58\n return base58Encode(finalHex);\n}\n\n/**\n * Base58 encoding\n */\nfunction base58Encode(hex: string): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\n // Convert hex to big integer\n let num = BigInt(\"0x\" + hex);\n let encoded = \"\";\n\n while (num > 0n) {\n const remainder = Number(num % 58n);\n num = num / 58n;\n encoded = ALPHABET[remainder] + encoded;\n }\n\n // Add leading 1s for leading 0s in hex\n for (let i = 0; i < hex.length && hex.substring(i, i + 2) === \"00\"; i += 2) {\n encoded = \"1\" + encoded;\n }\n\n return encoded;\n}\n","/**\n * L1 Address Derivation\n *\n * Uses core crypto functions for standard BIP32 derivation,\n * plus legacy functions for backward compatibility with old wallets.\n */\n\nimport CryptoJS from 'crypto-js';\nimport {\n deriveChildKey as coreDeriveChildKey,\n deriveKeyAtPath as coreDeriveKeyAtPath,\n generateMasterKey,\n generateAddressInfo,\n ec,\n type AddressInfo,\n} from '../core/crypto';\n\n// Re-export core functions with L1 naming conventions\nexport { ec };\n\n/**\n * Standard BIP32 child key derivation\n * Re-export from core with L1 naming convention\n */\nexport const deriveChildKeyBIP32 = coreDeriveChildKey;\n\n/**\n * Derive key at a full BIP44 path\n * Re-export from core\n */\nexport const deriveKeyAtPath = coreDeriveKeyAtPath;\n\n/**\n * Generate master key and chain code from seed (BIP32 standard)\n * Wrapper around core function with L1 return type naming\n */\nexport function generateMasterKeyFromSeed(seedHex: string): {\n masterPrivateKey: string;\n masterChainCode: string;\n} {\n const result = generateMasterKey(seedHex);\n return {\n masterPrivateKey: result.privateKey,\n masterChainCode: result.chainCode,\n };\n}\n\n/**\n * Generate HD address using standard BIP32\n * Standard path: m/44'/0'/0'/0/{index} (external chain, non-hardened)\n * For change addresses, use isChange = true to get m/44'/0'/0'/1/{index}\n */\nexport function generateHDAddressBIP32(\n masterPriv: string,\n chainCode: string,\n index: number,\n basePath: string = \"m/44'/0'/0'\",\n isChange: boolean = false\n): AddressInfo {\n // Chain: 0 = external (receiving), 1 = internal (change)\n const chain = isChange ? 1 : 0;\n const fullPath = `${basePath}/${chain}/${index}`;\n\n const derived = coreDeriveKeyAtPath(masterPriv, chainCode, fullPath);\n\n return generateAddressInfo(derived.privateKey, index, fullPath);\n}\n\n// ============================================\n// Original index.html compatible derivation\n// ============================================\n\n/**\n * Generate address from master private key using HMAC-SHA512 derivation\n * This matches exactly the original index.html implementation\n * NOTE: This is NON-STANDARD derivation for legacy wallet compatibility\n *\n * @param masterPrivateKey - 32-byte hex private key (64 chars)\n * @param index - Address index\n */\nexport function generateAddressFromMasterKey(\n masterPrivateKey: string,\n index: number\n): AddressInfo {\n const derivationPath = `m/44'/0'/${index}'`;\n\n // HMAC-SHA512 with path as key (matching index.html exactly)\n const hmacInput = CryptoJS.enc.Hex.parse(masterPrivateKey);\n const hmacKey = CryptoJS.enc.Utf8.parse(derivationPath);\n const hmacOutput = CryptoJS.HmacSHA512(hmacInput, hmacKey).toString();\n\n // Use left 32 bytes for private key\n const childPrivateKey = hmacOutput.substring(0, 64);\n\n return generateAddressInfo(childPrivateKey, index, derivationPath);\n}\n\n// ============================================\n// Legacy functions for backward compatibility\n// ============================================\n\n/**\n * @deprecated Use deriveChildKeyBIP32 for new wallets\n * Legacy HMAC-SHA512 derivation (non-standard)\n * Kept for backward compatibility with old wallets\n */\nexport function deriveChildKey(\n masterPriv: string,\n chainCode: string,\n index: number\n) {\n const data = masterPriv + index.toString(16).padStart(8, '0');\n\n const I = CryptoJS.HmacSHA512(\n CryptoJS.enc.Hex.parse(data),\n CryptoJS.enc.Hex.parse(chainCode)\n ).toString();\n\n return {\n privateKey: I.substring(0, 64),\n nextChainCode: I.substring(64),\n };\n}\n\n/**\n * @deprecated Use generateHDAddressBIP32 for new wallets\n * Legacy HD address generation (non-standard derivation)\n */\nexport function generateHDAddress(\n masterPriv: string,\n chainCode: string,\n index: number\n): AddressInfo {\n const child = deriveChildKey(masterPriv, chainCode, index);\n const path = `m/44'/0'/0'/${index}`;\n\n return generateAddressInfo(child.privateKey, index, path);\n}\n","/**\n * Transaction handling - Strict copy of index.html logic\n */\nimport { getUtxo, broadcast } from \"./network\";\nimport { decodeBech32 } from \"../core/bech32\";\nimport CryptoJS from \"crypto-js\";\nimport elliptic from \"elliptic\";\nimport type { Wallet, TransactionPlan, Transaction, UTXO } from \"./types\";\nimport { vestingState } from \"./vestingState\";\nimport { WalletAddressHelper } from \"./addressHelpers\";\n\nconst ec = new elliptic.ec(\"secp256k1\");\n\n// Constants\nconst FEE = 10_000; // sats per transaction\nconst DUST = 546; // dust threshold\nconst SAT = 100_000_000; // sats in 1 ALPHA\n\n/**\n * Create scriptPubKey for address (P2WPKH for bech32)\n * Exact copy from index.html\n */\nexport function createScriptPubKey(address: string): string {\n if (!address || typeof address !== \"string\") {\n throw new Error(\"Invalid address: must be a string\");\n }\n\n const decoded = decodeBech32(address);\n if (!decoded) {\n throw new Error(\"Invalid bech32 address: \" + address);\n }\n\n // Convert data array to hex string\n const dataHex = Array.from(decoded.data)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n // P2WPKH scriptPubKey: OP_0 <20-byte-key-hash>\n return \"0014\" + dataHex;\n}\n\n/**\n * Create signature hash for SegWit (BIP143)\n * Exact copy from index.html createSignatureHash()\n */\nfunction createSignatureHash(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n publicKey: string\n): string {\n let preimage = \"\";\n\n // 1. nVersion (4 bytes, little-endian)\n preimage += \"02000000\";\n\n // 2. hashPrevouts (32 bytes)\n const txidBytes = txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n const voutBytes = (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n const prevouts = txidBytes + voutBytes;\n const hashPrevouts = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(prevouts))).toString();\n preimage += hashPrevouts;\n\n // 3. hashSequence (32 bytes)\n const sequence = \"feffffff\";\n const hashSequence = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(sequence))).toString();\n preimage += hashSequence;\n\n // 4. outpoint (36 bytes)\n preimage += txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n preimage += (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // 5. scriptCode for P2WPKH (includes length prefix)\n const pubKeyHash = CryptoJS.RIPEMD160(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(publicKey))).toString();\n const scriptCode = \"1976a914\" + pubKeyHash + \"88ac\";\n preimage += scriptCode;\n\n // 6. amount (8 bytes, little-endian)\n const amountHex = txPlan.input.value.toString(16).padStart(16, \"0\");\n preimage += amountHex.match(/../g)!.reverse().join(\"\");\n\n // 7. nSequence (4 bytes, little-endian)\n preimage += sequence;\n\n // 8. hashOutputs (32 bytes)\n let outputs = \"\";\n for (const output of txPlan.outputs) {\n const outAmountHex = output.value.toString(16).padStart(16, \"0\");\n outputs += outAmountHex.match(/../g)!.reverse().join(\"\");\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (scriptPubKey.length / 2).toString(16).padStart(2, \"0\");\n outputs += scriptLength;\n outputs += scriptPubKey;\n }\n const hashOutputs = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(outputs))).toString();\n preimage += hashOutputs;\n\n // 9. nLocktime (4 bytes, little-endian)\n preimage += \"00000000\";\n\n // 10. sighash type (4 bytes, little-endian)\n preimage += \"01000000\"; // SIGHASH_ALL\n\n // Double SHA256\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(preimage));\n const hash2 = CryptoJS.SHA256(hash1);\n return hash2.toString();\n}\n\n/**\n * Create witness data for the transaction\n * Exact copy from index.html createWitnessData()\n */\nfunction createWitnessData(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n keyPair: elliptic.ec.KeyPair,\n publicKey: string\n): string {\n // Create signature hash for witness\n const sigHash = createSignatureHash(txPlan, publicKey);\n\n // Sign the hash\n const signature = keyPair.sign(sigHash);\n\n // Ensure low-S canonical signature (BIP62)\n const halfOrder = ec.curve.n!.shrn(1);\n if (signature.s.cmp(halfOrder) > 0) {\n signature.s = ec.curve.n!.sub(signature.s);\n }\n\n const derSig = signature.toDER(\"hex\") + \"01\"; // SIGHASH_ALL\n\n // Build witness\n let witness = \"\";\n witness += \"02\"; // 2 stack items\n\n // Signature\n const sigLen = (derSig.length / 2).toString(16).padStart(2, \"0\");\n witness += sigLen;\n witness += derSig;\n\n // Public key\n const pubKeyLen = (publicKey.length / 2).toString(16).padStart(2, \"0\");\n witness += pubKeyLen;\n witness += publicKey;\n\n return witness;\n}\n\n/**\n * Build a proper SegWit transaction\n * Exact copy from index.html buildSegWitTransaction()\n */\nexport function buildSegWitTransaction(\n txPlan: { input: { tx_hash: string; tx_pos: number; value: number }; outputs: Array<{ value: number; address: string }> },\n keyPair: elliptic.ec.KeyPair,\n publicKey: string\n): { hex: string; txid: string } {\n let txHex = \"\";\n\n // Version (4 bytes, little-endian)\n txHex += \"02000000\"; // version 2\n\n // Marker and flag for SegWit\n txHex += \"00\"; // marker\n txHex += \"01\"; // flag\n\n // Number of inputs (varint)\n txHex += \"01\"; // 1 input\n\n // Input - Previous tx hash (32 bytes, reversed for little-endian)\n const prevTxHash = txPlan.input.tx_hash;\n const reversedHash = prevTxHash.match(/../g)!.reverse().join(\"\");\n txHex += reversedHash;\n\n // Previous output index (4 bytes, little-endian)\n const vout = txPlan.input.tx_pos;\n txHex += (\"00000000\" + vout.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // Script length (varint) - 0 for witness transactions\n txHex += \"00\";\n\n // Sequence (4 bytes)\n txHex += \"feffffff\";\n\n // Number of outputs (varint)\n const outputCount = txPlan.outputs.length;\n txHex += (\"0\" + outputCount.toString(16)).slice(-2);\n\n // Outputs\n for (const output of txPlan.outputs) {\n // Amount (8 bytes, little-endian)\n const amountHex = output.value.toString(16).padStart(16, \"0\");\n txHex += amountHex.match(/../g)!.reverse().join(\"\");\n\n // Script pubkey\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (scriptPubKey.length / 2).toString(16).padStart(2, \"0\");\n txHex += scriptLength;\n txHex += scriptPubKey;\n }\n\n // Witness data\n const witnessData = createWitnessData(txPlan, keyPair, publicKey);\n txHex += witnessData;\n\n // Locktime (4 bytes)\n txHex += \"00000000\";\n\n // Calculate transaction ID (double SHA256 of tx without witness data)\n let txForId = \"\";\n\n // Version (4 bytes)\n txForId += \"02000000\";\n\n // Input count (1 byte)\n txForId += \"01\";\n\n // Input: txid (32 bytes reversed) + vout (4 bytes)\n const inputTxidBytes = txPlan.input.tx_hash.match(/../g)!.reverse().join(\"\");\n txForId += inputTxidBytes;\n txForId += (\"00000000\" + txPlan.input.tx_pos.toString(16)).slice(-8).match(/../g)!.reverse().join(\"\");\n\n // Script sig (empty for P2WPKH)\n txForId += \"00\";\n\n // Sequence (4 bytes)\n txForId += \"feffffff\";\n\n // Output count\n txForId += (\"0\" + txPlan.outputs.length.toString(16)).slice(-2);\n\n // Add all outputs\n for (const output of txPlan.outputs) {\n const amountHex = (\"0000000000000000\" + output.value.toString(16)).slice(-16);\n const amountLittleEndian = amountHex.match(/../g)!.reverse().join(\"\");\n txForId += amountLittleEndian;\n\n const scriptPubKey = createScriptPubKey(output.address);\n const scriptLength = (\"0\" + (scriptPubKey.length / 2).toString(16)).slice(-2);\n txForId += scriptLength;\n txForId += scriptPubKey;\n }\n\n // Locktime (4 bytes)\n txForId += \"00000000\";\n\n // Calculate the correct txid\n const hash1 = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(txForId));\n const hash2 = CryptoJS.SHA256(hash1);\n const txid = hash2.toString().match(/../g)!.reverse().join(\"\");\n\n return {\n hex: txHex,\n txid: txid,\n };\n}\n\n/**\n * Create and sign a transaction\n * Uses the private key for the specific address being spent from\n */\nexport function createAndSignTransaction(\n wallet: Wallet,\n txPlan: Transaction\n): { raw: string; txid: string } {\n // Find the address entry that matches the input address\n const fromAddress = txPlan.input.address;\n const addressEntry = wallet.addresses.find(a => a.address === fromAddress);\n\n // Use the private key from the address entry, or fall back to childPrivateKey/masterPrivateKey\n let privateKeyHex: string | undefined;\n\n if (addressEntry?.privateKey) {\n // Use the specific private key for this address\n privateKeyHex = addressEntry.privateKey;\n } else if (wallet.childPrivateKey) {\n // Fall back to childPrivateKey (first address)\n privateKeyHex = wallet.childPrivateKey;\n } else {\n // Last resort: use master key\n privateKeyHex = wallet.masterPrivateKey;\n }\n\n if (!privateKeyHex) {\n throw new Error(\"No private key available for address: \" + fromAddress);\n }\n\n const keyPair = ec.keyFromPrivate(privateKeyHex, \"hex\");\n const publicKey = keyPair.getPublic(true, \"hex\"); // compressed\n\n // Convert Transaction to the format expected by buildSegWitTransaction\n const txPlanForBuild = {\n input: {\n tx_hash: txPlan.input.txid,\n tx_pos: txPlan.input.vout,\n value: txPlan.input.value,\n },\n outputs: txPlan.outputs,\n };\n\n const tx = buildSegWitTransaction(txPlanForBuild, keyPair, publicKey);\n\n return {\n raw: tx.hex,\n txid: tx.txid,\n };\n}\n\n/**\n * Collect UTXOs for required amount\n * Based on index.html collectUtxosForAmount()\n *\n * Strategy: First try to find a single UTXO that can cover amount + fee.\n * If not found, fall back to combining multiple UTXOs.\n */\nexport function collectUtxosForAmount(\n utxoList: UTXO[],\n amountSats: number,\n recipientAddress: string,\n senderAddress: string\n): TransactionPlan {\n const totalAvailable = utxoList.reduce((sum, u) => sum + u.value, 0);\n\n if (totalAvailable < amountSats + FEE) {\n return {\n success: false,\n transactions: [],\n error: `Insufficient funds. Available: ${totalAvailable / SAT} ALPHA, Required: ${(amountSats + FEE) / SAT} ALPHA (including fee)`,\n };\n }\n\n // Strategy 1: Find a single UTXO that covers amount + fee\n // Sort by value ascending to find the smallest sufficient UTXO\n const sortedByValue = [...utxoList].sort((a, b) => a.value - b.value);\n const sufficientUtxo = sortedByValue.find(u => u.value >= amountSats + FEE);\n\n if (sufficientUtxo) {\n const changeAmount = sufficientUtxo.value - amountSats - FEE;\n const tx: Transaction = {\n input: {\n txid: sufficientUtxo.txid ?? sufficientUtxo.tx_hash ?? \"\",\n vout: sufficientUtxo.vout ?? sufficientUtxo.tx_pos ?? 0,\n value: sufficientUtxo.value,\n address: sufficientUtxo.address ?? senderAddress,\n },\n outputs: [{ address: recipientAddress, value: amountSats }],\n fee: FEE,\n changeAmount: changeAmount,\n changeAddress: senderAddress,\n };\n\n // Add change output if above dust\n if (changeAmount > DUST) {\n tx.outputs.push({ value: changeAmount, address: senderAddress });\n }\n\n return {\n success: true,\n transactions: [tx],\n };\n }\n\n // Strategy 2: No single UTXO is sufficient, combine multiple UTXOs\n // Sort descending to use larger UTXOs first (fewer transactions)\n const sortedDescending = [...utxoList].sort((a, b) => b.value - a.value);\n\n const transactions: Transaction[] = [];\n let remainingAmount = amountSats;\n\n for (const utxo of sortedDescending) {\n if (remainingAmount <= 0) break;\n\n const utxoValue = utxo.value;\n let txAmount = 0;\n let changeAmount = 0;\n\n if (utxoValue >= remainingAmount + FEE) {\n // This UTXO covers the remaining amount plus fee\n txAmount = remainingAmount;\n changeAmount = utxoValue - remainingAmount - FEE;\n remainingAmount = 0;\n } else {\n // Use entire UTXO minus fee\n txAmount = utxoValue - FEE;\n if (txAmount <= 0) continue; // Skip UTXOs too small to cover fee\n remainingAmount -= txAmount;\n }\n\n const tx: Transaction = {\n input: {\n txid: utxo.txid ?? utxo.tx_hash ?? \"\",\n vout: utxo.vout ?? utxo.tx_pos ?? 0,\n value: utxo.value,\n address: utxo.address ?? senderAddress,\n },\n outputs: [{ address: recipientAddress, value: txAmount }],\n fee: FEE,\n changeAmount: changeAmount,\n changeAddress: senderAddress,\n };\n\n // Add change output if above dust\n if (changeAmount > DUST) {\n tx.outputs.push({ value: changeAmount, address: senderAddress });\n }\n\n transactions.push(tx);\n }\n\n if (remainingAmount > 0) {\n return {\n success: false,\n transactions: [],\n error: `Unable to collect enough UTXOs. Short by ${remainingAmount / SAT} ALPHA after fees.`,\n };\n }\n\n return {\n success: true,\n transactions,\n };\n}\n\n/**\n * Create transaction plan from wallet\n * @param wallet - The wallet\n * @param toAddress - Recipient address\n * @param amountAlpha - Amount in ALPHA\n * @param fromAddress - Optional: specific address to send from (defaults to first address)\n */\nexport async function createTransactionPlan(\n wallet: Wallet,\n toAddress: string,\n amountAlpha: number,\n fromAddress?: string\n): Promise<TransactionPlan> {\n if (!decodeBech32(toAddress)) {\n throw new Error(\"Invalid recipient address\");\n }\n\n // Use specified fromAddress or default to first external address\n const defaultAddr = WalletAddressHelper.getDefault(wallet);\n const senderAddress = fromAddress || defaultAddr.address;\n const amountSats = Math.floor(amountAlpha * SAT);\n\n // Get UTXOs filtered by current vesting mode (set in SendModal)\n let utxos: UTXO[];\n const currentMode = vestingState.getMode();\n\n if (vestingState.hasClassifiedData(senderAddress)) {\n // Use vesting-filtered UTXOs based on selected mode\n utxos = vestingState.getFilteredUtxos(senderAddress);\n console.log(`Using ${utxos.length} ${currentMode} UTXOs`);\n } else {\n // Fall back to all UTXOs if not yet classified\n utxos = await getUtxo(senderAddress);\n console.log(`Using ${utxos.length} UTXOs (vesting not classified yet)`);\n }\n\n if (!Array.isArray(utxos) || utxos.length === 0) {\n const modeText = currentMode !== 'all' ? ` (${currentMode} coins)` : '';\n throw new Error(`No UTXOs available${modeText} for address: ` + senderAddress);\n }\n\n return collectUtxosForAmount(utxos, amountSats, toAddress, senderAddress);\n}\n\n/**\n * Send ALPHA to address\n * @param wallet - The wallet\n * @param toAddress - Recipient address\n * @param amountAlpha - Amount in ALPHA\n * @param fromAddress - Optional: specific address to send from\n */\nexport async function sendAlpha(\n wallet: Wallet,\n toAddress: string,\n amountAlpha: number,\n fromAddress?: string\n) {\n const plan = await createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress);\n\n if (!plan.success) {\n throw new Error(plan.error || \"Transaction planning failed\");\n }\n\n const results = [];\n\n for (const tx of plan.transactions) {\n const signed = createAndSignTransaction(wallet, tx);\n const result = await broadcast(signed.raw);\n results.push({\n txid: signed.txid,\n raw: signed.raw,\n broadcastResult: result,\n });\n }\n\n return results;\n}\n","/**\n * VestingClassifier - Traces UTXOs to their coinbase origin to determine vesting status\n * VESTED: Coins from coinbase transactions in blocks <= VESTING_THRESHOLD (280000)\n * UNVESTED: Coins from coinbase transactions in blocks > VESTING_THRESHOLD\n *\n * Direct port from index.html VestingClassifier\n */\nimport { getTransaction, getCurrentBlockHeight } from \"./network\";\nimport type { UTXO, ClassifiedUTXO, ClassificationResult } from \"./types\";\n\nexport const VESTING_THRESHOLD = 280000;\n\n// Current block height - updated during classification\nlet currentBlockHeight: number | null = null;\n\ninterface CacheEntry {\n blockHeight: number | null; // null means \"not computed yet\"\n isCoinbase: boolean;\n inputTxId: string | null;\n}\n\ninterface TransactionData {\n txid: string;\n confirmations?: number;\n height?: number;\n vin?: Array<{\n txid?: string;\n coinbase?: string;\n }>;\n}\n\nclass VestingClassifier {\n private memoryCache = new Map<string, CacheEntry>();\n private dbName = \"SphereVestingCacheV5\"; // V5 - new cache with proper null handling\n private storeName = \"vestingCache\";\n private db: IDBDatabase | null = null;\n\n /**\n * Initialize IndexedDB for persistent caching\n */\n async initDB(): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, 1);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(this.storeName)) {\n db.createObjectStore(this.storeName, { keyPath: \"txHash\" });\n }\n };\n\n request.onsuccess = (event) => {\n this.db = (event.target as IDBOpenDBRequest).result;\n resolve();\n };\n\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Check if transaction is coinbase\n */\n private isCoinbaseTransaction(txData: TransactionData): boolean {\n if (txData.vin && txData.vin.length === 1) {\n const vin = txData.vin[0];\n // Check for coinbase field or missing txid\n if (vin.coinbase || (!vin.txid && vin.coinbase !== undefined)) {\n return true;\n }\n // Some formats use empty txid for coinbase\n if (vin.txid === \"0000000000000000000000000000000000000000000000000000000000000000\") {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Load from IndexedDB cache\n */\n private async loadFromDB(txHash: string): Promise<CacheEntry | null> {\n if (!this.db) return null;\n\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.storeName, \"readonly\");\n const store = tx.objectStore(this.storeName);\n const request = store.get(txHash);\n\n request.onsuccess = () => {\n if (request.result) {\n resolve({\n blockHeight: request.result.blockHeight,\n isCoinbase: request.result.isCoinbase,\n inputTxId: request.result.inputTxId,\n });\n } else {\n resolve(null);\n }\n };\n request.onerror = () => resolve(null);\n });\n }\n\n /**\n * Save to IndexedDB cache\n */\n private async saveToDB(txHash: string, entry: CacheEntry): Promise<void> {\n if (!this.db) return;\n\n return new Promise((resolve) => {\n const tx = this.db!.transaction(this.storeName, \"readwrite\");\n const store = tx.objectStore(this.storeName);\n store.put({ txHash, ...entry });\n tx.oncomplete = () => resolve();\n tx.onerror = () => resolve();\n });\n }\n\n /**\n * Trace a transaction to its coinbase origin\n * Alpha blockchain has single-input transactions, making this a linear trace\n */\n async traceToOrigin(txHash: string): Promise<{ coinbaseHeight: number | null; error?: string }> {\n let currentTxHash = txHash;\n let iterations = 0;\n const MAX_ITERATIONS = 10000;\n\n while (iterations < MAX_ITERATIONS) {\n iterations++;\n\n // Check memory cache first\n const cached = this.memoryCache.get(currentTxHash);\n if (cached) {\n if (cached.isCoinbase) {\n // Skip cache if blockHeight is null - needs re-fetch\n if (cached.blockHeight !== null && cached.blockHeight !== undefined) {\n return { coinbaseHeight: cached.blockHeight };\n }\n // Fall through to re-fetch\n } else if (cached.inputTxId) {\n // Follow the input chain\n currentTxHash = cached.inputTxId;\n continue;\n }\n }\n\n // Check IndexedDB cache\n const dbCached = await this.loadFromDB(currentTxHash);\n if (dbCached) {\n // Also store in memory cache\n this.memoryCache.set(currentTxHash, dbCached);\n if (dbCached.isCoinbase) {\n // Skip cache if blockHeight is null - needs re-fetch\n if (dbCached.blockHeight !== null && dbCached.blockHeight !== undefined) {\n return { coinbaseHeight: dbCached.blockHeight };\n }\n // Fall through to re-fetch\n } else if (dbCached.inputTxId) {\n currentTxHash = dbCached.inputTxId;\n continue;\n }\n }\n\n // Fetch from network\n const txData = await getTransaction(currentTxHash) as TransactionData;\n if (!txData || !txData.txid) {\n return { coinbaseHeight: null, error: `Failed to fetch tx ${currentTxHash}` };\n }\n\n // Determine if this is a coinbase transaction\n const isCoinbase = this.isCoinbaseTransaction(txData);\n\n // Calculate block height from confirmations (like index.html does)\n let blockHeight: number | null = null;\n if (txData.confirmations && currentBlockHeight !== null && currentBlockHeight !== undefined) {\n blockHeight = currentBlockHeight - txData.confirmations + 1;\n }\n\n // Get input transaction ID (if not coinbase)\n let inputTxId: string | null = null;\n if (!isCoinbase && txData.vin && txData.vin.length > 0 && txData.vin[0].txid) {\n inputTxId = txData.vin[0].txid;\n }\n\n // Cache the result\n const cacheEntry: CacheEntry = {\n blockHeight, // Can be null if confirmations not available\n isCoinbase,\n inputTxId,\n };\n this.memoryCache.set(currentTxHash, cacheEntry);\n await this.saveToDB(currentTxHash, cacheEntry);\n\n if (isCoinbase) {\n return { coinbaseHeight: blockHeight };\n }\n\n if (!inputTxId) {\n return { coinbaseHeight: null, error: \"Could not find input transaction\" };\n }\n\n currentTxHash = inputTxId;\n }\n\n return { coinbaseHeight: null, error: \"Max iterations exceeded\" };\n }\n\n /**\n * Classify a single UTXO\n */\n async classifyUtxo(utxo: UTXO): Promise<ClassificationResult> {\n const txHash = utxo.tx_hash || utxo.txid;\n if (!txHash) {\n return { isVested: false, coinbaseHeight: null, error: \"No transaction hash\" };\n }\n\n try {\n const result = await this.traceToOrigin(txHash);\n if (result.error || result.coinbaseHeight === null) {\n return { isVested: false, coinbaseHeight: null, error: result.error || \"Could not trace to origin\" };\n }\n return {\n isVested: result.coinbaseHeight <= VESTING_THRESHOLD,\n coinbaseHeight: result.coinbaseHeight,\n };\n } catch (err) {\n return {\n isVested: false,\n coinbaseHeight: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /**\n * Classify multiple UTXOs with progress callback\n */\n async classifyUtxos(\n utxos: UTXO[],\n onProgress?: (current: number, total: number) => void\n ): Promise<{\n vested: ClassifiedUTXO[];\n unvested: ClassifiedUTXO[];\n errors: Array<{ utxo: UTXO; error: string }>;\n }> {\n // Get current block height before classification\n currentBlockHeight = await getCurrentBlockHeight();\n\n // Clear memory cache to force re-fetch with current block height\n this.memoryCache.clear();\n\n const vested: ClassifiedUTXO[] = [];\n const unvested: ClassifiedUTXO[] = [];\n const errors: Array<{ utxo: UTXO; error: string }> = [];\n\n for (let i = 0; i < utxos.length; i++) {\n const utxo = utxos[i];\n const result = await this.classifyUtxo(utxo);\n\n if (result.error) {\n errors.push({ utxo, error: result.error });\n // Default to unvested on error for safety\n unvested.push({\n ...utxo,\n vestingStatus: \"error\",\n coinbaseHeight: null,\n });\n } else if (result.isVested) {\n vested.push({\n ...utxo,\n vestingStatus: \"vested\",\n coinbaseHeight: result.coinbaseHeight,\n });\n } else {\n unvested.push({\n ...utxo,\n vestingStatus: \"unvested\",\n coinbaseHeight: result.coinbaseHeight,\n });\n }\n\n // Report progress\n if (onProgress) {\n onProgress(i + 1, utxos.length);\n }\n\n // Yield every 5 UTXOs\n if (i % 5 === 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n return { vested, unvested, errors };\n }\n\n /**\n * Clear all caches\n */\n clearCaches(): void {\n this.memoryCache.clear();\n if (this.db) {\n const tx = this.db.transaction(this.storeName, \"readwrite\");\n tx.objectStore(this.storeName).clear();\n }\n }\n\n /**\n * Destroy caches and delete the IndexedDB database entirely.\n */\n async destroy(): Promise<void> {\n this.memoryCache.clear();\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n if (typeof indexedDB !== 'undefined') {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(this.dbName);\n req.onsuccess = () => resolve();\n req.onerror = () => resolve();\n req.onblocked = () => resolve();\n });\n }\n }\n}\n\nexport const vestingClassifier = new VestingClassifier();\n","import { vestingClassifier } from \"./vesting\";\nimport type {\n UTXO,\n ClassifiedUTXO,\n VestingMode,\n VestingBalances,\n} from \"./types\";\n\ninterface AddressVestingCache {\n classifiedUtxos: {\n vested: ClassifiedUTXO[];\n unvested: ClassifiedUTXO[];\n all: ClassifiedUTXO[];\n };\n vestingBalances: VestingBalances;\n}\n\nclass VestingStateManager {\n private currentMode: VestingMode = \"all\";\n private addressCache = new Map<string, AddressVestingCache>();\n private classificationInProgress = false;\n\n /**\n * Set the current vesting mode\n */\n setMode(mode: VestingMode): void {\n if (![\"all\", \"vested\", \"unvested\"].includes(mode)) {\n throw new Error(`Invalid vesting mode: ${mode}`);\n }\n this.currentMode = mode;\n }\n\n getMode(): VestingMode {\n return this.currentMode;\n }\n\n /**\n * Classify all UTXOs for an address\n */\n async classifyAddressUtxos(\n address: string,\n utxos: UTXO[],\n onProgress?: (current: number, total: number) => void\n ): Promise<void> {\n if (this.classificationInProgress) return;\n\n this.classificationInProgress = true;\n\n try {\n await vestingClassifier.initDB();\n\n const result = await vestingClassifier.classifyUtxos(utxos, onProgress);\n\n // Calculate balances\n const vestedBalance = result.vested.reduce(\n (sum, utxo) => sum + BigInt(utxo.value),\n 0n\n );\n const unvestedBalance = result.unvested.reduce(\n (sum, utxo) => sum + BigInt(utxo.value),\n 0n\n );\n\n // Store in cache\n this.addressCache.set(address, {\n classifiedUtxos: {\n vested: result.vested,\n unvested: result.unvested,\n all: [...result.vested, ...result.unvested],\n },\n vestingBalances: {\n vested: vestedBalance,\n unvested: unvestedBalance,\n all: vestedBalance + unvestedBalance,\n },\n });\n\n // Log any errors\n if (result.errors.length > 0) {\n console.warn(`Vesting classification errors: ${result.errors.length}`);\n result.errors.slice(0, 5).forEach((err) => {\n const txHash = err.utxo.tx_hash || err.utxo.txid;\n console.warn(` ${txHash}: ${err.error}`);\n });\n }\n } finally {\n this.classificationInProgress = false;\n }\n }\n\n /**\n * Get filtered UTXOs based on current vesting mode\n */\n getFilteredUtxos(address: string): ClassifiedUTXO[] {\n const cache = this.addressCache.get(address);\n if (!cache) return [];\n\n switch (this.currentMode) {\n case \"vested\":\n return cache.classifiedUtxos.vested;\n case \"unvested\":\n return cache.classifiedUtxos.unvested;\n default:\n return cache.classifiedUtxos.all;\n }\n }\n\n /**\n * Get all UTXOs regardless of vesting mode (for transactions)\n */\n getAllUtxos(address: string): ClassifiedUTXO[] {\n const cache = this.addressCache.get(address);\n if (!cache) return [];\n return cache.classifiedUtxos.all;\n }\n\n /**\n * Get balance for current vesting mode (in satoshis)\n */\n getBalance(address: string): bigint {\n const cache = this.addressCache.get(address);\n if (!cache) return 0n;\n\n return cache.vestingBalances[this.currentMode];\n }\n\n /**\n * Get all balances for display\n */\n getAllBalances(address: string): VestingBalances {\n const cache = this.addressCache.get(address);\n if (!cache) {\n return { vested: 0n, unvested: 0n, all: 0n };\n }\n return cache.vestingBalances;\n }\n\n /**\n * Check if address has been classified\n */\n hasClassifiedData(address: string): boolean {\n return this.addressCache.has(address);\n }\n\n /**\n * Check if classification is in progress\n */\n isClassifying(): boolean {\n return this.classificationInProgress;\n }\n\n /**\n * Clear cache for an address\n */\n clearAddressCache(address: string): void {\n this.addressCache.delete(address);\n }\n\n /**\n * Clear all caches\n */\n clearAllCaches(): void {\n this.addressCache.clear();\n vestingClassifier.clearCaches();\n }\n}\n\nexport const vestingState = new VestingStateManager();\n","/**\n * WalletAddressHelper - Path-based address lookup and mutation utilities\n *\n * Key principle: A BIP32 path ALWAYS derives the same address from a given master key.\n * - If we try to add a different address for an existing path → FATAL ERROR\n * - This indicates corruption, wrong derivation, or data integrity issue\n *\n * Performance: O(n) lookup is negligible for typical wallet sizes (5-100 addresses)\n */\n\nimport type { Wallet, WalletAddress } from \"./types\";\n\nexport class WalletAddressHelper {\n /**\n * Find address by BIP32 derivation path\n * @param wallet - The wallet to search\n * @param path - Full BIP32 path like \"m/84'/1'/0'/0/5\"\n * @returns The address if found, undefined otherwise\n */\n static findByPath(wallet: Wallet, path: string): WalletAddress | undefined {\n return wallet.addresses.find((a) => a.path === path);\n }\n\n /**\n * Get the default address (first external/non-change address)\n * This replaces `wallet.addresses[0]` pattern for safer access\n *\n * @param wallet - The wallet\n * @returns First non-change address, or first address if all are change\n */\n static getDefault(wallet: Wallet): WalletAddress {\n return wallet.addresses.find((a) => !a.isChange) ?? wallet.addresses[0];\n }\n\n /**\n * Get the default address, or undefined if wallet has no addresses\n * Safe version that doesn't throw on empty wallet\n */\n static getDefaultOrNull(wallet: Wallet): WalletAddress | undefined {\n if (!wallet.addresses || wallet.addresses.length === 0) {\n return undefined;\n }\n return wallet.addresses.find((a) => !a.isChange) ?? wallet.addresses[0];\n }\n\n /**\n * Add new address to wallet (immutable operation)\n *\n * THROWS if address with same path but different address string already exists.\n * This indicates a serious derivation or data corruption issue.\n *\n * If the same path+address already exists, returns wallet unchanged (idempotent).\n *\n * @param wallet - The wallet to add to\n * @param newAddress - The address to add\n * @returns New wallet object with address added\n * @throws Error if path exists with different address (corruption indicator)\n */\n static add(wallet: Wallet, newAddress: WalletAddress): Wallet {\n if (!newAddress.path) {\n throw new Error(\"Cannot add address without a path\");\n }\n\n const existing = this.findByPath(wallet, newAddress.path);\n\n if (existing) {\n // Path exists - verify it's the SAME address\n if (existing.address !== newAddress.address) {\n throw new Error(\n `CRITICAL: Attempted to overwrite address for path ${newAddress.path}\\n` +\n `Existing: ${existing.address}\\n` +\n `New: ${newAddress.address}\\n` +\n `This indicates master key corruption or derivation logic error.`\n );\n }\n\n // Same path + same address = idempotent, return unchanged\n return wallet;\n }\n\n // New path - add to array\n return {\n ...wallet,\n addresses: [...wallet.addresses, newAddress],\n };\n }\n\n /**\n * Remove address by path (immutable operation)\n * @param wallet - The wallet to modify\n * @param path - The path of the address to remove\n * @returns New wallet object with address removed\n */\n static removeByPath(wallet: Wallet, path: string): Wallet {\n return {\n ...wallet,\n addresses: wallet.addresses.filter((a) => a.path !== path),\n };\n }\n\n /**\n * Get all external (non-change) addresses\n * @param wallet - The wallet\n * @returns Array of external addresses\n */\n static getExternal(wallet: Wallet): WalletAddress[] {\n return wallet.addresses.filter((a) => !a.isChange);\n }\n\n /**\n * Get all change addresses\n * @param wallet - The wallet\n * @returns Array of change addresses\n */\n static getChange(wallet: Wallet): WalletAddress[] {\n return wallet.addresses.filter((a) => a.isChange);\n }\n\n /**\n * Check if wallet has an address with the given path\n * @param wallet - The wallet to check\n * @param path - The path to look for\n * @returns true if path exists\n */\n static hasPath(wallet: Wallet, path: string): boolean {\n return wallet.addresses.some((a) => a.path === path);\n }\n\n /**\n * Validate wallet address array integrity\n * Checks for duplicate paths which indicate data corruption\n *\n * @param wallet - The wallet to validate\n * @throws Error if duplicate paths found\n */\n static validate(wallet: Wallet): void {\n const paths = wallet.addresses.map((a) => a.path).filter(Boolean);\n const uniquePaths = new Set(paths);\n\n if (paths.length !== uniquePaths.size) {\n // Find duplicates for error message\n const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);\n throw new Error(\n `CRITICAL: Wallet has duplicate paths: ${duplicates.join(\", \")}\\n` +\n `This indicates data corruption. Please restore from backup.`\n );\n }\n }\n\n /**\n * Sort addresses with external first, then change, each sorted by index\n * Useful for display purposes\n *\n * @param wallet - The wallet\n * @returns New wallet with sorted addresses\n */\n static sortAddresses(wallet: Wallet): Wallet {\n const sorted = [...wallet.addresses].sort((a, b) => {\n // External addresses first (isChange = false/undefined)\n const aIsChange = a.isChange ? 1 : 0;\n const bIsChange = b.isChange ? 1 : 0;\n if (aIsChange !== bIsChange) return aIsChange - bIsChange;\n // Then by index\n return a.index - b.index;\n });\n\n return {\n ...wallet,\n addresses: sorted,\n };\n }\n}\n","/**\n * L1 Payments Sub-Module\n *\n * Handles Layer 1 (ALPHA blockchain) transactions including:\n * - Balance queries\n * - UTXO management\n * - Transaction sending\n * - Vesting classification\n * - Transaction history\n */\n\nimport type { FullIdentity } from '../../types';\nimport type { TransportProvider } from '../../transport';\nimport {\n connect as l1Connect,\n disconnect as l1Disconnect,\n isWebSocketConnected,\n getUtxo,\n getBalance as l1GetBalance,\n getTransactionHistory,\n getTransaction as l1GetTransaction,\n getCurrentBlockHeight,\n sendAlpha as l1SendAlpha,\n createTransactionPlan as l1CreateTransactionPlan,\n vestingClassifier,\n VESTING_THRESHOLD,\n type UTXO,\n type Wallet,\n type TransactionDetail,\n} from '../../l1';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface L1SendRequest {\n /** Recipient address */\n to: string;\n /** Amount in satoshis */\n amount: string;\n /** Fee rate in sat/byte */\n feeRate?: number;\n /** Use vested coins only */\n useVested?: boolean;\n /** Memo/OP_RETURN data */\n memo?: string;\n}\n\nexport interface L1SendResult {\n success: boolean;\n txHash?: string;\n fee?: string;\n error?: string;\n}\n\nexport interface L1Balance {\n confirmed: string;\n unconfirmed: string;\n vested: string;\n unvested: string;\n total: string;\n}\n\nexport interface L1Utxo {\n txid: string;\n vout: number;\n amount: string;\n address: string;\n isVested: boolean;\n confirmations: number;\n coinbaseHeight?: number;\n}\n\nexport interface L1Transaction {\n txid: string;\n type: 'send' | 'receive';\n amount: string;\n fee?: string;\n address: string;\n confirmations: number;\n timestamp: number;\n blockHeight?: number;\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface L1PaymentsModuleConfig {\n /** Fulcrum server URL */\n electrumUrl?: string;\n /** Network: mainnet or testnet */\n network?: 'mainnet' | 'testnet';\n /** Default fee rate */\n defaultFeeRate?: number;\n /** Enable vesting classification */\n enableVesting?: boolean;\n}\n\n// =============================================================================\n// Dependencies\n// =============================================================================\n\nexport interface L1PaymentsModuleDependencies {\n identity: FullIdentity;\n chainCode?: string;\n addresses?: string[];\n /** Transport provider for nametag resolution (optional) */\n transport?: TransportProvider;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * L1 Payments Module - Full Implementation\n *\n * Handles all L1 (ALPHA blockchain) operations including balance queries,\n * transaction sending, UTXO management, and vesting classification.\n */\nexport class L1PaymentsModule {\n private _initialized = false;\n private _config: L1PaymentsModuleConfig;\n private _identity?: FullIdentity;\n private _addresses: string[] = [];\n private _wallet?: Wallet;\n private _transport?: TransportProvider;\n\n constructor(config?: L1PaymentsModuleConfig) {\n this._config = {\n electrumUrl: config?.electrumUrl ?? 'wss://fulcrum.unicity.network:50004',\n network: config?.network ?? 'mainnet',\n defaultFeeRate: config?.defaultFeeRate ?? 10,\n enableVesting: config?.enableVesting ?? true,\n };\n }\n\n async initialize(deps: L1PaymentsModuleDependencies): Promise<void> {\n this._identity = deps.identity;\n this._addresses = deps.addresses ?? [];\n this._transport = deps.transport;\n\n // Build wallet object for L1 SDK functions\n this._wallet = {\n masterPrivateKey: deps.identity.privateKey,\n chainCode: deps.chainCode,\n addresses: [\n {\n address: deps.identity.l1Address,\n publicKey: deps.identity.chainPubkey,\n privateKey: deps.identity.privateKey,\n path: 'm/0',\n index: 0,\n },\n ],\n };\n\n // Add additional addresses\n for (const addr of this._addresses) {\n if (addr !== deps.identity.l1Address) {\n this._wallet.addresses.push({\n address: addr,\n path: null,\n index: this._wallet.addresses.length,\n });\n }\n }\n\n // NOTE: We do NOT connect to Fulcrum here. Connection is deferred to\n // first use (ensureConnected) so that import + scan flows are not\n // disrupted by an early L1 WebSocket connection on the global singleton.\n\n this._initialized = true;\n }\n\n /**\n * Ensure the Fulcrum WebSocket is connected. Called lazily before any\n * operation that needs the network. If the singleton is already connected\n * (e.g. by the address scanner), this is a no-op.\n */\n private async ensureConnected(): Promise<void> {\n if (!isWebSocketConnected() && this._config.electrumUrl) {\n await l1Connect(this._config.electrumUrl);\n }\n }\n\n destroy(): void {\n if (isWebSocketConnected()) {\n l1Disconnect();\n }\n this._initialized = false;\n this._identity = undefined;\n this._addresses = [];\n this._wallet = undefined;\n }\n\n /**\n * Check if a string looks like an L1 address (alpha1... or alphat1...)\n */\n private isL1Address(value: string): boolean {\n return value.startsWith('alpha1') || value.startsWith('alphat1');\n }\n\n /**\n * Resolve recipient to L1 address\n * Supports: L1 address (alpha1...), nametag (with or without @)\n */\n async resolveL1Address(recipient: string): Promise<string> {\n // Explicit nametag with @\n if (recipient.startsWith('@')) {\n const nametag = recipient.slice(1);\n return this.resolveNametagToL1Address(nametag);\n }\n\n // If it looks like an L1 address, return as-is\n if (this.isL1Address(recipient)) {\n return recipient;\n }\n\n // Smart detection: try as nametag\n try {\n const l1Address = await this.resolveNametagToL1Address(recipient);\n return l1Address;\n } catch {\n throw new Error(\n `Recipient \"${recipient}\" is not a valid nametag or L1 address. ` +\n `Use @nametag for explicit nametag or a valid alpha1... address.`\n );\n }\n }\n\n /**\n * Resolve nametag to L1 address using transport provider\n */\n private async resolveNametagToL1Address(nametag: string): Promise<string> {\n if (!this._transport?.resolve) {\n throw new Error('Transport provider does not support resolution');\n }\n\n const info = await this._transport.resolve(nametag);\n if (!info) {\n throw new Error(`Nametag not found: ${nametag}`);\n }\n\n if (!info.l1Address) {\n throw new Error(\n `Nametag @${nametag} does not have L1 address information. ` +\n `The owner needs to update their nametag registration.`\n );\n }\n\n return info.l1Address;\n }\n\n async send(request: L1SendRequest): Promise<L1SendResult> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n if (!this._wallet || !this._identity) {\n return { success: false, error: 'No wallet available' };\n }\n\n try {\n // Resolve recipient to L1 address (supports nametag)\n const recipientAddress = await this.resolveL1Address(request.to);\n\n // Convert amount from satoshis to ALPHA\n const amountAlpha = parseInt(request.amount, 10) / 100_000_000;\n\n // Send using the L1 SDK\n const results = await l1SendAlpha(\n this._wallet,\n recipientAddress,\n amountAlpha,\n this._identity.l1Address\n );\n\n if (results && results.length > 0) {\n // Calculate total fee from all transactions\n const txids = results.map((r) => r.txid);\n return {\n success: true,\n txHash: txids[0], // Return first txid (usually only one)\n };\n } else {\n return {\n success: false,\n error: 'Transaction failed - no results returned',\n };\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n async getBalance(): Promise<L1Balance> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const addresses = this._getWatchedAddresses();\n let totalAlpha = 0;\n let vestedSats = BigInt(0);\n let unvestedSats = BigInt(0);\n\n // Get balance for all addresses\n for (const address of addresses) {\n const balance = await l1GetBalance(address);\n totalAlpha += balance;\n }\n\n const totalSats = BigInt(Math.floor(totalAlpha * 100_000_000));\n\n // Calculate vesting if enabled\n if (this._config.enableVesting) {\n await vestingClassifier.initDB();\n const allUtxos = await this._getAllUtxos();\n const classified = await vestingClassifier.classifyUtxos(allUtxos);\n\n for (const utxo of classified.vested) {\n vestedSats += BigInt(utxo.value);\n }\n for (const utxo of classified.unvested) {\n unvestedSats += BigInt(utxo.value);\n }\n }\n\n return {\n confirmed: totalSats.toString(),\n unconfirmed: '0', // Simplified - would need separate tracking\n vested: vestedSats.toString(),\n unvested: unvestedSats.toString(),\n total: totalSats.toString(),\n };\n }\n\n async getUtxos(): Promise<L1Utxo[]> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const result: L1Utxo[] = [];\n const currentHeight = await getCurrentBlockHeight();\n const allUtxos = await this._getAllUtxos();\n\n // Classify if vesting is enabled\n const classifiedVested: Set<string> = new Set();\n const classifiedCoinbaseHeights: Map<string, number | null> = new Map();\n\n if (this._config.enableVesting) {\n await vestingClassifier.initDB();\n const classified = await vestingClassifier.classifyUtxos(allUtxos);\n\n for (const utxo of classified.vested) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n classifiedVested.add(key);\n classifiedCoinbaseHeights.set(key, utxo.coinbaseHeight ?? null);\n }\n for (const utxo of classified.unvested) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n classifiedCoinbaseHeights.set(key, utxo.coinbaseHeight ?? null);\n }\n }\n\n for (const utxo of allUtxos) {\n const key = `${utxo.tx_hash}:${utxo.tx_pos}`;\n const isVested = classifiedVested.has(key);\n const coinbaseHeight = classifiedCoinbaseHeights.get(key) ?? undefined;\n\n result.push({\n txid: utxo.tx_hash ?? utxo.txid ?? '',\n vout: utxo.tx_pos ?? utxo.vout ?? 0,\n amount: utxo.value.toString(),\n address: utxo.address ?? '',\n isVested,\n confirmations: currentHeight - (utxo.height || currentHeight),\n coinbaseHeight: coinbaseHeight ?? undefined,\n });\n }\n\n return result;\n }\n\n async getHistory(limit?: number): Promise<L1Transaction[]> {\n await this.ensureConnected();\n this.ensureInitialized();\n\n const addresses = this._getWatchedAddresses();\n const transactions: L1Transaction[] = [];\n const seenTxids = new Set<string>();\n const currentHeight = await getCurrentBlockHeight();\n\n // Cache for fetched transactions (avoids re-fetching the same tx)\n const txCache = new Map<string, TransactionDetail | null>();\n const fetchTx = async (txid: string): Promise<TransactionDetail | null> => {\n if (txCache.has(txid)) return txCache.get(txid)!;\n const detail = (await l1GetTransaction(txid)) as TransactionDetail | null;\n txCache.set(txid, detail);\n return detail;\n };\n\n const addressSet = new Set(addresses.map((a) => a.toLowerCase()));\n\n for (const address of addresses) {\n const history = await getTransactionHistory(address);\n\n for (const item of history) {\n if (seenTxids.has(item.tx_hash)) continue;\n seenTxids.add(item.tx_hash);\n\n const tx = await fetchTx(item.tx_hash);\n if (!tx) continue;\n\n // Resolve input addresses by looking up previous transactions\n let isSend = false;\n for (const vin of (tx.vin ?? [])) {\n if (!vin.txid) continue;\n const prevTx = await fetchTx(vin.txid);\n if (prevTx?.vout?.[vin.vout]) {\n const prevOut = prevTx.vout[vin.vout];\n const prevAddrs = [\n ...(prevOut.scriptPubKey?.addresses ?? []),\n ...(prevOut.scriptPubKey?.address ? [prevOut.scriptPubKey.address] : []),\n ];\n if (prevAddrs.some((a) => addressSet.has(a.toLowerCase()))) {\n isSend = true;\n break;\n }\n }\n }\n\n // Calculate amounts: sum outputs to us vs outputs to others\n let amountToUs = 0;\n let amountToOthers = 0;\n let txAddress = address;\n let externalAddress = '';\n if (tx.vout) {\n for (const vout of tx.vout) {\n const voutAddresses = [\n ...(vout.scriptPubKey?.addresses ?? []),\n ...(vout.scriptPubKey?.address ? [vout.scriptPubKey.address] : []),\n ];\n const isOurs = voutAddresses.some((a) => addressSet.has(a.toLowerCase()));\n const valueSats = Math.floor((vout.value ?? 0) * 100_000_000);\n if (isOurs) {\n amountToUs += valueSats;\n if (!txAddress) txAddress = voutAddresses[0];\n } else {\n amountToOthers += valueSats;\n if (!externalAddress && voutAddresses.length > 0) {\n externalAddress = voutAddresses[0];\n }\n }\n }\n }\n\n // For sends: amount is what went to external addresses; address is the recipient\n // For receives: amount is what came to us; address is our address\n const amount = isSend ? amountToOthers.toString() : amountToUs.toString();\n const displayAddress = isSend ? (externalAddress || txAddress) : txAddress;\n\n transactions.push({\n txid: item.tx_hash,\n type: isSend ? 'send' : 'receive',\n amount,\n address: displayAddress,\n confirmations: item.height > 0 ? currentHeight - item.height : 0,\n timestamp: tx.time ? tx.time * 1000 : Date.now(),\n blockHeight: item.height > 0 ? item.height : undefined,\n });\n }\n }\n\n // Sort by block height descending\n transactions.sort((a, b) => (b.blockHeight ?? 0) - (a.blockHeight ?? 0));\n\n return limit ? transactions.slice(0, limit) : transactions;\n }\n\n async getTransaction(txid: string): Promise<L1Transaction | null> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n const tx = (await l1GetTransaction(txid)) as TransactionDetail | null;\n if (!tx) return null;\n\n const addresses = this._getWatchedAddresses();\n const currentHeight = await getCurrentBlockHeight();\n\n // Determine if this is a send (our address in inputs)\n const isSend = tx.vin?.some((vin) =>\n addresses.includes(vin.txid ?? '')\n );\n\n let amount = '0';\n let txAddress = '';\n if (tx.vout) {\n for (const vout of tx.vout) {\n const voutAddresses = vout.scriptPubKey?.addresses ?? [];\n if (vout.scriptPubKey?.address) {\n voutAddresses.push(vout.scriptPubKey.address);\n }\n const matchedAddr = voutAddresses.find((a) => addresses.includes(a));\n if (matchedAddr) {\n amount = Math.floor((vout.value ?? 0) * 100_000_000).toString();\n txAddress = matchedAddr;\n break;\n }\n }\n }\n\n return {\n txid,\n type: isSend ? 'send' : 'receive',\n amount,\n address: txAddress,\n confirmations: tx.confirmations ?? 0,\n timestamp: tx.time ? tx.time * 1000 : Date.now(),\n blockHeight: tx.confirmations ? currentHeight - tx.confirmations + 1 : undefined,\n };\n }\n\n async estimateFee(\n to: string,\n amount: string\n ): Promise<{ fee: string; feeRate: number }> {\n this.ensureInitialized();\n await this.ensureConnected();\n\n if (!this._wallet) {\n return { fee: '0', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n\n try {\n // Convert satoshis to ALPHA\n const amountAlpha = parseInt(amount, 10) / 100_000_000;\n\n const plan = await l1CreateTransactionPlan(\n this._wallet,\n to,\n amountAlpha\n );\n\n if (!plan.success) {\n return { fee: '0', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n\n // Sum fees from all transactions\n const totalFee = plan.transactions.reduce((sum, tx) => sum + tx.fee, 0);\n\n return {\n fee: totalFee.toString(),\n feeRate: this._config.defaultFeeRate ?? 10,\n };\n } catch {\n return { fee: '10000', feeRate: this._config.defaultFeeRate ?? 10 };\n }\n }\n\n getAddresses(): string[] {\n return [...this._addresses];\n }\n\n addAddress(address: string): void {\n if (!this._addresses.includes(address)) {\n this._addresses.push(address);\n\n // Also add to wallet object\n if (this._wallet) {\n this._wallet.addresses.push({\n address,\n path: null,\n index: this._wallet.addresses.length,\n });\n }\n }\n }\n\n getVestingThreshold(): number {\n return VESTING_THRESHOLD;\n }\n\n isConnected(): boolean {\n return isWebSocketConnected();\n }\n\n private ensureInitialized(): void {\n if (!this._initialized) {\n throw new Error('L1PaymentsModule not initialized');\n }\n }\n\n private _getWatchedAddresses(): string[] {\n const addresses = [...this._addresses];\n if (this._identity?.l1Address && !addresses.includes(this._identity.l1Address)) {\n addresses.unshift(this._identity.l1Address);\n }\n return addresses;\n }\n\n private async _getAllUtxos(): Promise<UTXO[]> {\n const addresses = this._getWatchedAddresses();\n const allUtxos: UTXO[] = [];\n\n for (const addr of addresses) {\n const utxos = await getUtxo(addr);\n allUtxos.push(...utxos);\n }\n\n return allUtxos;\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createL1PaymentsModule(\n config?: L1PaymentsModuleConfig\n): L1PaymentsModule {\n return new L1PaymentsModule(config);\n}\n","/**\n * Token Split Calculator\n * Calculates optimal token splits for partial transfers\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { Token } from '../../types';\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TokenWithAmount {\n sdkToken: SdkToken<any>;\n amount: bigint;\n uiToken: Token;\n}\n\nexport interface SplitPlan {\n /** Tokens that can be transferred directly (exact match or combination) */\n tokensToTransferDirectly: TokenWithAmount[];\n /** Token that needs to be split (if requiresSplit is true) */\n tokenToSplit: TokenWithAmount | null;\n /** Amount to send to recipient from split token */\n splitAmount: bigint | null;\n /** Amount to keep as change from split token */\n remainderAmount: bigint | null;\n /** Total amount being transferred */\n totalTransferAmount: bigint;\n /** Coin type being transferred */\n coinId: string;\n /** Whether a split operation is required */\n requiresSplit: boolean;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenSplitCalculator {\n /**\n * Calculate optimal split plan for transferring a specific amount\n *\n * Strategy:\n * 1. Try to find exact match (single token = amount)\n * 2. Try to find combination of tokens that sum to exact amount\n * 3. If no exact match, determine which token to split\n */\n async calculateOptimalSplit(\n availableTokens: Token[],\n targetAmount: bigint,\n targetCoinIdHex: string\n ): Promise<SplitPlan | null> {\n const candidates: TokenWithAmount[] = [];\n\n // Build candidate list from available tokens\n for (const t of availableTokens) {\n if (t.coinId !== targetCoinIdHex) continue;\n if (t.status !== 'confirmed') continue;\n if (!t.sdkData) continue;\n\n try {\n const parsed = JSON.parse(t.sdkData);\n const sdkToken = await SdkToken.fromJSON(parsed);\n const realAmount = this.getTokenBalance(sdkToken, targetCoinIdHex);\n\n if (realAmount <= 0n) {\n console.warn(`[SplitCalculator] Token ${t.id} has 0 balance for coinId ${targetCoinIdHex}`);\n continue;\n }\n\n candidates.push({\n sdkToken,\n amount: realAmount,\n uiToken: t,\n });\n } catch (e) {\n console.warn('[SplitCalculator] Failed to parse token', t.id, e);\n }\n }\n\n // Sort by amount (ascending) for greedy algorithm\n candidates.sort((a, b) => (a.amount < b.amount ? -1 : 1));\n\n // Check total available\n const totalAvailable = candidates.reduce((sum, t) => sum + t.amount, 0n);\n if (totalAvailable < targetAmount) {\n console.error(\n `[SplitCalculator] Insufficient funds. Available: ${totalAvailable}, Required: ${targetAmount}`\n );\n return null;\n }\n\n // Strategy 1: Find exact match\n const exactMatch = candidates.find((t) => t.amount === targetAmount);\n if (exactMatch) {\n return this.createDirectPlan([exactMatch], targetAmount, targetCoinIdHex);\n }\n\n // Strategy 2: Try to find combination of tokens (up to 5)\n const maxCombinationSize = Math.min(5, candidates.length);\n for (let size = 2; size <= maxCombinationSize; size++) {\n const combo = this.findCombinationOfSize(candidates, targetAmount, size);\n if (combo) {\n return this.createDirectPlan(combo, targetAmount, targetCoinIdHex);\n }\n }\n\n // Strategy 3: Greedy selection with split\n const toTransfer: TokenWithAmount[] = [];\n let currentSum = 0n;\n\n for (const candidate of candidates) {\n const newSum = currentSum + candidate.amount;\n\n if (newSum === targetAmount) {\n // Perfect match found during greedy\n toTransfer.push(candidate);\n return this.createDirectPlan(toTransfer, targetAmount, targetCoinIdHex);\n } else if (newSum < targetAmount) {\n // Add to transfer set\n toTransfer.push(candidate);\n currentSum = newSum;\n } else {\n // Need to split this token\n const neededFromThisToken = targetAmount - currentSum;\n const remainderForSender = candidate.amount - neededFromThisToken;\n\n return {\n tokensToTransferDirectly: toTransfer,\n tokenToSplit: candidate,\n splitAmount: neededFromThisToken,\n remainderAmount: remainderForSender,\n totalTransferAmount: targetAmount,\n coinId: targetCoinIdHex,\n requiresSplit: true,\n };\n }\n }\n\n // Should not reach here if totalAvailable >= targetAmount\n return null;\n }\n\n /**\n * Get balance of a specific coin from token (lottery-compatible)\n */\n private getTokenBalance(sdkToken: SdkToken<any>, coinIdHex: string): bigint {\n try {\n if (!sdkToken.coins) return 0n;\n const coinId = CoinId.fromJSON(coinIdHex);\n return sdkToken.coins.get(coinId) ?? 0n;\n } catch {\n return 0n;\n }\n }\n\n /**\n * Create a plan for direct transfer (no split needed)\n */\n private createDirectPlan(\n tokens: TokenWithAmount[],\n total: bigint,\n coinId: string\n ): SplitPlan {\n return {\n tokensToTransferDirectly: tokens,\n tokenToSplit: null,\n splitAmount: null,\n remainderAmount: null,\n totalTransferAmount: total,\n coinId,\n requiresSplit: false,\n };\n }\n\n /**\n * Find a combination of exactly `size` tokens that sum to targetAmount\n */\n private findCombinationOfSize(\n tokens: TokenWithAmount[],\n targetAmount: bigint,\n size: number\n ): TokenWithAmount[] | null {\n const generator = this.generateCombinations(tokens, size);\n\n for (const combo of generator) {\n const sum = combo.reduce((acc, t) => acc + t.amount, 0n);\n if (sum === targetAmount) {\n return combo;\n }\n }\n return null;\n }\n\n /**\n * Generator for k-combinations of tokens\n */\n private *generateCombinations(\n tokens: TokenWithAmount[],\n k: number,\n start: number = 0,\n current: TokenWithAmount[] = []\n ): Generator<TokenWithAmount[]> {\n if (k === 0) {\n yield current;\n return;\n }\n\n for (let i = start; i < tokens.length; i++) {\n yield* this.generateCombinations(tokens, k - 1, i + 1, [\n ...current,\n tokens[i],\n ]);\n }\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createTokenSplitCalculator(): TokenSplitCalculator {\n return new TokenSplitCalculator();\n}\n","/**\n * Token Split Executor\n * Token split operations for payments\n *\n * Split flow:\n * 1. Burn original token\n * 2. Mint two new tokens: one for recipient, one for sender (change)\n * 3. Create transfer commitment for recipient token\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TokenCoinData } from '@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData';\nimport { TokenSplitBuilder } from '@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SplitResult {\n tokenForRecipient: any;\n tokenForSender: any;\n recipientTransferTx: any;\n}\n\nexport interface TokenSplitExecutorConfig {\n stateTransitionClient: any;\n trustBase: any;\n signingService: any;\n}\n\n// =============================================================================\n// Hash Utilities\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenSplitExecutor {\n private client: any;\n private trustBase: any;\n private signingService: any;\n\n constructor(config: TokenSplitExecutorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n }\n\n async executeSplit(\n tokenToSplit: any,\n splitAmount: bigint,\n remainderAmount: bigint,\n coinIdHex: string,\n recipientAddress: any\n ): Promise<SplitResult> {\n const tokenIdHex = toHex(tokenToSplit.id.bytes);\n console.log(`[TokenSplitExecutor] Splitting token ${tokenIdHex.slice(0, 8)}...`);\n\n const coinId = new CoinId(fromHex(coinIdHex));\n const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}`;\n\n // Generate IDs and salts\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Create sender address\n const senderAddressRef = await UnmaskedPredicateReference.create(\n tokenToSplit.type,\n this.signingService.algorithm,\n this.signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const senderAddress = await senderAddressRef.toAddress();\n\n // Build split\n const builder = new TokenSplitBuilder();\n\n const coinDataA = TokenCoinData.create([[coinId, splitAmount]]);\n builder.createToken(recipientTokenId, tokenToSplit.type, new Uint8Array(0), coinDataA, senderAddress, recipientSalt, null);\n\n const coinDataB = TokenCoinData.create([[coinId, remainderAmount]]);\n builder.createToken(senderTokenId, tokenToSplit.type, new Uint8Array(0), coinDataB, senderAddress, senderSalt, null);\n\n const split = await builder.build(tokenToSplit);\n\n // Step 1: Burn\n console.log('[TokenSplitExecutor] Step 1: Burning original token...');\n const burnSalt = await sha256(seedString + '_burn_salt');\n const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn failed: ${burnResponse.status}`);\n }\n\n const burnInclusionProof = await waitInclusionProof(this.trustBase, this.client, burnCommitment);\n const burnTransaction = burnCommitment.toTransaction(burnInclusionProof);\n console.log('[TokenSplitExecutor] Original token burned.');\n\n // Step 2: Mint\n console.log('[TokenSplitExecutor] Step 2: Minting split tokens...');\n const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);\n\n const mintedTokensInfo: Array<{ commitment: any; inclusionProof: any; isForRecipient: boolean; tokenId: any; salt: Uint8Array }> = [];\n\n for (const commitment of mintCommitments) {\n const res = await this.client.submitMintCommitment(commitment);\n if (res.status !== 'SUCCESS' && res.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint split token failed: ${res.status}`);\n }\n\n const proof = await waitInclusionProof(this.trustBase, this.client, commitment);\n const commTokenIdHex = toHex(commitment.transactionData.tokenId.bytes);\n const recipientIdHex = toHex(recipientTokenId.bytes);\n\n mintedTokensInfo.push({\n commitment,\n inclusionProof: proof,\n isForRecipient: commTokenIdHex === recipientIdHex,\n tokenId: commitment.transactionData.tokenId,\n salt: commitment.transactionData.salt,\n });\n }\n console.log('[TokenSplitExecutor] Split tokens minted.');\n\n // Step 3: Reconstruct tokens\n const recipientInfo = mintedTokensInfo.find((t) => t.isForRecipient)!;\n const senderInfo = mintedTokensInfo.find((t) => !t.isForRecipient)!;\n\n const createToken = async (info: typeof recipientInfo, label: string) => {\n const predicate = await UnmaskedPredicate.create(info.tokenId, tokenToSplit.type, this.signingService, HashAlgorithm.SHA256, info.salt);\n const state = new TokenState(predicate, null);\n const token = await Token.mint(this.trustBase, state, info.commitment.toTransaction(info.inclusionProof));\n const verification = await token.verify(this.trustBase);\n if (!verification.isSuccessful) throw new Error(`Token verification failed: ${label}`);\n return token;\n };\n\n const recipientTokenBeforeTransfer = await createToken(recipientInfo, 'Recipient');\n const senderToken = await createToken(senderInfo, 'Sender');\n\n // Step 4: Transfer\n console.log('[TokenSplitExecutor] Step 3: Transferring to recipient...');\n const transferSalt = await sha256(seedString + '_transfer_salt');\n\n const transferCommitment = await TransferCommitment.create(\n recipientTokenBeforeTransfer,\n recipientAddress,\n transferSalt,\n null,\n null,\n this.signingService\n );\n\n const transferRes = await this.client.submitTransferCommitment(transferCommitment);\n if (transferRes.status !== 'SUCCESS' && transferRes.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer failed: ${transferRes.status}`);\n }\n\n const transferProof = await waitInclusionProof(this.trustBase, this.client, transferCommitment);\n const transferTx = transferCommitment.toTransaction(transferProof);\n\n console.log('[TokenSplitExecutor] Split transfer complete!');\n\n return {\n tokenForRecipient: recipientTokenBeforeTransfer,\n tokenForSender: senderToken,\n recipientTransferTx: transferTx,\n };\n }\n}\n\nexport function createTokenSplitExecutor(config: TokenSplitExecutorConfig): TokenSplitExecutor {\n return new TokenSplitExecutor(config);\n}\n","/**\n * Nametag Minter\n * Mints nametag tokens on-chain for PROXY address support\n *\n * Flow (same as Sphere wallet and lottery):\n * 1. Generate salt\n * 2. Create MintTransactionData from nametag\n * 3. Create MintCommitment\n * 4. Submit to aggregator\n * 5. Wait for inclusion proof\n * 6. Create Token with proof\n * 7. Return token for storage\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { DirectAddress } from '@unicitylabs/state-transition-sdk/lib/address/DirectAddress';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport { normalizeNametag } from '@unicitylabs/nostr-js-sdk';\nimport type { NametagData } from '../../types/txf';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Unicity token type for nametags\n * Same as used in Sphere wallet and lottery\n */\nconst UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface NametagMinterConfig {\n stateTransitionClient: any;\n trustBase: any;\n signingService: SigningService;\n /** Skip trust base verification (dev mode) */\n skipVerification?: boolean;\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport interface MintNametagResult {\n success: boolean;\n token?: Token<any>;\n nametagData?: NametagData;\n error?: string;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class NametagMinter {\n private client: any;\n private trustBase: any;\n private signingService: SigningService;\n private skipVerification: boolean;\n private debug: boolean;\n\n constructor(config: NametagMinterConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.skipVerification = config.skipVerification ?? false;\n this.debug = config.debug ?? false;\n }\n\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[NametagMinter]', ...args);\n }\n }\n\n /**\n * Check if a nametag is available (not already minted)\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n try {\n const stripped = nametag.startsWith('@') ? nametag.slice(1) : nametag;\n const cleanNametag = normalizeNametag(stripped);\n const nametagTokenId = await TokenId.fromNameTag(cleanNametag);\n\n const isMinted = await this.client.isMinted(this.trustBase, nametagTokenId);\n return !isMinted;\n } catch (error) {\n this.log('Error checking nametag availability:', error);\n return false;\n }\n }\n\n /**\n * Mint a nametag token on-chain\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @param ownerAddress - The owner's direct address\n * @returns MintNametagResult with token if successful\n */\n async mintNametag(\n nametag: string,\n ownerAddress: DirectAddress\n ): Promise<MintNametagResult> {\n const stripped = nametag.startsWith('@') ? nametag.slice(1) : nametag;\n const cleanNametag = normalizeNametag(stripped);\n this.log(`Starting mint for nametag: ${cleanNametag}`);\n\n try {\n // 1. Create token ID and type\n const nametagTokenId = await TokenId.fromNameTag(cleanNametag);\n const nametagTokenType = new TokenType(\n Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex')\n );\n\n // 2. Generate deterministic salt from signing key + nametag.\n // This ensures the same wallet can recover its nametag token if lost\n // from local storage, because re-minting produces the same commitment\n // and the aggregator returns REQUEST_ID_EXISTS with the same inclusion proof.\n const nametagBytes = new TextEncoder().encode(cleanNametag);\n const pubKey = this.signingService.publicKey;\n const saltInput = new Uint8Array(pubKey.length + nametagBytes.length);\n saltInput.set(pubKey, 0);\n saltInput.set(nametagBytes, pubKey.length);\n const saltBuffer = await crypto.subtle.digest('SHA-256', saltInput);\n const salt = new Uint8Array(saltBuffer);\n this.log('Generated deterministic salt');\n\n // 3. Create mint transaction data\n const mintData = await MintTransactionData.createFromNametag(\n cleanNametag,\n nametagTokenType,\n ownerAddress,\n salt,\n ownerAddress\n );\n this.log('Created MintTransactionData');\n\n // 4. Create commitment\n const commitment = await MintCommitment.create(mintData);\n this.log('Created MintCommitment');\n\n // 5. Submit to aggregator with retries\n // If the nametag was previously minted by this wallet (same deterministic salt),\n // the aggregator returns REQUEST_ID_EXISTS which is handled as success.\n const MAX_RETRIES = 3;\n let submitSuccess = false;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n this.log(`Submitting commitment (attempt ${attempt}/${MAX_RETRIES})...`);\n const response = await this.client.submitMintCommitment(commitment);\n\n if (response.status === 'SUCCESS' || response.status === 'REQUEST_ID_EXISTS') {\n this.log(`Commitment ${response.status === 'REQUEST_ID_EXISTS' ? 'already exists' : 'submitted successfully'}`);\n submitSuccess = true;\n break;\n } else {\n this.log(`Commitment failed: ${response.status}`);\n if (attempt === MAX_RETRIES) {\n return {\n success: false,\n error: `Failed to submit commitment after ${MAX_RETRIES} attempts: ${response.status}`,\n };\n }\n await new Promise(r => setTimeout(r, 1000 * attempt));\n }\n } catch (error) {\n this.log(`Attempt ${attempt} error:`, error);\n if (attempt === MAX_RETRIES) {\n return {\n success: false,\n error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n await new Promise(r => setTimeout(r, 1000 * attempt));\n }\n }\n\n if (!submitSuccess) {\n return {\n success: false,\n error: 'Failed to submit commitment after retries',\n };\n }\n\n // 6. Wait for inclusion proof\n this.log('Waiting for inclusion proof...');\n const inclusionProof = await waitInclusionProof(this.trustBase, this.client, commitment);\n this.log('Received inclusion proof');\n\n // 7. Create genesis transaction\n const genesisTransaction = commitment.toTransaction(inclusionProof);\n\n // 8. Create token predicate and state\n const nametagPredicate = await UnmaskedPredicate.create(\n nametagTokenId,\n nametagTokenType,\n this.signingService,\n HashAlgorithm.SHA256,\n salt\n );\n\n const tokenState = new TokenState(nametagPredicate, null);\n\n // 9. Create final token\n let token: Token<any>;\n\n if (this.skipVerification) {\n this.log('Creating token WITHOUT verification (dev mode)');\n const tokenJson = {\n version: '2.0',\n state: tokenState.toJSON(),\n genesis: genesisTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n token = await Token.fromJSON(tokenJson);\n } else {\n token = await Token.mint(\n this.trustBase,\n tokenState,\n genesisTransaction\n );\n }\n\n this.log(`Nametag minted successfully: ${cleanNametag}`);\n\n // 10. Create NametagData for storage\n const nametagData: NametagData = {\n name: cleanNametag,\n token: token.toJSON(),\n timestamp: Date.now(),\n format: 'txf',\n version: '2.0',\n };\n\n return {\n success: true,\n token,\n nametagData,\n };\n } catch (error) {\n this.log('Minting failed:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createNametagMinter(config: NametagMinterConfig): NametagMinter {\n return new NametagMinter(config);\n}\n","/**\n * SDK2 Constants\n * Default configuration values and storage keys\n */\n\n// =============================================================================\n// Storage Keys\n// =============================================================================\n\n/** Default prefix for all storage keys */\nexport const STORAGE_PREFIX = 'sphere_' as const;\n\n/**\n * Default encryption key for wallet data\n * WARNING: This is a placeholder. In production, use user-provided password.\n * This key is used when no password is provided to encrypt/decrypt mnemonic.\n */\nexport const DEFAULT_ENCRYPTION_KEY = 'sphere-default-key' as const;\n\n/**\n * Global storage keys (one per wallet, no address index)\n * Final key format: sphere_{key}\n */\nexport const STORAGE_KEYS_GLOBAL = {\n /** Encrypted BIP39 mnemonic */\n MNEMONIC: 'mnemonic',\n /** Encrypted master private key */\n MASTER_KEY: 'master_key',\n /** BIP32 chain code */\n CHAIN_CODE: 'chain_code',\n /** HD derivation path (full path like m/44'/0'/0'/0/0) */\n DERIVATION_PATH: 'derivation_path',\n /** Base derivation path (like m/44'/0'/0' without chain/index) */\n BASE_PATH: 'base_path',\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n DERIVATION_MODE: 'derivation_mode',\n /** Wallet source: mnemonic, file, unknown */\n WALLET_SOURCE: 'wallet_source',\n /** Wallet existence flag */\n WALLET_EXISTS: 'wallet_exists',\n /** Current active address index */\n CURRENT_ADDRESS_INDEX: 'current_address_index',\n /** Nametag cache per address (separate from tracked addresses registry) */\n ADDRESS_NAMETAGS: 'address_nametags',\n /** Active addresses registry (JSON: TrackedAddressesStorage) */\n TRACKED_ADDRESSES: 'tracked_addresses',\n /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */\n LAST_WALLET_EVENT_TS: 'last_wallet_event_ts',\n /** Group chat: joined groups */\n GROUP_CHAT_GROUPS: 'group_chat_groups',\n /** Group chat: messages */\n GROUP_CHAT_MESSAGES: 'group_chat_messages',\n /** Group chat: members */\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 /** Group chat: last used relay URL (stale data detection) */\n GROUP_CHAT_RELAY_URL: 'group_chat_relay_url',\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} as const;\n\n/** @deprecated Use STORAGE_KEYS_GLOBAL and STORAGE_KEYS_ADDRESS instead */\nexport const STORAGE_KEYS = {\n ...STORAGE_KEYS_GLOBAL,\n ...STORAGE_KEYS_ADDRESS,\n} as const;\n\n/**\n * Build a per-address storage key using address identifier\n * @param addressId - Short identifier for the address (e.g., first 8 chars of pubkey hash, or direct address hash)\n * @param key - The key from STORAGE_KEYS_ADDRESS\n * @returns Key in format: \"{addressId}_{key}\" e.g., \"a1b2c3d4_tokens\"\n */\nexport function getAddressStorageKey(addressId: string, key: string): string {\n return `${addressId}_${key}`;\n}\n\n/**\n * Create a readable address identifier from directAddress or chainPubkey\n * Format: DIRECT_first6_last6 (sanitized for filesystem/storage)\n * @param directAddress - The L3 direct address (DIRECT:xxx) or chainPubkey\n * @returns Sanitized identifier like \"DIRECT_abc123_xyz789\"\n */\nexport function getAddressId(directAddress: string): string {\n // Remove DIRECT:// or DIRECT: prefix if present\n let hash = directAddress;\n if (hash.startsWith('DIRECT://')) {\n hash = hash.slice(9);\n } else if (hash.startsWith('DIRECT:')) {\n hash = hash.slice(7);\n }\n // Format: DIRECT_first6_last6 (sanitized)\n const first = hash.slice(0, 6).toLowerCase();\n const last = hash.slice(-6).toLowerCase();\n return `DIRECT_${first}_${last}`;\n}\n\n// =============================================================================\n// Nostr Defaults\n// =============================================================================\n\n/** Default Nostr relays */\nexport const DEFAULT_NOSTR_RELAYS = [\n 'wss://relay.unicity.network',\n 'wss://relay.damus.io',\n 'wss://nos.lol',\n 'wss://relay.nostr.band',\n] as const;\n\n/** Nostr event kinds used by SDK - must match @unicitylabs/nostr-js-sdk */\nexport const NOSTR_EVENT_KINDS = {\n /** NIP-04 encrypted direct message */\n DIRECT_MESSAGE: 4,\n /** Token transfer (Unicity custom - 31113) */\n TOKEN_TRANSFER: 31113,\n /** Payment request (Unicity custom - 31115) */\n PAYMENT_REQUEST: 31115,\n /** Payment request response (Unicity custom - 31116) */\n PAYMENT_REQUEST_RESPONSE: 31116,\n /** Nametag binding (NIP-78 app-specific data) */\n NAMETAG_BINDING: 30078,\n /** Public broadcast */\n BROADCAST: 1,\n} as const;\n\n/**\n * NIP-29 Event Kinds for relay-based group chat\n * https://github.com/nostr-protocol/nips/blob/master/29.md\n */\nexport const NIP29_KINDS = {\n /** Chat message sent to group */\n CHAT_MESSAGE: 9,\n /** Thread root message */\n THREAD_ROOT: 11,\n /** Thread reply message */\n THREAD_REPLY: 12,\n /** User join request */\n JOIN_REQUEST: 9021,\n /** User leave request */\n LEAVE_REQUEST: 9022,\n /** Admin: add/update user */\n PUT_USER: 9000,\n /** Admin: remove user */\n REMOVE_USER: 9001,\n /** Admin: edit group metadata */\n EDIT_METADATA: 9002,\n /** Admin: delete event */\n DELETE_EVENT: 9005,\n /** Admin: create group */\n CREATE_GROUP: 9007,\n /** Admin: delete group */\n DELETE_GROUP: 9008,\n /** Admin: create invite code */\n CREATE_INVITE: 9009,\n /** Relay-signed group metadata */\n GROUP_METADATA: 39000,\n /** Relay-signed group admins */\n GROUP_ADMINS: 39001,\n /** Relay-signed group members */\n GROUP_MEMBERS: 39002,\n /** Relay-signed group roles */\n GROUP_ROLES: 39003,\n} as const;\n\n// =============================================================================\n// Aggregator (Oracle) Defaults\n// =============================================================================\n\n/**\n * Default aggregator URL\n * Note: The aggregator is conceptually an oracle - a trusted service that provides\n * verifiable truth about token state through cryptographic inclusion proofs.\n */\nexport const DEFAULT_AGGREGATOR_URL = 'https://aggregator.unicity.network/rpc' as const;\n\n/** Dev aggregator URL */\nexport const DEV_AGGREGATOR_URL = 'https://dev-aggregator.dyndns.org/rpc' as const;\n\n/** Test aggregator URL (Goggregator) */\nexport const TEST_AGGREGATOR_URL = 'https://goggregator-test.unicity.network' as const;\n\n/** Default aggregator request timeout (ms) */\nexport const DEFAULT_AGGREGATOR_TIMEOUT = 30000;\n\n/** Default API key for aggregator authentication */\nexport const DEFAULT_AGGREGATOR_API_KEY = 'sk_06365a9c44654841a366068bcfc68986' as const;\n\n// =============================================================================\n// IPFS Defaults\n// =============================================================================\n\n/** Default IPFS gateways */\nexport const DEFAULT_IPFS_GATEWAYS = [\n 'https://ipfs.unicity.network',\n 'https://dweb.link',\n 'https://ipfs.io',\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.alpha.unicity.network:50004' as const;\n\n/** Testnet Fulcrum electrum server */\nexport const TEST_ELECTRUM_URL = 'wss://fulcrum.alpha.testnet.unicity.network:50004' as const;\n\n// =============================================================================\n// Network Defaults\n// =============================================================================\n\n/** Testnet Nostr relays */\nexport const TEST_NOSTR_RELAYS = [\n 'wss://nostr-relay.testnet.unicity.network',\n] as const;\n\n/** Default group chat relays (NIP-29 Zooid relay) */\nexport const DEFAULT_GROUP_RELAYS = [\n 'wss://sphere-relay.unicity.network',\n] as const;\n\n/** Network configurations */\nexport const NETWORKS = {\n mainnet: {\n name: 'Mainnet',\n aggregatorUrl: DEFAULT_AGGREGATOR_URL,\n nostrRelays: DEFAULT_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: DEFAULT_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n },\n testnet: {\n name: 'Testnet',\n aggregatorUrl: TEST_AGGREGATOR_URL,\n nostrRelays: TEST_NOSTR_RELAYS,\n ipfsGateways: DEFAULT_IPFS_GATEWAYS,\n electrumUrl: TEST_ELECTRUM_URL,\n groupRelays: DEFAULT_GROUP_RELAYS,\n },\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 },\n} as const;\n\nexport type NetworkType = keyof typeof NETWORKS;\nexport type NetworkConfig = (typeof NETWORKS)[NetworkType];\n\n// =============================================================================\n// Timeouts & Limits\n// =============================================================================\n\n/** Default timeouts (ms) */\nexport const TIMEOUTS = {\n /** WebSocket connection timeout */\n WEBSOCKET_CONNECT: 10000,\n /** Nostr relay reconnect delay */\n NOSTR_RECONNECT_DELAY: 3000,\n /** Max reconnect attempts */\n MAX_RECONNECT_ATTEMPTS: 5,\n /** Proof polling interval */\n PROOF_POLL_INTERVAL: 1000,\n /** Sync interval */\n SYNC_INTERVAL: 60000,\n} as const;\n\n/** Validation limits */\nexport const LIMITS = {\n /** Min nametag length */\n NAMETAG_MIN_LENGTH: 3,\n /** Max nametag length */\n NAMETAG_MAX_LENGTH: 20,\n /** Max memo length */\n MEMO_MAX_LENGTH: 500,\n /** Max message length */\n MESSAGE_MAX_LENGTH: 10000,\n} as const;\n","/**\n * TXF (Token eXchange Format) Type Definitions\n * Based on TXF Format Specification v2.0\n *\n * These types define the serialization format for tokens,\n * independent of any UI or storage implementation.\n */\n\n// =============================================================================\n// TXF Token Structure (v2.0)\n// =============================================================================\n\n/**\n * Complete token object in TXF format\n */\nexport interface TxfToken {\n version: '2.0';\n genesis: TxfGenesis;\n state: TxfState;\n transactions: TxfTransaction[];\n nametags?: string[];\n _integrity?: TxfIntegrity;\n}\n\n/**\n * Genesis transaction (initial minting)\n */\nexport interface TxfGenesis {\n data: TxfGenesisData;\n inclusionProof: TxfInclusionProof;\n}\n\n/**\n * Genesis data payload\n */\nexport interface TxfGenesisData {\n tokenId: string; // 64-char hex\n tokenType: string; // 64-char hex\n coinData: [string, string][]; // [[coinId, amount], ...]\n tokenData: string; // Optional metadata\n salt: string; // 64-char hex\n recipient: string; // DIRECT://... address\n recipientDataHash: string | null;\n reason: string | null;\n}\n\n/**\n * Current token state\n */\nexport interface TxfState {\n data: string;\n predicate: string; // Hex-encoded CBOR predicate\n}\n\n/**\n * State transition transaction\n */\nexport interface TxfTransaction {\n previousStateHash: string;\n newStateHash?: string;\n predicate: string;\n inclusionProof: TxfInclusionProof | null; // null = uncommitted\n data?: Record<string, unknown>;\n}\n\n/**\n * Sparse Merkle Tree inclusion proof\n */\nexport interface TxfInclusionProof {\n authenticator: TxfAuthenticator;\n merkleTreePath: TxfMerkleTreePath;\n transactionHash: string;\n unicityCertificate: string; // Hex-encoded CBOR\n}\n\n/**\n * Proof authenticator\n */\nexport interface TxfAuthenticator {\n algorithm: string;\n publicKey: string;\n signature: string;\n stateHash: string;\n}\n\n/**\n * Merkle tree path for proof verification\n */\nexport interface TxfMerkleTreePath {\n root: string;\n steps: TxfMerkleStep[];\n}\n\n/**\n * Single step in merkle path\n */\nexport interface TxfMerkleStep {\n data: string;\n path: string;\n}\n\n/**\n * Token integrity metadata\n */\nexport interface TxfIntegrity {\n genesisDataJSONHash: string;\n currentStateHash?: string;\n}\n\n// =============================================================================\n// Storage Format (for IPFS/File storage)\n// =============================================================================\n\n/**\n * Nametag data (one per identity)\n */\nexport interface NametagData {\n name: string;\n token: object;\n timestamp: number;\n format: string;\n version: string;\n}\n\n/**\n * Tombstone entry for tracking spent token states\n */\nexport interface TombstoneEntry {\n tokenId: string;\n stateHash: string;\n timestamp: number;\n}\n\n/**\n * Invalidated nametag entry\n */\nexport interface InvalidatedNametagEntry {\n name: string;\n token: object;\n timestamp: number;\n format: string;\n version: string;\n invalidatedAt: number;\n invalidationReason: string;\n}\n\n/**\n * Outbox entry for pending transfers\n */\nexport interface OutboxEntry {\n id: string;\n status: 'pending' | 'submitted' | 'confirmed' | 'delivered' | 'failed';\n sourceTokenId: string;\n salt: string;\n commitmentJson: string;\n recipientPubkey: string;\n recipientNametag?: string;\n amount: string;\n createdAt: number;\n updatedAt: number;\n error?: string;\n retryCount?: number;\n}\n\n/**\n * Mint outbox entry for pending mints\n */\nexport interface MintOutboxEntry {\n id: string;\n status: 'pending' | 'submitted' | 'confirmed' | 'failed';\n type: 'split' | 'faucet' | 'other';\n salt: string;\n requestIdHex: string;\n mintDataJson: string;\n createdAt: number;\n updatedAt: number;\n error?: string;\n}\n\n/**\n * Storage metadata\n */\nexport interface TxfMeta {\n version: number;\n address: string;\n ipnsName: string;\n formatVersion: '2.0';\n lastCid?: string;\n deviceId?: string;\n}\n\n/**\n * Complete storage data structure\n */\nexport interface TxfStorageData {\n _meta: TxfMeta;\n _nametag?: NametagData;\n _nametags?: NametagData[];\n _tombstones?: TombstoneEntry[];\n _invalidatedNametags?: InvalidatedNametagEntry[];\n _outbox?: OutboxEntry[];\n _mintOutbox?: MintOutboxEntry[];\n [key: string]: TxfToken | TxfMeta | NametagData | NametagData[] | TombstoneEntry[] | InvalidatedNametagEntry[] | OutboxEntry[] | MintOutboxEntry[] | undefined;\n}\n\n// =============================================================================\n// Token Storage Provider Interface\n// =============================================================================\n\n/**\n * Base interface that storage providers must implement\n * to support TXF token storage\n */\nexport interface TxfStorageDataBase {\n _meta: TxfMeta;\n _nametag?: NametagData;\n _nametags?: NametagData[];\n _tombstones?: TombstoneEntry[];\n _invalidatedNametags?: InvalidatedNametagEntry[];\n _outbox?: OutboxEntry[];\n _mintOutbox?: MintOutboxEntry[];\n [key: string]: unknown;\n}\n\n// =============================================================================\n// Validation Types\n// =============================================================================\n\nexport interface ValidationIssue {\n tokenId: string;\n reason: string;\n recoverable?: boolean;\n}\n\nexport interface TokenValidationResult {\n isValid: boolean;\n reason?: string;\n action?: 'ACCEPT' | 'RETRY_LATER' | 'DISCARD_FORK';\n}\n\n// =============================================================================\n// Key Utilities\n// =============================================================================\n\nconst ARCHIVED_PREFIX = 'archived-';\nconst FORKED_PREFIX = '_forked_';\nconst RESERVED_KEYS = ['_meta', '_nametag', '_nametags', '_tombstones', '_invalidatedNametags', '_outbox', '_mintOutbox', '_sent', '_invalid', '_integrity'];\n\n/**\n * Check if a key is an active token key\n */\nexport function isTokenKey(key: string): boolean {\n return key.startsWith('_') &&\n !key.startsWith(ARCHIVED_PREFIX) &&\n !key.startsWith(FORKED_PREFIX) &&\n !RESERVED_KEYS.includes(key);\n}\n\n/**\n * Check if a key is an archived token key\n */\nexport function isArchivedKey(key: string): boolean {\n return key.startsWith(ARCHIVED_PREFIX);\n}\n\n/**\n * Check if a key is a forked token key\n */\nexport function isForkedKey(key: string): boolean {\n return key.startsWith(FORKED_PREFIX);\n}\n\n/**\n * Extract token ID from storage key\n */\nexport function tokenIdFromKey(key: string): string {\n return key.startsWith('_') ? key.substring(1) : key;\n}\n\n/**\n * Create storage key from token ID\n */\nexport function keyFromTokenId(tokenId: string): string {\n return `_${tokenId}`;\n}\n\n/**\n * Extract token ID from archived key\n */\nexport function tokenIdFromArchivedKey(key: string): string {\n return key.startsWith(ARCHIVED_PREFIX) ? key.substring(ARCHIVED_PREFIX.length) : key;\n}\n\n/**\n * Create archived key from token ID\n */\nexport function archivedKeyFromTokenId(tokenId: string): string {\n return `${ARCHIVED_PREFIX}${tokenId}`;\n}\n\n/**\n * Create forked key from token ID and state hash\n */\nexport function forkedKeyFromTokenIdAndState(tokenId: string, stateHash: string): string {\n return `${FORKED_PREFIX}${tokenId}_${stateHash}`;\n}\n\n/**\n * Parse forked key into tokenId and stateHash\n */\nexport function parseForkedKey(key: string): { tokenId: string; stateHash: string } | null {\n if (!key.startsWith(FORKED_PREFIX)) return null;\n const remainder = key.substring(FORKED_PREFIX.length);\n const underscoreIndex = remainder.indexOf('_');\n if (underscoreIndex === -1 || underscoreIndex < 64) return null;\n return {\n tokenId: remainder.substring(0, underscoreIndex),\n stateHash: remainder.substring(underscoreIndex + 1),\n };\n}\n\n/**\n * Validate 64-character hex token ID\n */\nexport function isValidTokenId(tokenId: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(tokenId);\n}\n","[\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"non-fungible\",\n \"name\": \"unicity\",\n \"description\": \"Unicity testnet token type\",\n \"id\": \"f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity\",\n \"symbol\": \"UCT\",\n \"decimals\": 18,\n \"description\": \"Unicity testnet native coin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity_logo_32.png\" }\n ],\n \"id\": \"455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity-usd\",\n \"symbol\": \"USDU\",\n \"decimals\": 6,\n \"description\": \"Unicity testnet USD stablecoin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/usdu_logo_32.png\" }\n ],\n \"id\": \"8f0f3d7a5e7297be0ee98c63b81bcebb2740f43f616566fc290f9823a54f52d7\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"unicity-eur\",\n \"symbol\": \"EURU\",\n \"decimals\": 6,\n \"description\": \"Unicity testnet EUR stablecoin\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/euru_logo_32.png\" }\n ],\n \"id\": \"5e160d5e9fdbb03b553fb9c3f6e6c30efa41fa807be39fb4f18e43776e492925\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"solana\",\n \"symbol\": \"SOL\",\n \"decimals\": 9,\n \"description\": \"Solana testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/sol.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/sol.png\" }\n ],\n \"id\": \"dee5f8ce778562eec90e9c38a91296a023210ccc76ff4c29d527ac3eb64ade93\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"bitcoin\",\n \"symbol\": \"BTC\",\n \"decimals\": 8,\n \"description\": \"Bitcoin testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/btc.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/btc.png\" }\n ],\n \"id\": \"86bc190fcf7b2d07c6078de93db803578760148b16d4431aa2f42a3241ff0daa\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"ethereum\",\n \"symbol\": \"ETH\",\n \"decimals\": 18,\n \"description\": \"Ethereum testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/eth.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/eth.png\" }\n ],\n \"id\": \"3c2450f2fd867e7bb60c6a69d7ad0e53ce967078c201a3ecaa6074ed4c0deafb\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"alpha_test\",\n \"symbol\": \"ALPHT\",\n \"decimals\": 8,\n \"description\": \"ALPHA testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/alpha_coin.png\" }\n ],\n \"id\": \"cde78ded16ef65818a51f43138031c4284e519300ab0cb60c30a8f9078080e5f\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"tether\",\n \"symbol\": \"USDT\",\n \"decimals\": 6,\n \"description\": \"Tether (Ethereum) testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdt.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdt.png\" }\n ],\n \"id\": \"40d25444648418fe7efd433e147187a3a6adf049ac62bc46038bda5b960bf690\"\n },\n {\n \"network\": \"unicity:testnet\",\n \"assetKind\": \"fungible\",\n \"name\": \"usd-coin\",\n \"symbol\": \"USDC\",\n \"decimals\": 6,\n \"description\": \"USDC (Ethereum) testnet coin on Unicity\",\n \"icons\": [\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdc.svg\" },\n { \"url\": \"https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdc.png\" }\n ],\n \"id\": \"2265121770fa6f41131dd9a6cc571e28679263d09a53eb2642e145b5b9a5b0a2\"\n }\n]\n","/**\n * Token Registry\n *\n * Provides token definitions (metadata) for known tokens on the Unicity network.\n * Uses bundled static data for offline access and consistency.\n */\n\nimport testnetRegistry from './token-registry.testnet.json';\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// 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 * @example\n * ```ts\n * import { TokenRegistry } from '@unicitylabs/sphere-sdk';\n *\n * const registry = TokenRegistry.getInstance();\n * const def = registry.getDefinition('455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89');\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 private constructor() {\n this.definitionsById = new Map();\n this.definitionsBySymbol = new Map();\n this.definitionsByName = new Map();\n this.loadRegistry();\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 * Reset the singleton instance (useful for testing)\n */\n static resetInstance(): void {\n TokenRegistry.instance = null;\n }\n\n /**\n * Load registry data from bundled JSON\n */\n private loadRegistry(): void {\n const definitions = testnetRegistry as TokenDefinition[];\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 // 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 * TXF Serializer for SDK2\n * Converts between SDK Token format and TXF storage format\n *\n * Platform-independent implementation that works with SDK types directly.\n */\n\nimport type {\n TxfToken,\n TxfTransaction,\n TxfStorageData,\n TxfMeta,\n NametagData,\n TombstoneEntry,\n OutboxEntry,\n MintOutboxEntry,\n InvalidatedNametagEntry,\n} from '../types/txf';\nimport {\n isTokenKey,\n isArchivedKey,\n isForkedKey,\n tokenIdFromKey,\n tokenIdFromArchivedKey,\n parseForkedKey,\n keyFromTokenId,\n archivedKeyFromTokenId,\n forkedKeyFromTokenIdAndState,\n} from '../types/txf';\nimport type { Token, TokenStatus } from '../types';\nimport { TokenRegistry } from '../registry/TokenRegistry';\n\n// =============================================================================\n// SDK Token Normalization\n// =============================================================================\n\n/**\n * Convert bytes array/object to hex string\n */\nfunction bytesToHex(bytes: number[] | Uint8Array): string {\n const arr = Array.isArray(bytes) ? bytes : Array.from(bytes);\n return arr.map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Normalize a value that may be a hex string, bytes object, or Buffer to hex string\n */\nfunction normalizeToHex(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (value && typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n // SDK format: { bytes: [...] }\n if ('bytes' in obj && (Array.isArray(obj.bytes) || obj.bytes instanceof Uint8Array)) {\n return bytesToHex(obj.bytes as number[] | Uint8Array);\n }\n // Buffer.toJSON() format: { type: \"Buffer\", data: [...] }\n if (obj.type === 'Buffer' && Array.isArray(obj.data)) {\n return bytesToHex(obj.data as number[]);\n }\n }\n return String(value);\n}\n\n/**\n * Normalize SDK token JSON to canonical TXF storage format.\n * Converts all bytes objects to hex strings before storage.\n */\nexport function normalizeSdkTokenToStorage(sdkTokenJson: unknown): TxfToken {\n const txf = JSON.parse(JSON.stringify(sdkTokenJson));\n\n // Normalize genesis.data fields\n if (txf.genesis?.data) {\n const data = txf.genesis.data;\n if (data.tokenId !== undefined) {\n data.tokenId = normalizeToHex(data.tokenId);\n }\n if (data.tokenType !== undefined) {\n data.tokenType = normalizeToHex(data.tokenType);\n }\n if (data.salt !== undefined) {\n data.salt = normalizeToHex(data.salt);\n }\n }\n\n // Normalize authenticator fields in genesis inclusion proof\n if (txf.genesis?.inclusionProof?.authenticator) {\n const auth = txf.genesis.inclusionProof.authenticator;\n if (auth.publicKey !== undefined) {\n auth.publicKey = normalizeToHex(auth.publicKey);\n }\n if (auth.signature !== undefined) {\n auth.signature = normalizeToHex(auth.signature);\n }\n }\n\n // Normalize transaction authenticators\n if (Array.isArray(txf.transactions)) {\n for (const tx of txf.transactions) {\n if (tx.inclusionProof?.authenticator) {\n const auth = tx.inclusionProof.authenticator;\n if (auth.publicKey !== undefined) {\n auth.publicKey = normalizeToHex(auth.publicKey);\n }\n if (auth.signature !== undefined) {\n auth.signature = normalizeToHex(auth.signature);\n }\n }\n }\n }\n\n return txf as TxfToken;\n}\n\n// =============================================================================\n// Token → TXF Conversion\n// =============================================================================\n\n/**\n * Extract TXF token structure from Token.sdkData (jsonData)\n */\nexport function tokenToTxf(token: Token): TxfToken | null {\n const jsonData = token.sdkData;\n if (!jsonData) {\n return null;\n }\n\n try {\n const txfData = normalizeSdkTokenToStorage(JSON.parse(jsonData));\n\n if (!txfData.genesis || !txfData.state) {\n return null;\n }\n\n // Ensure required fields\n if (!txfData.version) {\n txfData.version = '2.0';\n }\n if (!txfData.transactions) {\n txfData.transactions = [];\n }\n if (!txfData.nametags) {\n txfData.nametags = [];\n }\n if (!txfData._integrity) {\n txfData._integrity = {\n genesisDataJSONHash: '0000' + '0'.repeat(60),\n };\n }\n\n return txfData;\n } catch {\n return null;\n }\n}\n\n/**\n * Convert token interface to simplified Token for parsing\n */\ninterface TokenLike {\n id: string;\n sdkData?: string;\n}\n\n/**\n * Extract TXF from any object with id and sdkData\n */\nexport function objectToTxf(obj: TokenLike): TxfToken | null {\n if (!obj.sdkData) return null;\n try {\n const txfData = normalizeSdkTokenToStorage(JSON.parse(obj.sdkData));\n if (!txfData.genesis || !txfData.state) return null;\n return txfData;\n } catch {\n return null;\n }\n}\n\n// =============================================================================\n// TXF → Token Conversion\n// =============================================================================\n\n/**\n * Determine token status from TXF data\n */\nfunction determineTokenStatus(txf: TxfToken): TokenStatus {\n if (txf.transactions.length > 0) {\n const lastTx = txf.transactions[txf.transactions.length - 1];\n if (lastTx.inclusionProof === null) {\n return 'pending';\n }\n }\n return 'confirmed';\n}\n\n/**\n * Convert TXF token to Token interface\n */\nexport function txfToToken(tokenId: string, txf: TxfToken): Token {\n const coinData = txf.genesis.data.coinData;\n const totalAmount = coinData.reduce((sum, [, amt]) => {\n return sum + BigInt(amt || '0');\n }, BigInt(0));\n\n // Get coin ID (use first non-zero coin, or first coin)\n let coinId = coinData[0]?.[0] || '';\n for (const [cid, amt] of coinData) {\n if (BigInt(amt || '0') > 0) {\n coinId = cid;\n break;\n }\n }\n\n const tokenType = txf.genesis.data.tokenType;\n const isNft = tokenType === '455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89';\n\n const now = Date.now();\n\n const registry = TokenRegistry.getInstance();\n const def = registry.getDefinition(coinId);\n\n return {\n id: tokenId,\n coinId,\n symbol: isNft ? 'NFT' : (def?.symbol || coinId.slice(0, 8)),\n name: isNft ? 'NFT' : (def?.name ? def.name.charAt(0).toUpperCase() + def.name.slice(1) : 'Token'),\n decimals: isNft ? 0 : (def?.decimals ?? 8),\n amount: totalAmount.toString(),\n status: determineTokenStatus(txf),\n createdAt: now,\n updatedAt: now,\n sdkData: JSON.stringify(txf),\n };\n}\n\n// =============================================================================\n// Storage Data Building\n// =============================================================================\n\n/**\n * Build TXF storage data from tokens and metadata\n */\nexport async function buildTxfStorageData(\n tokens: Token[],\n meta: Omit<TxfMeta, 'formatVersion'>,\n options?: {\n nametags?: NametagData[];\n tombstones?: TombstoneEntry[];\n archivedTokens?: Map<string, TxfToken>;\n forkedTokens?: Map<string, TxfToken>;\n outboxEntries?: OutboxEntry[];\n mintOutboxEntries?: MintOutboxEntry[];\n invalidatedNametags?: InvalidatedNametagEntry[];\n }\n): Promise<TxfStorageData> {\n const storageData: TxfStorageData = {\n _meta: {\n ...meta,\n formatVersion: '2.0',\n },\n };\n\n if (options?.nametags && options.nametags.length > 0) {\n storageData._nametags = options.nametags;\n }\n\n if (options?.tombstones && options.tombstones.length > 0) {\n storageData._tombstones = options.tombstones;\n }\n\n if (options?.outboxEntries && options.outboxEntries.length > 0) {\n storageData._outbox = options.outboxEntries;\n }\n\n if (options?.mintOutboxEntries && options.mintOutboxEntries.length > 0) {\n storageData._mintOutbox = options.mintOutboxEntries;\n }\n\n if (options?.invalidatedNametags && options.invalidatedNametags.length > 0) {\n storageData._invalidatedNametags = options.invalidatedNametags;\n }\n\n // Add active tokens\n for (const token of tokens) {\n const txf = tokenToTxf(token);\n if (txf) {\n const actualTokenId = txf.genesis.data.tokenId;\n storageData[keyFromTokenId(actualTokenId)] = txf;\n }\n }\n\n // Add archived tokens\n if (options?.archivedTokens && options.archivedTokens.size > 0) {\n for (const [tokenId, txf] of options.archivedTokens) {\n storageData[archivedKeyFromTokenId(tokenId)] = txf;\n }\n }\n\n // Add forked tokens\n if (options?.forkedTokens && options.forkedTokens.size > 0) {\n for (const [key, txf] of options.forkedTokens) {\n const [tokenId, stateHash] = key.split('_');\n if (tokenId && stateHash) {\n storageData[forkedKeyFromTokenIdAndState(tokenId, stateHash)] = txf;\n }\n }\n }\n\n return storageData;\n}\n\n// =============================================================================\n// Storage Data Parsing\n// =============================================================================\n\nexport interface ParsedStorageData {\n tokens: Token[];\n meta: TxfMeta | null;\n nametags: NametagData[];\n tombstones: TombstoneEntry[];\n archivedTokens: Map<string, TxfToken>;\n forkedTokens: Map<string, TxfToken>;\n outboxEntries: OutboxEntry[];\n mintOutboxEntries: MintOutboxEntry[];\n invalidatedNametags: InvalidatedNametagEntry[];\n validationErrors: string[];\n}\n\n/**\n * Parse TXF storage data\n */\nexport function parseTxfStorageData(data: unknown): ParsedStorageData {\n const result: ParsedStorageData = {\n tokens: [],\n meta: null,\n nametags: [],\n tombstones: [],\n archivedTokens: new Map(),\n forkedTokens: new Map(),\n outboxEntries: [],\n mintOutboxEntries: [],\n invalidatedNametags: [],\n validationErrors: [],\n };\n\n if (!data || typeof data !== 'object') {\n result.validationErrors.push('Storage data is not an object');\n return result;\n }\n\n const storageData = data as Record<string, unknown>;\n\n // Extract metadata\n if (storageData._meta && typeof storageData._meta === 'object') {\n result.meta = storageData._meta as TxfMeta;\n }\n\n // Extract nametags (plural, array — primary source)\n const seenNames = new Set<string>();\n if (Array.isArray(storageData._nametags)) {\n for (const entry of storageData._nametags) {\n if (entry && typeof entry === 'object' && typeof (entry as NametagData).name === 'string') {\n result.nametags.push(entry as NametagData);\n seenNames.add((entry as NametagData).name);\n }\n }\n }\n\n // Backward compat: read singular _nametag and add if not already present\n if (storageData._nametag && typeof storageData._nametag === 'object') {\n const legacy = storageData._nametag as NametagData;\n if (typeof legacy.name === 'string' && !seenNames.has(legacy.name)) {\n result.nametags.push(legacy);\n }\n }\n\n // Extract tombstones\n if (storageData._tombstones && Array.isArray(storageData._tombstones)) {\n for (const entry of storageData._tombstones) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as TombstoneEntry).tokenId === 'string' &&\n typeof (entry as TombstoneEntry).stateHash === 'string' &&\n typeof (entry as TombstoneEntry).timestamp === 'number'\n ) {\n result.tombstones.push(entry as TombstoneEntry);\n }\n }\n }\n\n // Extract outbox entries\n if (storageData._outbox && Array.isArray(storageData._outbox)) {\n for (const entry of storageData._outbox) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as OutboxEntry).id === 'string' &&\n typeof (entry as OutboxEntry).status === 'string'\n ) {\n result.outboxEntries.push(entry as OutboxEntry);\n }\n }\n }\n\n // Extract mint outbox entries\n if (storageData._mintOutbox && Array.isArray(storageData._mintOutbox)) {\n for (const entry of storageData._mintOutbox) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as MintOutboxEntry).id === 'string' &&\n typeof (entry as MintOutboxEntry).status === 'string'\n ) {\n result.mintOutboxEntries.push(entry as MintOutboxEntry);\n }\n }\n }\n\n // Extract invalidated nametags\n if (storageData._invalidatedNametags && Array.isArray(storageData._invalidatedNametags)) {\n for (const entry of storageData._invalidatedNametags) {\n if (\n typeof entry === 'object' &&\n entry !== null &&\n typeof (entry as InvalidatedNametagEntry).name === 'string'\n ) {\n result.invalidatedNametags.push(entry as InvalidatedNametagEntry);\n }\n }\n }\n\n // Extract tokens\n for (const key of Object.keys(storageData)) {\n // Active tokens\n if (isTokenKey(key)) {\n const tokenId = tokenIdFromKey(key);\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n const token = txfToToken(tokenId, txfToken);\n result.tokens.push(token);\n }\n } catch (err) {\n result.validationErrors.push(`Token ${tokenId}: ${err}`);\n }\n }\n // Archived tokens\n else if (isArchivedKey(key)) {\n const tokenId = tokenIdFromArchivedKey(key);\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n result.archivedTokens.set(tokenId, txfToken);\n }\n } catch {\n result.validationErrors.push(`Archived token ${tokenId}: invalid structure`);\n }\n }\n // Forked tokens\n else if (isForkedKey(key)) {\n const parsed = parseForkedKey(key);\n if (parsed) {\n try {\n const txfToken = storageData[key] as TxfToken;\n if (txfToken?.genesis?.data?.tokenId) {\n const mapKey = `${parsed.tokenId}_${parsed.stateHash}`;\n result.forkedTokens.set(mapKey, txfToken);\n }\n } catch {\n result.validationErrors.push(`Forked token ${parsed.tokenId}: invalid structure`);\n }\n }\n }\n // Individual file format tokens (from IPFS storage's saveToken)\n else if (key.startsWith('token-')) {\n try {\n const entry = storageData[key] as { token?: TxfToken };\n const txfToken = entry?.token;\n if (txfToken?.genesis?.data?.tokenId) {\n const tokenId = txfToken.genesis.data.tokenId;\n const token = txfToToken(tokenId, txfToken);\n result.tokens.push(token);\n }\n } catch (err) {\n result.validationErrors.push(`Token ${key}: ${err}`);\n }\n }\n }\n\n return result;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Get token ID from Token object (prefers genesis.data.tokenId)\n */\nexport function getTokenId(token: Token): string {\n if (token.sdkData) {\n try {\n const txf = JSON.parse(token.sdkData);\n if (txf.genesis?.data?.tokenId) {\n return txf.genesis.data.tokenId;\n }\n } catch {\n // Fall through\n }\n }\n return token.id;\n}\n\n/**\n * Get the current state hash from a TXF token\n * Checks multiple sources in order of preference:\n * 1. Last transaction's newStateHash\n * 2. _integrity.currentStateHash\n * 3. Last transaction's inclusionProof authenticator stateHash\n * 4. Genesis inclusionProof authenticator stateHash (for never-transferred tokens)\n */\nexport function getCurrentStateHash(txf: TxfToken): string | undefined {\n // Check last transaction's explicit newStateHash\n if (txf.transactions && txf.transactions.length > 0) {\n const lastTx = txf.transactions[txf.transactions.length - 1];\n if (lastTx?.newStateHash) {\n return lastTx.newStateHash;\n }\n // Check authenticator stateHash from last transaction's proof\n if (lastTx?.inclusionProof?.authenticator?.stateHash) {\n return lastTx.inclusionProof.authenticator.stateHash;\n }\n }\n\n // Check integrity metadata\n if (txf._integrity?.currentStateHash) {\n return txf._integrity.currentStateHash;\n }\n\n // For tokens with no transactions, use genesis proof's stateHash\n if (txf.genesis?.inclusionProof?.authenticator?.stateHash) {\n return txf.genesis.inclusionProof.authenticator.stateHash;\n }\n\n return undefined;\n}\n\n/**\n * Check if token has valid TXF data\n */\nexport function hasValidTxfData(token: Token): boolean {\n if (!token.sdkData) return false;\n\n try {\n const txf = JSON.parse(token.sdkData);\n return !!(\n txf.genesis &&\n txf.genesis.data &&\n txf.genesis.data.tokenId &&\n txf.state &&\n txf.genesis.inclusionProof\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if token has uncommitted transactions\n */\nexport function hasUncommittedTransactions(token: Token): boolean {\n if (!token.sdkData) return false;\n\n try {\n const txf = JSON.parse(token.sdkData);\n if (!txf.transactions || txf.transactions.length === 0) return false;\n\n return txf.transactions.some(\n (tx: TxfTransaction) => tx.inclusionProof === null\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Check if a TXF token has missing newStateHash on any transaction\n */\nexport function hasMissingNewStateHash(txf: TxfToken): boolean {\n if (!txf.transactions || txf.transactions.length === 0) {\n return false;\n }\n return txf.transactions.some(tx => !tx.newStateHash);\n}\n\n/**\n * Count committed transactions in a token\n */\nexport function countCommittedTransactions(token: Token): number {\n if (!token.sdkData) return 0;\n\n try {\n const txf = JSON.parse(token.sdkData);\n if (!txf.transactions) return 0;\n\n return txf.transactions.filter(\n (tx: TxfTransaction) => tx.inclusionProof !== null\n ).length;\n } catch {\n return 0;\n }\n}\n","/**\n * InstantSplitExecutor\n *\n * Optimized token split executor that achieves ~2.3s critical path latency\n * instead of the standard ~42s sequential flow.\n *\n * Key Insight: TransferCommitment.create() only needs token.state, NOT the mint proof.\n * This allows creating transfer commitments immediately after mint data creation,\n * without waiting for mint proofs.\n *\n * V5 Flow (Production Mode):\n * 1. Create burn commitment, submit to aggregator (~50ms)\n * 2. Wait for burn inclusion proof (~2s - unavoidable)\n * 3. Create mint commitments with proper SplitMintReason (~50ms)\n * 4. Create transfer commitment from mint data (~100ms)\n * 5. Package bundle -> send via transport -> SUCCESS (~150ms)\n * TOTAL: ~2.3s\n *\n * Background (non-blocking):\n * 6. Submit mint commitments (parallel)\n * 7. Wait for mint proofs\n * 8. Reconstruct & save change token\n * 9. Sync to storage\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TokenCoinData } from '@unicitylabs/state-transition-sdk/lib/token/fungible/TokenCoinData';\nimport { TokenSplitBuilder } from '@unicitylabs/state-transition-sdk/lib/transaction/split/TokenSplitBuilder';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { IAddress } from '@unicitylabs/state-transition-sdk/lib/address/IAddress';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport type {\n InstantSplitBundleV5,\n InstantSplitResult,\n InstantSplitOptions,\n BackgroundProgressStatus,\n} from '../../types/instant-split';\nimport type { TransportProvider } from '../../transport';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InstantSplitExecutorConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n /** Dev mode skips trust base verification (for testing) */\n devMode?: boolean;\n}\n\nexport interface InstantSplitExecutorDeps {\n stClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n devMode?: boolean;\n}\n\nexport interface BackgroundContext {\n signingService: SigningService;\n tokenType: TokenType;\n coinId: CoinId;\n senderTokenId: TokenId;\n senderSalt: Uint8Array;\n onProgress?: (status: BackgroundProgressStatus) => void;\n onChangeTokenCreated?: (token: Token<any>) => Promise<void>;\n onStorageSync?: () => Promise<boolean>;\n}\n\n// =============================================================================\n// Hash Utilities\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// InstantSplitExecutor Implementation\n// =============================================================================\n\nexport class InstantSplitExecutor {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private signingService: SigningService;\n private devMode: boolean;\n\n constructor(config: InstantSplitExecutorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Execute an instant split transfer with V5 optimized flow.\n *\n * Critical path (~2.3s):\n * 1. Create and submit burn commitment\n * 2. Wait for burn proof\n * 3. Create mint commitments with SplitMintReason\n * 4. Create transfer commitment (no mint proof needed)\n * 5. Send bundle via transport\n *\n * @param tokenToSplit - The SDK token to split\n * @param splitAmount - Amount to send to recipient\n * @param remainderAmount - Amount to keep as change\n * @param coinIdHex - Coin ID in hex format\n * @param recipientAddress - Recipient's address (PROXY or DIRECT)\n * @param transport - Transport provider for sending the bundle\n * @param recipientPubkey - Recipient's transport public key\n * @param options - Optional configuration\n * @returns InstantSplitResult with success status and timing info\n */\n async executeSplitInstant(\n tokenToSplit: Token<any>,\n splitAmount: bigint,\n remainderAmount: bigint,\n coinIdHex: string,\n recipientAddress: IAddress,\n transport: TransportProvider,\n recipientPubkey: string,\n options?: InstantSplitOptions\n ): Promise<InstantSplitResult> {\n const startTime = performance.now();\n const splitGroupId = crypto.randomUUID();\n\n const tokenIdHex = toHex(tokenToSplit.id.bytes);\n console.log(`[InstantSplit] Starting V5 split for token ${tokenIdHex.slice(0, 8)}...`);\n\n try {\n const coinId = new CoinId(fromHex(coinIdHex));\n const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}_${Date.now()}`;\n\n // Generate IDs and salts (deterministic from seed)\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Create sender address (for minting to self first)\n const senderAddressRef = await UnmaskedPredicateReference.create(\n tokenToSplit.type,\n this.signingService.algorithm,\n this.signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const senderAddress = await senderAddressRef.toAddress();\n\n // Build split configuration\n const builder = new TokenSplitBuilder();\n\n // Recipient token (will be transferred)\n const coinDataA = TokenCoinData.create([[coinId, splitAmount]]);\n builder.createToken(\n recipientTokenId,\n tokenToSplit.type,\n new Uint8Array(0),\n coinDataA,\n senderAddress, // Mint to sender first, then transfer\n recipientSalt,\n null\n );\n\n // Sender token (change)\n const coinDataB = TokenCoinData.create([[coinId, remainderAmount]]);\n builder.createToken(\n senderTokenId,\n tokenToSplit.type,\n new Uint8Array(0),\n coinDataB,\n senderAddress,\n senderSalt,\n null\n );\n\n const split = await builder.build(tokenToSplit);\n\n // === STEP 1: CREATE AND SUBMIT BURN COMMITMENT ===\n console.log('[InstantSplit] Step 1: Creating and submitting burn...');\n const burnSalt = await sha256(seedString + '_burn_salt');\n const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn submission failed: ${burnResponse.status}`);\n }\n\n // === STEP 2: WAIT FOR BURN PROOF (~2s) ===\n console.log('[InstantSplit] Step 2: Waiting for burn proof...');\n const burnProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(burnCommitment, options?.burnProofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, burnCommitment);\n const burnTransaction = burnCommitment.toTransaction(burnProof);\n\n const burnDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Burn proof received in ${burnDuration.toFixed(0)}ms`);\n\n options?.onBurnCompleted?.(JSON.stringify(burnTransaction.toJSON()));\n\n // === STEP 3: CREATE MINT COMMITMENTS WITH SPLITMINT REASON ===\n console.log('[InstantSplit] Step 3: Creating mint commitments...');\n const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);\n\n // Find recipient and sender mint commitments\n const recipientIdHex = toHex(recipientTokenId.bytes);\n const senderIdHex = toHex(senderTokenId.bytes);\n\n const recipientMintCommitment = mintCommitments.find(\n (c) => toHex(c.transactionData.tokenId.bytes) === recipientIdHex\n );\n const senderMintCommitment = mintCommitments.find(\n (c) => toHex(c.transactionData.tokenId.bytes) === senderIdHex\n );\n\n if (!recipientMintCommitment || !senderMintCommitment) {\n throw new Error('Failed to find expected mint commitments');\n }\n\n // === STEP 4: CREATE TRANSFER COMMITMENT FROM MINT DATA ===\n console.log('[InstantSplit] Step 4: Creating transfer commitment...');\n const transferSalt = await sha256(seedString + '_transfer_salt');\n\n const transferCommitment = await this.createTransferCommitmentFromMintData(\n recipientMintCommitment.transactionData,\n recipientAddress,\n transferSalt,\n this.signingService\n );\n\n // Create minted token state for recipient to reconstruct\n const mintedPredicate = await UnmaskedPredicate.create(\n recipientTokenId,\n tokenToSplit.type,\n this.signingService,\n HashAlgorithm.SHA256,\n recipientSalt\n );\n const mintedState = new TokenState(mintedPredicate, null);\n\n // === STEP 5: PACKAGE V5 BUNDLE ===\n console.log('[InstantSplit] Step 5: Packaging V5 bundle...');\n const senderPubkey = toHex(this.signingService.publicKey);\n\n // Get nametag token if this is a PROXY address transfer\n let nametagTokenJson: string | undefined;\n const recipientAddressStr = recipientAddress.toString();\n if (recipientAddressStr.startsWith('PROXY://') && tokenToSplit.nametagTokens?.length > 0) {\n // Include sender's nametag token for PROXY verification\n nametagTokenJson = JSON.stringify(tokenToSplit.nametagTokens[0].toJSON());\n }\n\n const bundle: InstantSplitBundleV5 = {\n version: '5.0',\n type: 'INSTANT_SPLIT',\n burnTransaction: JSON.stringify(burnTransaction.toJSON()),\n recipientMintData: JSON.stringify(recipientMintCommitment.transactionData.toJSON()),\n transferCommitment: JSON.stringify(transferCommitment.toJSON()),\n amount: splitAmount.toString(),\n coinId: coinIdHex,\n tokenTypeHex: toHex(tokenToSplit.type.bytes),\n splitGroupId,\n senderPubkey,\n recipientSaltHex: toHex(recipientSalt),\n transferSaltHex: toHex(transferSalt),\n mintedTokenStateJson: JSON.stringify(mintedState.toJSON()),\n finalRecipientStateJson: '', // Recipient creates their own\n recipientAddressJson: recipientAddressStr,\n nametagTokenJson,\n };\n\n // === STEP 6: SEND VIA TRANSPORT ===\n console.log('[InstantSplit] Step 6: Sending via transport...');\n const nostrEventId = await transport.sendTokenTransfer(recipientPubkey, {\n token: JSON.stringify(bundle),\n proof: null, // Proof is included in the bundle\n memo: 'INSTANT_SPLIT_V5',\n sender: {\n transportPubkey: senderPubkey,\n },\n });\n\n const criticalPathDuration = performance.now() - startTime;\n console.log(`[InstantSplit] V5 complete in ${criticalPathDuration.toFixed(0)}ms`);\n\n options?.onNostrDelivered?.(nostrEventId);\n\n // === STEP 7: BACKGROUND PROCESSING ===\n let backgroundPromise: Promise<void> | undefined;\n if (!options?.skipBackground) {\n backgroundPromise = this.submitBackgroundV5(senderMintCommitment, recipientMintCommitment, transferCommitment, {\n signingService: this.signingService,\n tokenType: tokenToSplit.type,\n coinId,\n senderTokenId,\n senderSalt,\n onProgress: options?.onBackgroundProgress,\n onChangeTokenCreated: options?.onChangeTokenCreated,\n onStorageSync: options?.onStorageSync,\n });\n }\n\n return {\n success: true,\n nostrEventId,\n splitGroupId,\n criticalPathDurationMs: criticalPathDuration,\n backgroundStarted: !options?.skipBackground,\n backgroundPromise,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplit] Failed after ${duration.toFixed(0)}ms:`, error);\n\n return {\n success: false,\n splitGroupId,\n criticalPathDurationMs: duration,\n error: errorMessage,\n backgroundStarted: false,\n };\n }\n }\n\n /**\n * Create a TransferCommitment from MintTransactionData WITHOUT waiting for mint proof.\n *\n * Key insight: TransferCommitment.create() only needs token.state and token.nametagTokens.\n * It does NOT need the genesis transaction or mint proof.\n */\n private async createTransferCommitmentFromMintData(\n mintData: MintTransactionData<any>,\n recipientAddress: IAddress,\n transferSalt: Uint8Array,\n signingService: SigningService,\n nametagTokens?: Token<any>[]\n ): Promise<TransferCommitment> {\n // Recreate the predicate from mint data\n const predicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n mintData.tokenType,\n signingService,\n HashAlgorithm.SHA256,\n mintData.salt\n );\n\n // Create token state (what TransferCommitment.create actually uses)\n const state = new TokenState(predicate, null);\n\n // Create a minimal token-like object\n // TransferCommitment.create() only accesses token.state and token.nametagTokens\n const minimalToken = {\n state,\n nametagTokens: nametagTokens || [],\n id: mintData.tokenId,\n type: mintData.tokenType,\n };\n\n // Create the transfer commitment\n const transferCommitment = await TransferCommitment.create(\n minimalToken as any,\n recipientAddress,\n transferSalt,\n null, // recipientData\n null, // recipientDataHash\n signingService\n );\n\n return transferCommitment;\n }\n\n /**\n * V5 background submission.\n *\n * Submits mint commitments to aggregator in PARALLEL after transport delivery.\n * Then waits for sender's mint proof, reconstructs change token, and saves it.\n */\n private submitBackgroundV5(\n senderMintCommitment: MintCommitment<any>,\n recipientMintCommitment: MintCommitment<any>,\n transferCommitment: TransferCommitment,\n context: BackgroundContext\n ): Promise<void> {\n console.log('[InstantSplit] Background: Starting parallel mint submission...');\n const startTime = performance.now();\n\n // Submit all commitments in parallel\n const submissions = Promise.all([\n this.client\n .submitMintCommitment(senderMintCommitment)\n .then((res) => ({ type: 'senderMint', status: res.status }))\n .catch((err) => ({ type: 'senderMint', status: 'ERROR', error: err })),\n\n this.client\n .submitMintCommitment(recipientMintCommitment)\n .then((res) => ({ type: 'recipientMint', status: res.status }))\n .catch((err) => ({ type: 'recipientMint', status: 'ERROR', error: err })),\n\n this.client\n .submitTransferCommitment(transferCommitment)\n .then((res) => ({ type: 'transfer', status: res.status }))\n .catch((err) => ({ type: 'transfer', status: 'ERROR', error: err })),\n ]);\n\n return submissions\n .then(async (results) => {\n const submitDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Background: Submissions complete in ${submitDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'MINTS_SUBMITTED',\n message: `All commitments submitted in ${submitDuration.toFixed(0)}ms`,\n });\n\n // Check for critical failures\n const senderMintResult = results.find((r) => r.type === 'senderMint');\n if (\n senderMintResult?.status !== 'SUCCESS' &&\n senderMintResult?.status !== 'REQUEST_ID_EXISTS'\n ) {\n console.error('[InstantSplit] Background: Sender mint failed - cannot save change token');\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Sender mint submission failed',\n error: String((senderMintResult as any)?.error),\n });\n return;\n }\n\n // Wait for sender's mint proof to save change token\n console.log('[InstantSplit] Background: Waiting for sender mint proof...');\n const proofStartTime = performance.now();\n\n try {\n const senderMintProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(senderMintCommitment)\n : await waitInclusionProof(this.trustBase, this.client, senderMintCommitment);\n\n const proofDuration = performance.now() - proofStartTime;\n console.log(`[InstantSplit] Background: Sender mint proof received in ${proofDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'MINTS_PROVEN',\n message: `Mint proof received in ${proofDuration.toFixed(0)}ms`,\n });\n\n // Reconstruct change token\n const mintTransaction = senderMintCommitment.toTransaction(senderMintProof);\n const predicate = await UnmaskedPredicate.create(\n context.senderTokenId,\n context.tokenType,\n context.signingService,\n HashAlgorithm.SHA256,\n context.senderSalt\n );\n const state = new TokenState(predicate, null);\n const changeToken = await Token.mint(this.trustBase, state, mintTransaction);\n\n // Verify if not in dev mode\n if (!this.devMode) {\n const verification = await changeToken.verify(this.trustBase);\n if (!verification.isSuccessful) {\n throw new Error(`Change token verification failed`);\n }\n }\n\n console.log('[InstantSplit] Background: Change token created');\n\n context.onProgress?.({\n stage: 'CHANGE_TOKEN_SAVED',\n message: 'Change token created and verified',\n });\n\n // Save change token via callback\n if (context.onChangeTokenCreated) {\n await context.onChangeTokenCreated(changeToken);\n console.log('[InstantSplit] Background: Change token saved');\n }\n\n // Trigger storage sync if provided\n if (context.onStorageSync) {\n try {\n const syncSuccess = await context.onStorageSync();\n console.log(`[InstantSplit] Background: Storage sync ${syncSuccess ? 'completed' : 'deferred'}`);\n context.onProgress?.({\n stage: 'STORAGE_SYNCED',\n message: syncSuccess ? 'Storage synchronized' : 'Sync deferred',\n });\n } catch (syncError) {\n console.warn('[InstantSplit] Background: Storage sync error:', syncError);\n }\n }\n\n const totalDuration = performance.now() - startTime;\n console.log(`[InstantSplit] Background: Complete in ${totalDuration.toFixed(0)}ms`);\n\n context.onProgress?.({\n stage: 'COMPLETED',\n message: `Background processing complete in ${totalDuration.toFixed(0)}ms`,\n });\n } catch (proofError) {\n console.error('[InstantSplit] Background: Failed to get sender mint proof:', proofError);\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Failed to get mint proof',\n error: String(proofError),\n });\n }\n })\n .catch((err) => {\n console.error('[InstantSplit] Background: Submission batch failed:', err);\n context.onProgress?.({\n stage: 'FAILED',\n message: 'Background submission failed',\n error: String(err),\n });\n });\n }\n\n /**\n * Dev mode bypass for waitInclusionProof.\n * In dev mode, we create a mock proof for testing.\n */\n private async waitInclusionProofWithDevBypass(\n commitment: TransferCommitment | MintCommitment<any>,\n timeoutMs = 60000\n ): Promise<any> {\n if (this.devMode) {\n // In dev mode, try to get real proof but with shorter timeout\n try {\n return await Promise.race([\n waitInclusionProof(this.trustBase, this.client, commitment as any),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Dev mode timeout')), Math.min(timeoutMs, 5000))\n ),\n ]);\n } catch {\n // Return a mock proof in dev mode\n console.log('[InstantSplit] Dev mode: Using mock proof');\n return {\n toJSON: () => ({ mock: true }),\n };\n }\n }\n return waitInclusionProof(this.trustBase, this.client, commitment as any);\n }\n}\n\n/**\n * Factory function for creating InstantSplitExecutor\n */\nexport function createInstantSplitExecutor(config: InstantSplitExecutorConfig): InstantSplitExecutor {\n return new InstantSplitExecutor(config);\n}\n","/**\n * InstantSplitProcessor\n *\n * Processes received INSTANT_SPLIT bundles on the recipient side.\n *\n * V5 Flow (Production Mode):\n * 1. Validate burn transaction (already has proof)\n * 2. Recreate and submit mint commitment -> wait for proof\n * 3. Reconstruct minted token using sender's state from bundle\n * 4. Submit transfer commitment -> wait for proof\n * 5. Create recipient's final state\n * 6. Finalize token with transfer transaction\n * 7. Verify and return finalized token\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport {\n type InstantSplitBundle,\n type InstantSplitBundleV5,\n type InstantSplitBundleV4,\n type InstantSplitProcessResult,\n isInstantSplitBundleV5,\n isInstantSplitBundleV4,\n} from '../../types/instant-split';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface InstantSplitProcessorConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n /** Dev mode skips trust base verification (for testing) */\n devMode?: boolean;\n}\n\nexport interface ProcessBundleOptions {\n /** Timeout for proof waiting in ms (default: 60000) */\n proofTimeoutMs?: number;\n /** Callback to find nametag token for PROXY address */\n findNametagToken?: (proxyAddress: string) => Promise<Token<any> | null>;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n// =============================================================================\n// InstantSplitProcessor Implementation\n// =============================================================================\n\nexport class InstantSplitProcessor {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private devMode: boolean;\n\n constructor(config: InstantSplitProcessorConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Process a received INSTANT_SPLIT bundle.\n *\n * @param bundle - The received bundle (V4 or V5)\n * @param signingService - Recipient's signing service\n * @param senderPubkey - Sender's public key (for verification)\n * @param options - Processing options\n * @returns Processing result with finalized token if successful\n */\n async processReceivedBundle(\n bundle: InstantSplitBundle,\n signingService: SigningService,\n senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n if (isInstantSplitBundleV5(bundle)) {\n return this.processV5Bundle(bundle, signingService, senderPubkey, options);\n } else if (isInstantSplitBundleV4(bundle)) {\n return this.processV4Bundle(bundle, signingService, senderPubkey, options);\n }\n\n return {\n success: false,\n error: `Unknown bundle version: ${(bundle as any).version}`,\n durationMs: 0,\n };\n }\n\n /**\n * Process a V5 bundle (production mode).\n *\n * V5 Flow:\n * 1. Burn transaction already has proof (just validate)\n * 2. Submit mint commitment -> wait for proof\n * 3. Reconstruct minted token (use sender's state from bundle)\n * 4. Submit transfer commitment -> wait for proof\n * 5. Create recipient's final state and finalize token\n */\n private async processV5Bundle(\n bundle: InstantSplitBundleV5,\n signingService: SigningService,\n senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n console.log('[InstantSplitProcessor] Processing V5 bundle...');\n const startTime = performance.now();\n\n try {\n // Validate sender pubkey matches bundle\n if (bundle.senderPubkey !== senderPubkey) {\n console.warn('[InstantSplitProcessor] Sender pubkey mismatch (non-fatal)');\n }\n\n // === Step 1: Validate burn transaction ===\n const burnTxJson = JSON.parse(bundle.burnTransaction);\n const burnTransaction = await TransferTransaction.fromJSON(burnTxJson);\n console.log('[InstantSplitProcessor] Burn transaction validated');\n\n // === Step 2: Deserialize and submit MintCommitment ===\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n\n const mintCommitment = await MintCommitment.create(mintData);\n console.log('[InstantSplitProcessor] Mint commitment recreated');\n\n const mintResponse = await this.client.submitMintCommitment(mintCommitment);\n if (mintResponse.status !== 'SUCCESS' && mintResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint submission failed: ${mintResponse.status}`);\n }\n console.log(`[InstantSplitProcessor] Mint submitted: ${mintResponse.status}`);\n\n // === Step 3: Wait for mint inclusion proof ===\n const mintProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(mintCommitment, options?.proofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, mintCommitment);\n const mintTransaction = mintCommitment.toTransaction(mintProof);\n console.log('[InstantSplitProcessor] Mint proof received');\n\n // === Step 4: Reconstruct minted token using sender's state ===\n const tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);\n\n const tokenJson = {\n version: '2.0',\n state: senderMintedStateJson,\n genesis: mintTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n const mintedToken = await Token.fromJSON(tokenJson);\n console.log('[InstantSplitProcessor] Minted token reconstructed from sender state');\n\n // === Step 5: Submit transfer commitment ===\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n\n const transferResponse = await this.client.submitTransferCommitment(transferCommitment);\n if (transferResponse.status !== 'SUCCESS' && transferResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer submission failed: ${transferResponse.status}`);\n }\n console.log(`[InstantSplitProcessor] Transfer submitted: ${transferResponse.status}`);\n\n // === Step 6: Wait for transfer inclusion proof ===\n const transferProof = this.devMode\n ? await this.waitInclusionProofWithDevBypass(transferCommitment, options?.proofTimeoutMs)\n : await waitInclusionProof(this.trustBase, this.client, transferCommitment);\n const transferTransaction = transferCommitment.toTransaction(transferProof);\n console.log('[InstantSplitProcessor] Transfer proof received');\n\n // === Step 7: Create recipient's final state ===\n const transferSalt = fromHex(bundle.transferSaltHex);\n const finalRecipientPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const finalRecipientState = new TokenState(finalRecipientPredicate, null);\n console.log('[InstantSplitProcessor] Final recipient state created');\n\n // === Step 8: Find nametag token for PROXY addresses ===\n let nametagTokens: Token<any>[] = [];\n const recipientAddressStr = bundle.recipientAddressJson;\n\n if (recipientAddressStr.startsWith('PROXY://')) {\n console.log('[InstantSplitProcessor] PROXY address detected, finding nametag token...');\n\n // Try to get nametag token from bundle first\n if (bundle.nametagTokenJson) {\n try {\n const nametagToken = await Token.fromJSON(JSON.parse(bundle.nametagTokenJson));\n // Validate PROXY address matches nametag token\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address !== recipientAddressStr) {\n console.warn('[InstantSplitProcessor] Nametag PROXY address mismatch, ignoring bundle token');\n // Fall through to callback path\n } else {\n nametagTokens = [nametagToken];\n console.log('[InstantSplitProcessor] Using nametag token from bundle (address validated)');\n }\n } catch (err) {\n console.warn('[InstantSplitProcessor] Failed to parse nametag token from bundle:', err);\n }\n }\n\n // If not in bundle, use callback to find it\n if (nametagTokens.length === 0 && options?.findNametagToken) {\n const token = await options.findNametagToken(recipientAddressStr);\n if (token) {\n nametagTokens = [token];\n console.log('[InstantSplitProcessor] Found nametag token via callback');\n }\n }\n\n // CRITICAL: For PROXY addresses, we MUST have a nametag token\n if (nametagTokens.length === 0 && !this.devMode) {\n throw new Error(\n `PROXY address transfer requires nametag token for verification. ` +\n `Address: ${recipientAddressStr}`\n );\n }\n }\n\n // === Step 9: Finalize token ===\n let finalToken: Token<any>;\n\n if (this.devMode) {\n // Dev mode: create token without verification\n console.log('[InstantSplitProcessor] Dev mode: finalizing without verification');\n const tokenJson = mintedToken.toJSON() as any;\n tokenJson.state = finalRecipientState.toJSON();\n tokenJson.transactions = [transferTransaction.toJSON()];\n finalToken = await Token.fromJSON(tokenJson);\n } else {\n // Production: use client.finalizeTransaction\n finalToken = await this.client.finalizeTransaction(\n this.trustBase,\n mintedToken,\n finalRecipientState,\n transferTransaction,\n nametagTokens\n );\n }\n console.log('[InstantSplitProcessor] Token finalized');\n\n // === Step 10: Verify the final token ===\n if (!this.devMode) {\n const verification = await finalToken.verify(this.trustBase);\n if (!verification.isSuccessful) {\n throw new Error(`Token verification failed`);\n }\n console.log('[InstantSplitProcessor] Token verified');\n }\n\n const duration = performance.now() - startTime;\n console.log(`[InstantSplitProcessor] V5 bundle processed in ${duration.toFixed(0)}ms`);\n\n return {\n success: true,\n token: finalToken,\n durationMs: duration,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplitProcessor] V5 processing failed:`, error);\n\n return {\n success: false,\n error: errorMessage,\n durationMs: duration,\n };\n }\n }\n\n /**\n * Process a V4 bundle (dev mode only).\n *\n * V4 Flow:\n * 1. Submit burn commitment -> wait for proof\n * 2. Submit mint commitment -> wait for proof\n * 3. Reconstruct minted token\n * 4. Submit transfer commitment -> wait for proof\n * 5. Finalize token\n */\n private async processV4Bundle(\n bundle: InstantSplitBundleV4,\n signingService: SigningService,\n _senderPubkey: string,\n options?: ProcessBundleOptions\n ): Promise<InstantSplitProcessResult> {\n if (!this.devMode) {\n return {\n success: false,\n error: 'INSTANT_SPLIT V4 is only supported in dev mode',\n durationMs: 0,\n };\n }\n\n console.log('[InstantSplitProcessor] Processing V4 bundle (dev mode)...');\n const startTime = performance.now();\n\n try {\n // === Step 1: Submit burn commitment and wait for proof ===\n const burnCommitmentJson = JSON.parse(bundle.burnCommitment);\n const burnCommitment = await TransferCommitment.fromJSON(burnCommitmentJson);\n\n const burnResponse = await this.client.submitTransferCommitment(burnCommitment);\n if (burnResponse.status !== 'SUCCESS' && burnResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Burn submission failed: ${burnResponse.status}`);\n }\n\n await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);\n console.log('[InstantSplitProcessor] V4: Burn proof received');\n\n // === Step 2: Submit mint commitment and wait for proof ===\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n\n const mintCommitment = await MintCommitment.create(mintData);\n\n const mintResponse = await this.client.submitMintCommitment(mintCommitment);\n if (mintResponse.status !== 'SUCCESS' && mintResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint submission failed: ${mintResponse.status}`);\n }\n\n const mintProof = await this.waitInclusionProofWithDevBypass(\n mintCommitment,\n options?.proofTimeoutMs\n );\n const mintTransaction = mintCommitment.toTransaction(mintProof);\n console.log('[InstantSplitProcessor] V4: Mint proof received');\n\n // === Step 3: Reconstruct minted token ===\n const tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n const recipientSalt = fromHex(bundle.recipientSaltHex);\n\n const recipientPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n recipientSalt\n );\n const recipientState = new TokenState(recipientPredicate, null);\n\n const tokenJson = {\n version: '2.0',\n state: recipientState.toJSON(),\n genesis: mintTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n const mintedToken = await Token.fromJSON(tokenJson);\n console.log('[InstantSplitProcessor] V4: Minted token reconstructed');\n\n // === Step 4: Submit transfer commitment and wait for proof ===\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n\n const transferResponse = await this.client.submitTransferCommitment(transferCommitment);\n if (transferResponse.status !== 'SUCCESS' && transferResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer submission failed: ${transferResponse.status}`);\n }\n\n const transferProof = await this.waitInclusionProofWithDevBypass(\n transferCommitment,\n options?.proofTimeoutMs\n );\n const transferTransaction = transferCommitment.toTransaction(transferProof);\n console.log('[InstantSplitProcessor] V4: Transfer proof received');\n\n // === Step 5: Finalize token (dev mode - no verification) ===\n const transferSalt = fromHex(bundle.transferSaltHex);\n const finalPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const finalState = new TokenState(finalPredicate, null);\n\n const finalTokenJson = mintedToken.toJSON() as any;\n finalTokenJson.state = finalState.toJSON();\n finalTokenJson.transactions = [transferTransaction.toJSON()];\n const finalToken = await Token.fromJSON(finalTokenJson);\n console.log('[InstantSplitProcessor] V4: Token finalized');\n\n const duration = performance.now() - startTime;\n console.log(`[InstantSplitProcessor] V4 bundle processed in ${duration.toFixed(0)}ms`);\n\n return {\n success: true,\n token: finalToken,\n durationMs: duration,\n };\n } catch (error) {\n const duration = performance.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(`[InstantSplitProcessor] V4 processing failed:`, error);\n\n return {\n success: false,\n error: errorMessage,\n durationMs: duration,\n };\n }\n }\n\n /**\n * Dev mode bypass for waitInclusionProof.\n */\n private async waitInclusionProofWithDevBypass(\n commitment: TransferCommitment | MintCommitment<any>,\n timeoutMs = 60000\n ): Promise<any> {\n if (this.devMode) {\n try {\n return await Promise.race([\n waitInclusionProof(this.trustBase, this.client, commitment as any),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error('Dev mode timeout')), Math.min(timeoutMs, 5000))\n ),\n ]);\n } catch {\n console.log('[InstantSplitProcessor] Dev mode: Using mock proof');\n return {\n toJSON: () => ({ mock: true }),\n };\n }\n }\n return waitInclusionProof(this.trustBase, this.client, commitment as any);\n }\n}\n\n/**\n * Factory function for creating InstantSplitProcessor\n */\nexport function createInstantSplitProcessor(\n config: InstantSplitProcessorConfig\n): InstantSplitProcessor {\n return new InstantSplitProcessor(config);\n}\n","/**\n * INSTANT_SPLIT V5 Types\n *\n * Optimized token split transfer types that achieve ~2.3s critical path latency\n * instead of the standard ~42s sequential flow.\n *\n * Key Insight: TransferCommitment.create() only needs token.state, NOT the mint proof.\n * This allows creating transfer commitments immediately after mint data creation,\n * without waiting for mint proofs.\n *\n * V5 Flow (Production Mode):\n * 1. Create burn commitment, submit to aggregator\n * 2. Wait for burn inclusion proof (~2s - unavoidable)\n * 3. Create mint commitments with proper SplitMintReason (requires burn proof)\n * 4. Create transfer commitment from mint data (no mint proof needed)\n * 5. Package bundle -> send via Nostr -> SUCCESS (~2.3s total!)\n * 6. Background: submit mints, wait for proofs, save change token, sync storage\n */\n\n// Note: Token type is generic - we use 'any' to avoid import complexity\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype SdkToken = any;\n\n// =============================================================================\n// INSTANT_SPLIT V5 Bundle Types\n// =============================================================================\n\n/**\n * Bundle payload for INSTANT_SPLIT V5 (Production Mode)\n *\n * V5 achieves ~2.3s sender latency while working with production aggregators:\n * - Burn proof is required for creating SplitMintReason\n * - Transfer commitment is created from mint data WITHOUT waiting for mint proof\n * - Mints are submitted in background after Nostr delivery\n *\n * Security: Burn is proven on-chain before mints can be created, preventing double-spend.\n */\nexport interface InstantSplitBundleV5 {\n /** Bundle version - V5 is production mode (proper SplitMintReason) */\n version: '5.0';\n\n /** Bundle type identifier */\n type: 'INSTANT_SPLIT';\n\n /**\n * Burn TRANSACTION JSON (WITH inclusion proof!)\n * V5 sends the proven burn transaction so recipient can verify burn completed.\n */\n burnTransaction: string;\n\n /**\n * Recipient's MintTransactionData JSON (contains proper SplitMintReason in V5)\n * The SplitMintReason references the burn transaction.\n */\n recipientMintData: string;\n\n /**\n * Pre-created TransferCommitment JSON (recipient submits and waits for proof)\n * Created from mint data WITHOUT any proofs.\n */\n transferCommitment: string;\n\n /** Payment amount (display metadata) */\n amount: string;\n\n /** Coin ID hex */\n coinId: string;\n\n /** Token type hex */\n tokenTypeHex: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId: string;\n\n /** Sender's pubkey for acknowledgment */\n senderPubkey: string;\n\n /** Salt for recipient predicate creation (hex) */\n recipientSaltHex: string;\n\n /** Salt for transfer commitment creation (hex) */\n transferSaltHex: string;\n\n /**\n * Serialized TokenState JSON for the intermediate minted token.\n *\n * In V5, the mint is to sender's address first, then transferred to recipient.\n * The recipient needs this state to reconstruct the minted token before applying transfer.\n * Without this, the recipient can't create a matching predicate (they don't have sender's signing key).\n */\n mintedTokenStateJson: string;\n\n /**\n * Serialized TokenState JSON for the final recipient state (after transfer).\n *\n * The sender creates the transfer commitment targeting the recipient's PROXY address.\n * The recipient can't recreate this state correctly because their signingService\n * creates predicates for their DIRECT address, not the PROXY address.\n * This is optional - recipient can create their own if they have the correct address.\n */\n finalRecipientStateJson: string;\n\n /**\n * Serialized recipient address JSON (PROXY or DIRECT).\n *\n * Used by the recipient to identify which nametag token is being targeted.\n * For PROXY address transfers, the recipient needs to find the matching\n * nametag token and pass it to finalizeTransaction() for verification.\n */\n recipientAddressJson: string;\n\n /**\n * Serialized nametag token JSON (for PROXY address transfers).\n *\n * For PROXY address transfers, the sender includes the nametag token\n * so the recipient can verify they're authorized to receive at this address.\n * This is REQUIRED for PROXY addresses - transfers without this will fail.\n */\n nametagTokenJson?: string;\n}\n\n// =============================================================================\n// INSTANT_SPLIT V4 Bundle Types (Dev Mode Only)\n// =============================================================================\n\n/**\n * Bundle payload for INSTANT_SPLIT V4 (Dev Mode Only - True Nostr-First Split)\n *\n * V4 achieves near-zero sender latency (~0.3s) by:\n * 1. Creating ALL commitments locally BEFORE any aggregator submission\n * 2. Persisting via Nostr FIRST\n * 3. Then submitting ALL to aggregator in background\n *\n * NOTE: V4 only works in dev mode. Production requires V5 with proper SplitMintReason.\n */\nexport interface InstantSplitBundleV4 {\n /** Bundle version - V4 is true Nostr-first (dev mode only) */\n version: '4.0';\n\n /** Bundle type identifier */\n type: 'INSTANT_SPLIT';\n\n /**\n * Burn commitment JSON (NOT transaction - no proof yet!)\n * Both sender and recipient submit this to aggregator.\n */\n burnCommitment: string;\n\n /** Recipient's MintTransactionData JSON (they recreate commitment and submit) */\n recipientMintData: string;\n\n /**\n * Pre-created TransferCommitment JSON (recipient submits and waits for proof)\n * Created from mint data WITHOUT any proofs.\n */\n transferCommitment: string;\n\n /** Payment amount (display metadata) */\n amount: string;\n\n /** Coin ID hex */\n coinId: string;\n\n /** Token type hex */\n tokenTypeHex: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId: string;\n\n /** Sender's pubkey for acknowledgment */\n senderPubkey: string;\n\n /** Salt for recipient predicate creation (hex) */\n recipientSaltHex: string;\n\n /** Salt for transfer commitment creation (hex) */\n transferSaltHex: string;\n}\n\n/** Union type for all InstantSplit bundle versions */\nexport type InstantSplitBundle = InstantSplitBundleV4 | InstantSplitBundleV5;\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard to check if an object is an InstantSplitBundle (V4 or V5)\n */\nexport function isInstantSplitBundle(obj: unknown): obj is InstantSplitBundle {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n const bundle = obj as Record<string, unknown>;\n\n // Check common fields\n if (bundle.type !== 'INSTANT_SPLIT') return false;\n if (typeof bundle.recipientMintData !== 'string') return false;\n if (typeof bundle.transferCommitment !== 'string') return false;\n if (typeof bundle.amount !== 'string') return false;\n if (typeof bundle.coinId !== 'string') return false;\n if (typeof bundle.splitGroupId !== 'string') return false;\n if (typeof bundle.senderPubkey !== 'string') return false;\n if (typeof bundle.recipientSaltHex !== 'string') return false;\n if (typeof bundle.transferSaltHex !== 'string') return false;\n\n // Version-specific checks\n if (bundle.version === '4.0') {\n // V4 has burnCommitment (no proof)\n return typeof bundle.burnCommitment === 'string';\n } else if (bundle.version === '5.0') {\n // V5 has burnTransaction (with proof), mintedTokenStateJson, finalRecipientStateJson, and recipientAddressJson\n return (\n typeof bundle.burnTransaction === 'string' &&\n typeof bundle.mintedTokenStateJson === 'string' &&\n typeof bundle.finalRecipientStateJson === 'string' &&\n typeof bundle.recipientAddressJson === 'string'\n );\n }\n\n return false;\n}\n\n/**\n * Type guard to check if bundle is V4 (dev mode)\n */\nexport function isInstantSplitBundleV4(obj: unknown): obj is InstantSplitBundleV4 {\n return isInstantSplitBundle(obj) && obj.version === '4.0';\n}\n\n/**\n * Type guard to check if bundle is V5 (production mode)\n */\nexport function isInstantSplitBundleV5(obj: unknown): obj is InstantSplitBundleV5 {\n return isInstantSplitBundle(obj) && obj.version === '5.0';\n}\n\n// =============================================================================\n// Result Types\n// =============================================================================\n\n/**\n * Result of an instant split send operation\n */\nexport interface InstantSplitResult {\n /** Whether the operation succeeded (Nostr delivery) */\n success: boolean;\n\n /** Nostr event ID (if delivered) */\n nostrEventId?: string;\n\n /** Split group ID for recovery correlation */\n splitGroupId?: string;\n\n /** Time taken for critical path (Nostr delivery) in ms */\n criticalPathDurationMs: number;\n\n /** Error message (if failed) */\n error?: string;\n\n /** Whether background processing was started */\n backgroundStarted?: boolean;\n\n /** Promise that resolves when background processing completes (change token saved) */\n backgroundPromise?: Promise<void>;\n}\n\n/**\n * Result from processing an INSTANT_SPLIT bundle (recipient side)\n */\nexport interface InstantSplitProcessResult {\n /** Whether processing succeeded */\n success: boolean;\n\n /** The finalized SDK token (if successful) */\n token?: SdkToken;\n\n /** Error message (if failed) */\n error?: string;\n\n /** Processing duration in ms */\n durationMs: number;\n}\n\n// =============================================================================\n// Options Types\n// =============================================================================\n\n/**\n * Options for instant split send operation\n */\nexport interface InstantSplitOptions {\n /** Timeout for Nostr delivery in ms (default: 30000) */\n nostrTimeoutMs?: number;\n\n /** Timeout for burn proof wait in ms (default: 60000) */\n burnProofTimeoutMs?: number;\n\n /** Timeout for mint proof wait in ms (default: 60000) */\n mintProofTimeoutMs?: number;\n\n /** Skip background processing (for testing) */\n skipBackground?: boolean;\n\n /** Use dev mode (V4 flow without SplitMintReason validation) */\n devMode?: boolean;\n\n /** Callback when burn is completed */\n onBurnCompleted?: (burnTxJson: string) => void;\n\n /** Callback when Nostr delivery is completed */\n onNostrDelivered?: (eventId: string) => void;\n\n /** Callback for background progress updates */\n onBackgroundProgress?: (status: BackgroundProgressStatus) => void;\n\n /** Callback when change token is created (background) */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChangeTokenCreated?: (token: any) => Promise<void>;\n\n /** Callback to trigger storage sync (background) */\n onStorageSync?: () => Promise<boolean>;\n}\n\n/**\n * Background processing status\n */\nexport interface BackgroundProgressStatus {\n stage:\n | 'MINTS_SUBMITTED'\n | 'MINTS_PROVEN'\n | 'CHANGE_TOKEN_SAVED'\n | 'STORAGE_SYNCED'\n | 'COMPLETED'\n | 'FAILED';\n message: string;\n error?: string;\n}\n\n// =============================================================================\n// Recovery Types\n// =============================================================================\n\n/**\n * Metadata for V5 recovery (stored with outbox entry)\n */\nexport interface InstantSplitV5RecoveryMetadata {\n version: '5.0';\n seedString: string;\n senderTokenIdHex: string;\n senderSaltHex: string;\n changeAmount: string;\n burnRequestIdHex: string;\n}\n\n/**\n * Result of recovering an orphaned split\n */\nexport interface SplitRecoveryResult {\n /** Number of splits successfully recovered */\n splitsRecovered: number;\n\n /** Number of change tokens recovered */\n changeTokensRecovered: number;\n\n /** Errors encountered during recovery */\n errors: Array<{\n splitGroupId: string;\n error: string;\n timestamp: number;\n }>;\n\n /** Total duration of recovery in ms */\n durationMs: number;\n}\n\n// =============================================================================\n// Pending Finalization Types (Lazy Proof Resolution)\n// =============================================================================\n\n/** Finalization stage for V5 bundles saved as unconfirmed */\nexport type V5FinalizationStage =\n | 'RECEIVED'\n | 'MINT_SUBMITTED'\n | 'MINT_PROVEN'\n | 'TRANSFER_SUBMITTED'\n | 'FINALIZED';\n\n/** Pending finalization metadata stored in token.sdkData */\nexport interface PendingV5Finalization {\n type: 'v5_bundle';\n stage: V5FinalizationStage;\n bundleJson: string;\n senderPubkey: string;\n savedAt: number;\n lastAttemptAt?: number;\n attemptCount: number;\n mintProofJson?: string;\n}\n\n/** Result of resolveUnconfirmed() */\nexport interface UnconfirmedResolutionResult {\n resolved: number;\n stillPending: number;\n failed: number;\n details: Array<{\n tokenId: string;\n stage: string;\n status: 'resolved' | 'pending' | 'failed';\n }>;\n}\n","/**\n * Payments Module\n * Platform-independent token operations with full wallet repository functionality\n *\n * Includes:\n * - Token CRUD operations\n * - Tombstones for sync\n * - Archived tokens (spent history)\n * - Forked tokens (alternative histories)\n * - Transaction history\n * - Nametag storage\n */\n\nimport type {\n Asset,\n Token,\n TokenStatus,\n TransferRequest,\n TransferResult,\n IncomingTransfer,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n} from '../../types';\nimport type {\n TxfToken,\n TxfTransaction,\n TombstoneEntry,\n NametagData,\n} from '../../types/txf';\nimport { L1PaymentsModule, type L1PaymentsModuleConfig } from './L1PaymentsModule';\nimport { TokenSplitCalculator } from './TokenSplitCalculator';\nimport { TokenSplitExecutor } from './TokenSplitExecutor';\nimport { NametagMinter, type MintNametagResult } from './NametagMinter';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../../storage';\nimport type {\n TransportProvider,\n PeerInfo,\n IncomingTokenTransfer,\n PaymentRequestPayload,\n PaymentRequestResponsePayload,\n IncomingPaymentRequest as TransportPaymentRequest,\n IncomingPaymentRequestResponse as TransportPaymentRequestResponse,\n} from '../../transport';\nimport type { OracleProvider } from '../../oracle';\nimport type { PriceProvider } from '../../price';\nimport type {\n PaymentRequest,\n IncomingPaymentRequest,\n OutgoingPaymentRequest,\n PaymentRequestResult,\n PaymentRequestStatus,\n PaymentRequestHandler,\n PaymentRequestResponse,\n PaymentRequestResponseHandler,\n} from '../../types';\nimport { STORAGE_KEYS_ADDRESS } from '../../constants';\nimport {\n tokenToTxf,\n getCurrentStateHash,\n buildTxfStorageData,\n parseTxfStorageData,\n} from '../../serialization/txf-serializer';\nimport { TokenRegistry } from '../../registry';\n\n// Instant split imports\nimport { InstantSplitExecutor } from './InstantSplitExecutor';\nimport { InstantSplitProcessor } from './InstantSplitProcessor';\nimport type {\n InstantSplitBundle,\n InstantSplitBundleV5,\n InstantSplitProcessResult,\n InstantSplitOptions,\n InstantSplitResult,\n PendingV5Finalization,\n UnconfirmedResolutionResult,\n} from '../../types/instant-split';\nimport { isInstantSplitBundle, isInstantSplitBundleV5 } from '../../types/instant-split';\n\n// SDK imports for token parsing and transfers\nimport { Token as SdkToken } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { TransferCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';\nimport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { AddressScheme } from '@unicitylabs/state-transition-sdk/lib/address/AddressScheme';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport { InclusionProof } from '@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof';\nimport type { IAddress } from '@unicitylabs/state-transition-sdk/lib/address/IAddress';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\n// =============================================================================\n// Transaction History Entry\n// =============================================================================\n\nexport interface TransactionHistoryEntry {\n id: string;\n type: 'SENT' | 'RECEIVED' | 'SPLIT' | 'MINT';\n amount: string;\n coinId: string;\n symbol: string;\n timestamp: number;\n recipientNametag?: string;\n senderPubkey?: string;\n /** TransferResult.id that created this entry (links history to transfer operation) */\n transferId?: string;\n}\n\n// =============================================================================\n// Receive Options & Result\n// =============================================================================\n\nexport interface ReceiveOptions {\n /** Wait for all unconfirmed tokens to be finalized (default: false).\n * When false, calls resolveUnconfirmed() once to submit pending commitments.\n * When true, polls resolveUnconfirmed() + load() until all confirmed or timeout. */\n finalize?: boolean;\n /** Finalization timeout in ms (default: 60000). Only used when finalize=true. */\n timeout?: number;\n /** Poll interval in ms (default: 2000). Only used when finalize=true. */\n pollInterval?: number;\n /** Progress callback after each resolveUnconfirmed() poll. Only used when finalize=true. */\n onProgress?: (result: UnconfirmedResolutionResult) => void;\n}\n\nexport interface ReceiveResult {\n /** Newly received incoming transfers. */\n transfers: IncomingTransfer[];\n /** Finalization result (from resolveUnconfirmed). */\n finalization?: UnconfirmedResolutionResult;\n /** Whether finalization timed out (only when finalize=true). */\n timedOut?: boolean;\n /** Duration of finalization in ms (only when finalize=true). */\n finalizationDurationMs?: number;\n}\n\n// =============================================================================\n// Token Parsing Utilities\n// =============================================================================\n\ninterface ParsedTokenInfo {\n coinId: string;\n symbol: string;\n name: string;\n decimals: number;\n iconUrl?: string;\n amount: string;\n tokenId?: string;\n}\n\n/**\n * Enrich token info with data from TokenRegistry\n */\nfunction enrichWithRegistry(info: ParsedTokenInfo): ParsedTokenInfo {\n const registry = TokenRegistry.getInstance();\n const def = registry.getDefinition(info.coinId);\n if (def) {\n return {\n ...info,\n symbol: def.symbol || info.symbol,\n name: def.name.charAt(0).toUpperCase() + def.name.slice(1),\n decimals: def.decimals ?? 0,\n iconUrl: registry.getIconUrl(info.coinId) ?? undefined,\n };\n }\n return info;\n}\n\n/**\n * Parse token info from SDK token data or TXF JSON\n */\nasync function parseTokenInfo(tokenData: unknown): Promise<ParsedTokenInfo> {\n const defaultInfo: ParsedTokenInfo = {\n coinId: 'ALPHA',\n symbol: 'ALPHA',\n name: 'Alpha Token',\n decimals: 0,\n amount: '0',\n };\n\n try {\n // If it's a string, try to parse as JSON\n const data = typeof tokenData === 'string' ? JSON.parse(tokenData) : tokenData;\n\n // Try to create SDK token and extract coin info using SDK methods\n try {\n const sdkToken = await SdkToken.fromJSON(data);\n\n // Try to get token ID\n if (sdkToken.id) {\n defaultInfo.tokenId = sdkToken.id.toJSON();\n }\n\n // Extract coinId from SDK token's coins structure (lottery-compatible)\n if (sdkToken.coins && sdkToken.coins.coins) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rawCoins = sdkToken.coins.coins as any[];\n if (rawCoins.length > 0) {\n const firstCoin = rawCoins[0];\n // Format: [[CoinId, amount]] or [CoinId, amount]\n let coinIdObj: unknown;\n let amount: unknown;\n\n if (Array.isArray(firstCoin) && firstCoin.length === 2) {\n [coinIdObj, amount] = firstCoin;\n }\n\n // Extract hex string from CoinId object\n if (coinIdObj instanceof CoinId) {\n const coinIdHex = coinIdObj.toJSON() as string;\n return enrichWithRegistry({\n coinId: coinIdHex,\n symbol: coinIdHex.slice(0, 8),\n name: `Token ${coinIdHex.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount ?? '0'),\n tokenId: defaultInfo.tokenId,\n });\n } else if (coinIdObj && typeof coinIdObj === 'object' && 'bytes' in coinIdObj) {\n // CoinId stored as object with bytes\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bytes = (coinIdObj as any).bytes;\n const coinIdHex = Buffer.isBuffer(bytes)\n ? bytes.toString('hex')\n : Array.isArray(bytes)\n ? Buffer.from(bytes).toString('hex')\n : String(bytes);\n return enrichWithRegistry({\n coinId: coinIdHex,\n symbol: coinIdHex.slice(0, 8),\n name: `Token ${coinIdHex.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount ?? '0'),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n\n // Fallback: Extract from JSON representation\n const tokenJson = sdkToken.toJSON() as unknown as Record<string, unknown>;\n const genesisData = tokenJson.genesis as Record<string, unknown> | undefined;\n if (genesisData?.data) {\n const gData = genesisData.data as Record<string, unknown>;\n if (gData.coinData && typeof gData.coinData === 'object') {\n // coinData might be array: [[coinIdHex, amount]]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = gData.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n const coinIdStr = typeof coinIdHex === 'string' ? coinIdHex : String(coinIdHex);\n return enrichWithRegistry({\n coinId: coinIdStr,\n symbol: coinIdStr.slice(0, 8),\n name: `Token ${coinIdStr.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n }\n } catch {\n // SDK parsing failed, try manual extraction\n }\n\n // Manual extraction from TXF format - handle array structure\n if (data.genesis?.data) {\n const genesis = data.genesis.data;\n if (genesis.coinData) {\n // coinData can be: [[coinIdHex, amount]] or {coinIdHex: amount}\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = genesis.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n return enrichWithRegistry({\n coinId: String(coinIdHex),\n symbol: String(coinIdHex).slice(0, 8),\n name: `Token ${String(coinIdHex).slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: genesis.tokenId,\n });\n }\n } else if (typeof coinData === 'object') {\n const coinEntries = Object.entries(coinData);\n if (coinEntries.length > 0) {\n const [coinId, amount] = coinEntries[0] as [string, unknown];\n return enrichWithRegistry({\n coinId,\n symbol: coinId.slice(0, 8),\n name: `Token ${coinId.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: genesis.tokenId,\n });\n }\n }\n }\n if (genesis.tokenId) {\n defaultInfo.tokenId = genesis.tokenId;\n }\n }\n\n // Try to extract from state if available\n if (data.state?.coinData) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const coinData = data.state.coinData as any;\n if (Array.isArray(coinData) && coinData.length > 0) {\n const firstEntry = coinData[0];\n if (Array.isArray(firstEntry) && firstEntry.length === 2) {\n const [coinIdHex, amount] = firstEntry;\n return enrichWithRegistry({\n coinId: String(coinIdHex),\n symbol: String(coinIdHex).slice(0, 8),\n name: `Token ${String(coinIdHex).slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n } else if (typeof coinData === 'object') {\n const coinEntries = Object.entries(coinData);\n if (coinEntries.length > 0) {\n const [coinId, amount] = coinEntries[0] as [string, unknown];\n return enrichWithRegistry({\n coinId,\n symbol: coinId.slice(0, 8),\n name: `Token ${coinId.slice(0, 8)}`,\n decimals: 0,\n amount: String(amount),\n tokenId: defaultInfo.tokenId,\n });\n }\n }\n }\n } catch (error) {\n console.warn('[Payments] Failed to parse token info:', error);\n }\n\n return defaultInfo;\n}\n\n// =============================================================================\n// Repository Utility Functions\n// =============================================================================\n\n/**\n * Extract token ID (genesis tokenId) from sdkData/jsonData\n */\nfunction extractTokenIdFromSdkData(sdkData: string | undefined): string | null {\n if (!sdkData) return null;\n try {\n const txf = JSON.parse(sdkData);\n return txf.genesis?.data?.tokenId || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract state hash from sdkData/jsonData\n */\nfunction extractStateHashFromSdkData(sdkData: string | undefined): string {\n if (!sdkData) return '';\n try {\n const txf = JSON.parse(sdkData) as TxfToken;\n const stateHash = getCurrentStateHash(txf);\n\n // Try alternative locations if not found in standard place\n if (!stateHash) {\n if ((txf as any).state?.hash) {\n return (txf as any).state.hash;\n }\n if ((txf as any).stateHash) {\n return (txf as any).stateHash;\n }\n if ((txf as any).currentStateHash) {\n return (txf as any).currentStateHash;\n }\n }\n\n return stateHash || '';\n } catch {\n return '';\n }\n}\n\n/**\n * Create composite key from tokenId and stateHash\n * Format: {tokenId}_{stateHash}\n * This uniquely identifies a token at a specific state\n */\nfunction createTokenStateKey(tokenId: string, stateHash: string): string {\n return `${tokenId}_${stateHash}`;\n}\n\n/**\n * Extract composite key (tokenId_stateHash) from token\n * Returns null if token doesn't have valid tokenId and stateHash\n */\nfunction extractTokenStateKey(token: Token): string | null {\n const tokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n if (!tokenId || !stateHash) return null;\n return createTokenStateKey(tokenId, stateHash);\n}\n\n/**\n * Convert hex string to Uint8Array\n */\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Check if two tokens have the same genesis tokenId (same token, possibly different states)\n */\nfunction hasSameGenesisTokenId(t1: Token, t2: Token): boolean {\n const id1 = extractTokenIdFromSdkData(t1.sdkData);\n const id2 = extractTokenIdFromSdkData(t2.sdkData);\n return !!(id1 && id2 && id1 === id2);\n}\n\n/**\n * Check if two tokens are exactly the same (same tokenId AND same stateHash)\n */\nfunction isSameTokenState(t1: Token, t2: Token): boolean {\n const key1 = extractTokenStateKey(t1);\n const key2 = extractTokenStateKey(t2);\n return !!(key1 && key2 && key1 === key2);\n}\n\n/**\n * Create tombstone from token - requires valid tokenId and stateHash\n */\nfunction createTombstoneFromToken(token: Token): TombstoneEntry | null {\n const tokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n\n // Both tokenId and stateHash are required for a valid tombstone\n if (!tokenId || !stateHash) {\n return null;\n }\n\n return {\n tokenId,\n stateHash,\n timestamp: Date.now(),\n };\n}\n\n/**\n * Check if incoming token is an incremental update\n */\nfunction isIncrementalUpdate(existing: TxfToken, incoming: TxfToken): boolean {\n if (existing.genesis?.data?.tokenId !== incoming.genesis?.data?.tokenId) {\n return false;\n }\n\n const existingTxns = existing.transactions || [];\n const incomingTxns = incoming.transactions || [];\n\n if (incomingTxns.length < existingTxns.length) {\n return false;\n }\n\n for (let i = 0; i < existingTxns.length; i++) {\n const existingTx = existingTxns[i];\n const incomingTx = incomingTxns[i];\n\n if (existingTx.previousStateHash !== incomingTx.previousStateHash ||\n existingTx.newStateHash !== incomingTx.newStateHash) {\n return false;\n }\n }\n\n for (let i = existingTxns.length; i < incomingTxns.length; i++) {\n const newTx = incomingTxns[i] as TxfTransaction;\n if (newTx.inclusionProof === null) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Count committed transactions\n */\nfunction countCommittedTxns(txf: TxfToken): number {\n return (txf.transactions || []).filter(\n (tx: TxfTransaction) => tx.inclusionProof !== null\n ).length;\n}\n\n/**\n * Prune tombstones by age and count\n */\nfunction pruneTombstonesByAge(\n tombstones: TombstoneEntry[],\n maxAge: number = 30 * 24 * 60 * 60 * 1000,\n maxCount: number = 100\n): TombstoneEntry[] {\n const now = Date.now();\n let result = tombstones.filter(t => (now - t.timestamp) < maxAge);\n\n if (result.length > maxCount) {\n result = [...result].sort((a, b) => b.timestamp - a.timestamp);\n result = result.slice(0, maxCount);\n }\n\n return result;\n}\n\n/**\n * Prune Map by count\n */\nfunction pruneMapByCount<T>(items: Map<string, T>, maxCount: number): Map<string, T> {\n if (items.size <= maxCount) {\n return new Map(items);\n }\n\n const entries = [...items.entries()];\n const toKeep = entries.slice(entries.length - maxCount);\n return new Map(toKeep);\n}\n\n/**\n * Find best token version from archives\n */\nfunction findBestTokenVersion(\n tokenId: string,\n archivedTokens: Map<string, TxfToken>,\n forkedTokens: Map<string, TxfToken>\n): TxfToken | null {\n const candidates: TxfToken[] = [];\n\n const archived = archivedTokens.get(tokenId);\n if (archived) candidates.push(archived);\n\n for (const [key, forked] of forkedTokens) {\n if (key.startsWith(tokenId + '_')) {\n candidates.push(forked);\n }\n }\n\n if (candidates.length === 0) return null;\n\n candidates.sort((a, b) => countCommittedTxns(b) - countCommittedTxns(a));\n return candidates[0];\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface PaymentsModuleConfig {\n /** Auto-sync after operations */\n autoSync?: boolean;\n /** Auto-validate with aggregator */\n autoValidate?: boolean;\n /** Retry failed transfers */\n retryFailed?: boolean;\n /** Max retry attempts */\n maxRetries?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** L1 (ALPHA blockchain) configuration. Set to null to explicitly disable L1. */\n l1?: L1PaymentsModuleConfig | null;\n}\n\n// =============================================================================\n// NOSTR-FIRST Proof Polling Types\n// =============================================================================\n\n/**\n * Job for background proof polling (NOSTR-FIRST pattern)\n */\nexport interface ProofPollingJob {\n tokenId: string;\n requestIdHex: string;\n commitmentJson: string;\n startedAt: number;\n attemptCount: number;\n lastAttemptAt: number;\n /** Callback when proof is received */\n onProofReceived?: (tokenId: string) => void;\n}\n\n// =============================================================================\n// Dependencies Interface\n// =============================================================================\n\nexport interface PaymentsModuleDependencies {\n identity: FullIdentity;\n storage: StorageProvider;\n /** @deprecated Use tokenStorageProviders instead */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Multiple token storage providers (e.g., IPFS, MongoDB, file) */\n tokenStorageProviders?: Map<string, TokenStorageProvider<TxfStorageDataBase>>;\n transport: TransportProvider;\n oracle: OracleProvider;\n emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;\n /** Chain code for BIP32 HD derivation (for L1 multi-address support) */\n chainCode?: string;\n /** Additional L1 addresses to watch */\n l1Addresses?: string[];\n /** Price provider (optional — enables fiat value display) */\n price?: PriceProvider;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class PaymentsModule {\n private readonly moduleConfig: Omit<Required<PaymentsModuleConfig>, 'l1'>;\n private deps: PaymentsModuleDependencies | null = null;\n\n /** L1 (ALPHA blockchain) payments sub-module (null if disabled) */\n readonly l1: L1PaymentsModule | null;\n\n // Token State\n private tokens: Map<string, Token> = new Map();\n private pendingTransfers: Map<string, TransferResult> = new Map();\n private pendingBackgroundTasks: Promise<void>[] = [];\n\n // Repository State (tombstones, archives, forked, history)\n private tombstones: TombstoneEntry[] = [];\n private archivedTokens: Map<string, TxfToken> = new Map();\n private forkedTokens: Map<string, TxfToken> = new Map();\n private transactionHistory: TransactionHistoryEntry[] = [];\n private nametags: NametagData[] = [];\n\n // Payment Requests State (Incoming)\n private paymentRequests: IncomingPaymentRequest[] = [];\n private paymentRequestHandlers: Set<PaymentRequestHandler> = new Set();\n\n // Payment Requests State (Outgoing)\n private outgoingPaymentRequests: Map<string, OutgoingPaymentRequest> = new Map();\n private paymentRequestResponseHandlers: Set<PaymentRequestResponseHandler> = new Set();\n private pendingResponseResolvers: Map<string, {\n resolve: (response: PaymentRequestResponse) => void;\n reject: (error: Error) => void;\n timeout: ReturnType<typeof setTimeout>;\n }> = new Map();\n\n // Subscriptions\n private unsubscribeTransfers: (() => void) | null = null;\n private unsubscribePaymentRequests: (() => void) | null = null;\n private unsubscribePaymentRequestResponses: (() => void) | null = null;\n\n // NOSTR-FIRST proof polling (background proof verification)\n private proofPollingJobs: Map<string, ProofPollingJob> = new Map();\n private proofPollingInterval: ReturnType<typeof setInterval> | null = null;\n private static readonly PROOF_POLLING_INTERVAL_MS = 2000; // Poll every 2s\n private static readonly PROOF_POLLING_MAX_ATTEMPTS = 30; // Max 30 attempts (~60s)\n\n // Storage event subscriptions (push-based sync)\n private storageEventUnsubscribers: (() => void)[] = [];\n private syncDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n private static readonly SYNC_DEBOUNCE_MS = 500;\n\n /** Sync coalescing: concurrent sync() calls share the same operation */\n private _syncInProgress: Promise<{ added: number; removed: number }> | null = null;\n\n constructor(config?: PaymentsModuleConfig) {\n this.moduleConfig = {\n autoSync: config?.autoSync ?? true,\n autoValidate: config?.autoValidate ?? true,\n retryFailed: config?.retryFailed ?? true,\n maxRetries: config?.maxRetries ?? 3,\n debug: config?.debug ?? false,\n };\n\n // Initialize L1 sub-module by default (L1PaymentsModule has default electrumUrl).\n // Only skip if l1 is explicitly set to null.\n this.l1 = config?.l1 === null ? null : new L1PaymentsModule(config?.l1);\n }\n\n /**\n * Get the current module configuration (excluding L1 config).\n *\n * @returns Resolved configuration with all defaults applied.\n */\n getConfig(): Omit<Required<PaymentsModuleConfig>, 'l1'> {\n return this.moduleConfig;\n }\n\n /** Price provider (optional) */\n private priceProvider: PriceProvider | null = null;\n\n private log(...args: unknown[]): void {\n if (this.moduleConfig.debug) {\n console.log('[PaymentsModule]', ...args);\n }\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Initialize module with dependencies\n */\n initialize(deps: PaymentsModuleDependencies): void {\n // Clean up previous subscriptions before re-initializing\n this.unsubscribeTransfers?.();\n this.unsubscribeTransfers = null;\n this.unsubscribePaymentRequests?.();\n this.unsubscribePaymentRequests = null;\n this.unsubscribePaymentRequestResponses?.();\n this.unsubscribePaymentRequestResponses = null;\n\n // Reset per-address state (will be re-populated by load())\n this.tokens.clear();\n this.pendingTransfers.clear();\n this.tombstones = [];\n this.archivedTokens.clear();\n this.forkedTokens.clear();\n this.transactionHistory = [];\n this.nametags = [];\n\n this.deps = deps;\n this.priceProvider = deps.price ?? null;\n\n // Initialize L1 sub-module with chain code, addresses, and transport (if enabled)\n if (this.l1) {\n this.l1.initialize({\n identity: deps.identity,\n chainCode: deps.chainCode,\n addresses: deps.l1Addresses,\n transport: deps.transport,\n });\n }\n\n // Subscribe to incoming transfers\n this.unsubscribeTransfers = deps.transport.onTokenTransfer((transfer) =>\n this.handleIncomingTransfer(transfer)\n );\n\n // Subscribe to incoming payment requests (if supported)\n if (deps.transport.onPaymentRequest) {\n this.unsubscribePaymentRequests = deps.transport.onPaymentRequest((request) => {\n this.handleIncomingPaymentRequest(request);\n });\n }\n\n // Subscribe to payment request responses (if supported)\n if (deps.transport.onPaymentRequestResponse) {\n this.unsubscribePaymentRequestResponses = deps.transport.onPaymentRequestResponse((response) => {\n this.handlePaymentRequestResponse(response);\n });\n }\n\n // Subscribe to storage provider events (push-based sync)\n this.subscribeToStorageEvents();\n }\n\n /**\n * Load all token data from storage providers and restore wallet state.\n *\n * Loads tokens, nametag data, transaction history, and pending transfers\n * from configured storage providers. Restores pending V5 tokens and\n * triggers a fire-and-forget {@link resolveUnconfirmed} call.\n */\n async load(): Promise<void> {\n this.ensureInitialized();\n\n // Load metadata from TokenStorageProviders (archived, tombstones, forked)\n // Active tokens are NOT stored in TXF - they are loaded from token-xxx files\n const providers = this.getTokenStorageProviders();\n for (const [id, provider] of providers) {\n try {\n const result = await provider.load();\n if (result.success && result.data) {\n this.loadFromStorageData(result.data);\n this.log(`Loaded metadata from provider ${id}`);\n break; // Use first successful provider\n }\n } catch (err) {\n console.error(`[Payments] Failed to load from provider ${id}:`, err);\n }\n }\n\n // Restore pending V5 tokens\n await this.loadPendingV5Tokens();\n\n // Load transaction history\n const historyData = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);\n if (historyData) {\n try {\n this.transactionHistory = JSON.parse(historyData);\n } catch {\n this.transactionHistory = [];\n }\n }\n\n // Load pending transfers\n const pending = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);\n if (pending) {\n const transfers = JSON.parse(pending) as TransferResult[];\n for (const transfer of transfers) {\n this.pendingTransfers.set(transfer.id, transfer);\n }\n }\n\n // After loading, try to resolve any unconfirmed tokens\n this.resolveUnconfirmed().catch(() => {});\n }\n\n /**\n * Cleanup all subscriptions, polling jobs, and pending resolvers.\n *\n * Should be called when the wallet is being shut down or the module is\n * no longer needed. Also destroys the L1 sub-module if present.\n */\n destroy(): void {\n this.unsubscribeTransfers?.();\n this.unsubscribeTransfers = null;\n this.unsubscribePaymentRequests?.();\n this.unsubscribePaymentRequests = null;\n this.unsubscribePaymentRequestResponses?.();\n this.unsubscribePaymentRequestResponses = null;\n this.paymentRequestHandlers.clear();\n this.paymentRequestResponseHandlers.clear();\n\n // Stop proof polling (NOSTR-FIRST)\n this.stopProofPolling();\n this.proofPollingJobs.clear();\n\n // Clear pending response resolvers\n for (const [, resolver] of this.pendingResponseResolvers) {\n clearTimeout(resolver.timeout);\n resolver.reject(new Error('Module destroyed'));\n }\n this.pendingResponseResolvers.clear();\n\n // Clean up storage event subscriptions\n this.unsubscribeStorageEvents();\n\n if (this.l1) {\n this.l1.destroy();\n }\n }\n\n // ===========================================================================\n // Public API - Send\n // ===========================================================================\n\n /**\n * Send tokens to recipient\n * Supports automatic token splitting when exact amount is needed\n */\n async send(request: TransferRequest): Promise<TransferResult> {\n this.ensureInitialized();\n\n // Use mutable result for building the transfer\n const result: { -readonly [K in keyof TransferResult]: TransferResult[K] } = {\n id: crypto.randomUUID(),\n status: 'pending',\n tokens: [],\n tokenTransfers: [],\n };\n\n try {\n // Resolve recipient once — single network query\n const peerInfo = await this.deps!.transport.resolve?.(request.recipient) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(request.recipient, peerInfo);\n const recipientAddress = await this.resolveRecipientAddress(request.recipient, request.addressMode, peerInfo);\n\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available. Oracle provider must implement getStateTransitionClient()');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available. Oracle provider must implement getTrustBase()');\n }\n\n // Calculate optimal split plan\n const calculator = new TokenSplitCalculator();\n const availableTokens = Array.from(this.tokens.values());\n const splitPlan = await calculator.calculateOptimalSplit(\n availableTokens,\n BigInt(request.amount),\n request.coinId\n );\n\n if (!splitPlan) {\n throw new Error('Insufficient balance');\n }\n\n // Collect all tokens involved\n const tokensToSend: Token[] = splitPlan.tokensToTransferDirectly.map(t => t.uiToken);\n if (splitPlan.tokenToSplit) {\n tokensToSend.push(splitPlan.tokenToSplit.uiToken);\n }\n result.tokens = tokensToSend;\n\n // Mark as transferring\n for (const token of tokensToSend) {\n token.status = 'transferring';\n this.tokens.set(token.id, token);\n }\n\n // Save to outbox for recovery\n await this.saveToOutbox(result, recipientPubkey);\n\n result.status = 'submitted';\n\n const recipientNametag = request.recipient.startsWith('@') ? request.recipient.slice(1) : undefined;\n\n const transferMode = request.transferMode ?? 'instant';\n\n // Handle split if required\n if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {\n if (transferMode === 'conservative') {\n // Conservative split: use TokenSplitExecutor — collects all proofs before sending\n this.log('Executing conservative split...');\n const splitExecutor = new TokenSplitExecutor({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n });\n\n const splitResult = await splitExecutor.executeSplit(\n splitPlan.tokenToSplit.sdkToken,\n splitPlan.splitAmount!,\n splitPlan.remainderAmount!,\n splitPlan.coinId,\n recipientAddress,\n );\n\n // Save change token\n const changeTokenData = splitResult.tokenForSender.toJSON();\n const changeUiToken: Token = {\n id: crypto.randomUUID(),\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n name: this.getCoinName(request.coinId),\n decimals: this.getCoinDecimals(request.coinId),\n iconUrl: this.getCoinIconUrl(request.coinId),\n amount: splitPlan.remainderAmount!.toString(),\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(changeTokenData),\n };\n await this.addToken(changeUiToken, true);\n this.log(`Conservative split: change token saved: ${changeUiToken.id}`);\n\n // Send fully finalized { sourceToken, transferTx } via Nostr\n await this.deps!.transport.sendTokenTransfer(recipientPubkey, {\n sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),\n transferTx: JSON.stringify(splitResult.recipientTransferTx.toJSON()),\n memo: request.memo,\n } as unknown as import('../../transport').TokenTransferPayload);\n\n // Get request ID from the transfer commitment for tracking\n const splitCommitmentRequestId = splitResult.recipientTransferTx?.data?.requestId\n ?? splitResult.recipientTransferTx?.requestId;\n const splitRequestIdHex = splitCommitmentRequestId instanceof Uint8Array\n ? Array.from(splitCommitmentRequestId).map((b: number) => b.toString(16).padStart(2, '0')).join('')\n : splitCommitmentRequestId ? String(splitCommitmentRequestId) : undefined;\n\n // Remove the original token that was split — skipHistory because we record below\n await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);\n result.tokenTransfers.push({\n sourceTokenId: splitPlan.tokenToSplit.uiToken.id,\n method: 'split',\n requestIdHex: splitRequestIdHex,\n });\n this.log(`Conservative split transfer completed`);\n } else {\n // Instant split: use InstantSplitExecutor for fast (~2.3s) splits\n this.log('Executing instant split...');\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const devMode = (this.deps!.oracle as any).isDevMode?.() ?? false;\n const executor = new InstantSplitExecutor({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n devMode,\n });\n\n const instantResult = await executor.executeSplitInstant(\n splitPlan.tokenToSplit.sdkToken,\n splitPlan.splitAmount!,\n splitPlan.remainderAmount!,\n splitPlan.coinId,\n recipientAddress,\n this.deps!.transport,\n recipientPubkey,\n {\n onChangeTokenCreated: async (changeToken) => {\n const changeTokenData = changeToken.toJSON();\n const uiToken: Token = {\n id: crypto.randomUUID(),\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n name: this.getCoinName(request.coinId),\n decimals: this.getCoinDecimals(request.coinId),\n iconUrl: this.getCoinIconUrl(request.coinId),\n amount: splitPlan.remainderAmount!.toString(),\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(changeTokenData),\n };\n await this.addToken(uiToken, true);\n this.log(`Change token saved via background: ${uiToken.id}`);\n },\n onStorageSync: async () => {\n await this.save();\n return true;\n },\n }\n );\n\n if (!instantResult.success) {\n throw new Error(instantResult.error || 'Instant split failed');\n }\n\n // Track background task for change token creation\n if (instantResult.backgroundPromise) {\n this.pendingBackgroundTasks.push(instantResult.backgroundPromise);\n }\n\n await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);\n result.tokenTransfers.push({\n sourceTokenId: splitPlan.tokenToSplit.uiToken.id,\n method: 'split',\n splitGroupId: instantResult.splitGroupId,\n nostrEventId: instantResult.nostrEventId,\n });\n this.log(`Instant split transfer completed`);\n }\n }\n\n // Transfer direct tokens (no split needed)\n for (const tokenWithAmount of splitPlan.tokensToTransferDirectly) {\n const token = tokenWithAmount.uiToken;\n\n // Create SDK transfer commitment\n const commitment = await this.createSdkCommitment(token, recipientAddress, signingService);\n\n if (transferMode === 'conservative') {\n // Conservative direct: wait for proof, then send fully finalized transfer\n console.log(`[Payments] CONSERVATIVE: Sending direct token ${token.id.slice(0, 8)}... to ${recipientPubkey.slice(0, 8)}...`);\n\n // Submit commitment to aggregator and wait for inclusion proof\n const submitResponse = await stClient.submitTransferCommitment(commitment);\n if (submitResponse.status !== 'SUCCESS' && submitResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer commitment failed: ${submitResponse.status}`);\n }\n\n const inclusionProof = await waitInclusionProof(trustBase, stClient, commitment);\n const transferTx = commitment.toTransaction(inclusionProof);\n\n // Send fully finalized { sourceToken, transferTx } via Nostr\n await this.deps!.transport.sendTokenTransfer(recipientPubkey, {\n sourceToken: JSON.stringify(tokenWithAmount.sdkToken.toJSON()),\n transferTx: JSON.stringify(transferTx.toJSON()),\n memo: request.memo,\n } as unknown as import('../../transport').TokenTransferPayload);\n console.log(`[Payments] CONSERVATIVE: Direct token sent successfully`);\n } else {\n // Instant direct: NOSTR-FIRST - send commitment + source token via Nostr immediately\n // Submit to aggregator in background. Receiver handles via handleCommitmentOnlyTransfer().\n console.log(`[Payments] NOSTR-FIRST: Sending direct token ${token.id.slice(0, 8)}... to ${recipientPubkey.slice(0, 8)}...`);\n await this.deps!.transport.sendTokenTransfer(recipientPubkey, {\n sourceToken: JSON.stringify(tokenWithAmount.sdkToken.toJSON()),\n commitmentData: JSON.stringify(commitment.toJSON()),\n memo: request.memo,\n } as unknown as import('../../transport').TokenTransferPayload);\n console.log(`[Payments] NOSTR-FIRST: Direct token sent successfully`);\n\n // Submit commitment to aggregator in background (fire-and-forget)\n stClient.submitTransferCommitment(commitment).catch(err =>\n console.error('[Payments] Background commitment submit failed:', err)\n );\n }\n\n // Get request ID as hex string for tracking\n const requestIdBytes = commitment.requestId;\n const requestIdHex = requestIdBytes instanceof Uint8Array\n ? Array.from(requestIdBytes).map(b => b.toString(16).padStart(2, '0')).join('')\n : String(requestIdBytes);\n\n result.tokenTransfers.push({\n sourceTokenId: token.id,\n method: 'direct',\n requestIdHex,\n });\n\n this.log(`Token ${token.id} sent via ${transferMode.toUpperCase()}, requestId: ${requestIdHex}`);\n\n // Remove sent token (creates tombstone) — skipHistory because we record below\n await this.removeToken(token.id, recipientNametag, true);\n }\n\n result.status = 'delivered';\n\n // Save state\n await this.save();\n await this.removeFromOutbox(result.id);\n\n result.status = 'completed';\n\n // Add to transaction history\n await this.addToHistory({\n type: 'SENT',\n amount: request.amount,\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n timestamp: Date.now(),\n recipientNametag,\n transferId: result.id,\n });\n\n this.deps!.emitEvent('transfer:confirmed', result);\n return result;\n } catch (error) {\n result.status = 'failed';\n result.error = error instanceof Error ? error.message : String(error);\n\n // Restore tokens\n for (const token of result.tokens) {\n token.status = 'confirmed';\n this.tokens.set(token.id, token);\n }\n\n this.deps!.emitEvent('transfer:failed', result);\n throw error;\n }\n }\n\n /**\n * Get coin symbol from coinId\n */\n private getCoinSymbol(coinId: string): string {\n return TokenRegistry.getInstance().getSymbol(coinId);\n }\n\n /**\n * Get coin name from coinId\n */\n private getCoinName(coinId: string): string {\n return TokenRegistry.getInstance().getName(coinId);\n }\n\n /**\n * Get coin decimals from coinId\n */\n private getCoinDecimals(coinId: string): number {\n return TokenRegistry.getInstance().getDecimals(coinId);\n }\n\n /**\n * Get coin icon URL from coinId\n */\n private getCoinIconUrl(coinId: string): string | undefined {\n return TokenRegistry.getInstance().getIconUrl(coinId) ?? undefined;\n }\n\n // ===========================================================================\n // Public API - Instant Split (V5 Optimized)\n // ===========================================================================\n\n /**\n * Send tokens using INSTANT_SPLIT V5 optimized flow.\n *\n * This achieves ~2.3s critical path latency instead of ~42s by:\n * 1. Waiting only for burn proof (required)\n * 2. Creating transfer commitment from mint data (no mint proof needed)\n * 3. Sending bundle via Nostr immediately\n * 4. Processing mints in background\n *\n * @param request - Transfer request with recipient, amount, and coinId\n * @param options - Optional instant split configuration\n * @returns InstantSplitResult with timing info\n */\n async sendInstant(\n request: TransferRequest,\n options?: InstantSplitOptions\n ): Promise<InstantSplitResult> {\n this.ensureInitialized();\n\n const startTime = performance.now();\n\n try {\n // Resolve recipient once — single network query\n const peerInfo = await this.deps!.transport.resolve?.(request.recipient) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(request.recipient, peerInfo);\n const recipientAddress = await this.resolveRecipientAddress(request.recipient, request.addressMode, peerInfo);\n\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available');\n }\n\n // Calculate optimal split plan\n const calculator = new TokenSplitCalculator();\n const availableTokens = Array.from(this.tokens.values());\n const splitPlan = await calculator.calculateOptimalSplit(\n availableTokens,\n BigInt(request.amount),\n request.coinId\n );\n\n if (!splitPlan) {\n throw new Error('Insufficient balance');\n }\n\n if (!splitPlan.requiresSplit || !splitPlan.tokenToSplit) {\n // For direct transfers without split, fall back to standard flow\n this.log('No split required, falling back to standard send()');\n const result = await this.send(request);\n return {\n success: result.status === 'completed',\n criticalPathDurationMs: performance.now() - startTime,\n error: result.error,\n };\n }\n\n this.log(`InstantSplit: amount=${splitPlan.splitAmount}, remainder=${splitPlan.remainderAmount}`);\n\n // Mark token as transferring\n const tokenToSplit = splitPlan.tokenToSplit.uiToken;\n tokenToSplit.status = 'transferring';\n this.tokens.set(tokenToSplit.id, tokenToSplit);\n\n // Check if dev mode\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const devMode = options?.devMode ?? (this.deps!.oracle as any).isDevMode?.() ?? false;\n\n // Create instant split executor\n const executor = new InstantSplitExecutor({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n devMode,\n });\n\n // Execute instant split\n const result = await executor.executeSplitInstant(\n splitPlan.tokenToSplit.sdkToken,\n splitPlan.splitAmount!,\n splitPlan.remainderAmount!,\n splitPlan.coinId,\n recipientAddress,\n this.deps!.transport,\n recipientPubkey,\n {\n ...options,\n onChangeTokenCreated: async (changeToken) => {\n // Save change token when background completes\n const changeTokenData = changeToken.toJSON();\n const uiToken: Token = {\n id: crypto.randomUUID(),\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n name: this.getCoinName(request.coinId),\n decimals: this.getCoinDecimals(request.coinId),\n iconUrl: this.getCoinIconUrl(request.coinId),\n amount: splitPlan.remainderAmount!.toString(),\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(changeTokenData),\n };\n await this.addToken(uiToken, true);\n this.log(`Change token saved via background: ${uiToken.id}`);\n },\n onStorageSync: async () => {\n await this.save();\n return true;\n },\n }\n );\n\n if (result.success) {\n // Track background task for change token creation\n if (result.backgroundPromise) {\n this.pendingBackgroundTasks.push(result.backgroundPromise);\n }\n\n // Remove the original token — skipHistory because we record below\n const recipientNametag = request.recipient.startsWith('@') ? request.recipient.slice(1) : undefined;\n await this.removeToken(tokenToSplit.id, recipientNametag, true);\n\n // Add to transaction history (single entry for the actual sent amount)\n await this.addToHistory({\n type: 'SENT',\n amount: request.amount,\n coinId: request.coinId,\n symbol: this.getCoinSymbol(request.coinId),\n timestamp: Date.now(),\n recipientNametag,\n });\n\n await this.save();\n } else {\n // Restore token on failure\n tokenToSplit.status = 'confirmed';\n this.tokens.set(tokenToSplit.id, tokenToSplit);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n criticalPathDurationMs: performance.now() - startTime,\n error: errorMessage,\n };\n }\n }\n\n /**\n * Process a received INSTANT_SPLIT bundle.\n *\n * This should be called when receiving an instant split bundle via transport.\n * It handles the recipient-side processing:\n * 1. Validate burn transaction\n * 2. Submit and wait for mint proof\n * 3. Submit and wait for transfer proof\n * 4. Finalize and save the token\n *\n * @param bundle - The received InstantSplitBundle (V4 or V5)\n * @param senderPubkey - Sender's public key for verification\n * @returns Processing result with finalized token\n */\n private async processInstantSplitBundle(\n bundle: InstantSplitBundle,\n senderPubkey: string\n ): Promise<InstantSplitProcessResult> {\n this.ensureInitialized();\n\n if (!isInstantSplitBundleV5(bundle)) {\n // V4 (dev mode) still processes synchronously\n return this.processInstantSplitBundleSync(bundle, senderPubkey);\n }\n\n // V5: save immediately as unconfirmed, resolve proofs lazily\n try {\n // Use deterministic ID from splitGroupId to deduplicate Nostr re-deliveries\n const deterministicId = `v5split_${bundle.splitGroupId}`;\n if (this.tokens.has(deterministicId)) {\n this.log(`V5 bundle ${deterministicId.slice(0, 16)}... already exists, skipping duplicate`);\n return { success: true, durationMs: 0 };\n }\n\n const registry = TokenRegistry.getInstance();\n const pendingData: PendingV5Finalization = {\n type: 'v5_bundle',\n stage: 'RECEIVED',\n bundleJson: JSON.stringify(bundle),\n senderPubkey,\n savedAt: Date.now(),\n attemptCount: 0,\n };\n\n const uiToken: Token = {\n id: deterministicId,\n coinId: bundle.coinId,\n symbol: registry.getSymbol(bundle.coinId) || bundle.coinId,\n name: registry.getName(bundle.coinId) || bundle.coinId,\n decimals: registry.getDecimals(bundle.coinId) ?? 8,\n amount: bundle.amount,\n status: 'submitted', // UNCONFIRMED\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify({ _pendingFinalization: pendingData }),\n };\n\n await this.addToken(uiToken, false);\n this.log(`V5 bundle saved as unconfirmed: ${uiToken.id.slice(0, 8)}...`);\n\n // Emit incoming transfer event\n this.deps!.emitEvent('transfer:incoming', {\n id: bundle.splitGroupId,\n senderPubkey,\n tokens: [uiToken],\n receivedAt: Date.now(),\n });\n\n await this.save();\n\n // Fire-and-forget: try to resolve immediately\n this.resolveUnconfirmed().catch(() => {});\n\n return { success: true, durationMs: 0 };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMessage,\n durationMs: 0,\n };\n }\n }\n\n /**\n * Synchronous V4 bundle processing (dev mode only).\n * Kept for backward compatibility with V4 bundles.\n */\n private async processInstantSplitBundleSync(\n bundle: InstantSplitBundle,\n senderPubkey: string\n ): Promise<InstantSplitProcessResult> {\n try {\n const signingService = await this.createSigningService();\n\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n throw new Error('State transition client not available');\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n throw new Error('Trust base not available');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const devMode = (this.deps!.oracle as any).isDevMode?.() ?? false;\n\n const processor = new InstantSplitProcessor({\n stateTransitionClient: stClient,\n trustBase,\n devMode,\n });\n\n const result = await processor.processReceivedBundle(\n bundle,\n signingService,\n senderPubkey,\n {\n findNametagToken: async (proxyAddress: string) => {\n const currentNametag = this.getNametag();\n if (currentNametag?.token) {\n try {\n const nametagToken = await SdkToken.fromJSON(currentNametag.token);\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address === proxyAddress) {\n return nametagToken;\n }\n this.log(`Nametag PROXY address mismatch: ${proxy.address} !== ${proxyAddress}`);\n return null;\n } catch (err) {\n this.log('Failed to parse nametag token:', err);\n return null;\n }\n }\n return null;\n },\n }\n );\n\n if (result.success && result.token) {\n const tokenData = result.token.toJSON();\n const info = await parseTokenInfo(tokenData);\n\n const uiToken: Token = {\n id: crypto.randomUUID(),\n coinId: info.coinId,\n symbol: info.symbol,\n name: info.name,\n decimals: info.decimals,\n iconUrl: info.iconUrl,\n amount: bundle.amount,\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: JSON.stringify(tokenData),\n };\n\n await this.addToken(uiToken);\n\n await this.addToHistory({\n type: 'RECEIVED',\n amount: bundle.amount,\n coinId: info.coinId,\n symbol: info.symbol,\n timestamp: Date.now(),\n senderPubkey,\n });\n\n await this.save();\n\n this.deps!.emitEvent('transfer:incoming', {\n id: bundle.splitGroupId,\n senderPubkey,\n tokens: [uiToken],\n receivedAt: Date.now(),\n });\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: errorMessage,\n durationMs: 0,\n };\n }\n }\n\n /**\n * Type-guard: check whether a payload is a valid {@link InstantSplitBundle} (V4 or V5).\n *\n * @param payload - The object to test.\n * @returns `true` if the payload matches the InstantSplitBundle shape.\n */\n private isInstantSplitBundle(payload: unknown): payload is InstantSplitBundle {\n return isInstantSplitBundle(payload);\n }\n\n // ===========================================================================\n // Public API - Payment Requests\n // ===========================================================================\n\n /**\n * Send a payment request to someone\n * @param recipientPubkeyOrNametag - Recipient's pubkey or @nametag\n * @param request - Payment request details\n * @returns Result with event ID\n */\n async sendPaymentRequest(\n recipientPubkeyOrNametag: string,\n request: Omit<PaymentRequest, 'id' | 'createdAt'>\n ): Promise<PaymentRequestResult> {\n this.ensureInitialized();\n\n if (!this.deps!.transport.sendPaymentRequest) {\n return {\n success: false,\n error: 'Transport provider does not support payment requests',\n };\n }\n\n try {\n // Resolve recipient\n const peerInfo = await this.deps!.transport.resolve?.(recipientPubkeyOrNametag) ?? null;\n const recipientPubkey = this.resolveTransportPubkey(recipientPubkeyOrNametag, peerInfo);\n\n // Build payload\n const payload: PaymentRequestPayload = {\n amount: request.amount,\n coinId: request.coinId,\n message: request.message,\n recipientNametag: request.recipientNametag,\n metadata: request.metadata,\n };\n\n // Send via transport\n const eventId = await this.deps!.transport.sendPaymentRequest(recipientPubkey, payload);\n const requestId = crypto.randomUUID();\n\n // Track outgoing request\n const outgoingRequest: OutgoingPaymentRequest = {\n id: requestId,\n eventId,\n recipientPubkey,\n recipientNametag: recipientPubkeyOrNametag.startsWith('@')\n ? recipientPubkeyOrNametag.slice(1)\n : undefined,\n amount: request.amount,\n coinId: request.coinId,\n message: request.message,\n createdAt: Date.now(),\n status: 'pending',\n };\n this.outgoingPaymentRequests.set(requestId, outgoingRequest);\n\n this.log(`Payment request sent: ${eventId}`);\n\n return {\n success: true,\n requestId,\n eventId,\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log(`Failed to send payment request: ${errorMsg}`);\n return {\n success: false,\n error: errorMsg,\n };\n }\n }\n\n /**\n * Subscribe to incoming payment requests\n * @param handler - Handler function for incoming requests\n * @returns Unsubscribe function\n */\n onPaymentRequest(handler: PaymentRequestHandler): () => void {\n this.paymentRequestHandlers.add(handler);\n return () => this.paymentRequestHandlers.delete(handler);\n }\n\n /**\n * Get all payment requests\n * @param filter - Optional status filter\n */\n getPaymentRequests(filter?: { status?: PaymentRequestStatus }): IncomingPaymentRequest[] {\n if (filter?.status) {\n return this.paymentRequests.filter((r) => r.status === filter.status);\n }\n return [...this.paymentRequests];\n }\n\n /**\n * Get the count of payment requests with status `'pending'`.\n *\n * @returns Number of pending incoming payment requests.\n */\n getPendingPaymentRequestsCount(): number {\n return this.paymentRequests.filter((r) => r.status === 'pending').length;\n }\n\n /**\n * Accept a payment request and notify the requester.\n *\n * Marks the request as `'accepted'` and sends a response via transport.\n * The caller should subsequently call {@link send} to fulfill the payment.\n *\n * @param requestId - ID of the incoming payment request to accept.\n */\n async acceptPaymentRequest(requestId: string): Promise<void> {\n this.updatePaymentRequestStatus(requestId, 'accepted');\n await this.sendPaymentRequestResponse(requestId, 'accepted');\n }\n\n /**\n * Reject a payment request and notify the requester.\n *\n * @param requestId - ID of the incoming payment request to reject.\n */\n async rejectPaymentRequest(requestId: string): Promise<void> {\n this.updatePaymentRequestStatus(requestId, 'rejected');\n await this.sendPaymentRequestResponse(requestId, 'rejected');\n }\n\n /**\n * Mark a payment request as paid (local status update only).\n *\n * Typically called after a successful {@link send} to record that the\n * request has been fulfilled.\n *\n * @param requestId - ID of the incoming payment request to mark as paid.\n */\n markPaymentRequestPaid(requestId: string): void {\n this.updatePaymentRequestStatus(requestId, 'paid');\n }\n\n /**\n * Remove all non-pending incoming payment requests from memory.\n *\n * Keeps only requests with status `'pending'`.\n */\n clearProcessedPaymentRequests(): void {\n this.paymentRequests = this.paymentRequests.filter((r) => r.status === 'pending');\n }\n\n /**\n * Remove a specific incoming payment request by ID.\n *\n * @param requestId - ID of the payment request to remove.\n */\n removePaymentRequest(requestId: string): void {\n this.paymentRequests = this.paymentRequests.filter((r) => r.id !== requestId);\n }\n\n /**\n * Pay a payment request directly\n * Convenience method that accepts, sends, and marks as paid\n */\n async payPaymentRequest(requestId: string, memo?: string): Promise<TransferResult> {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (!request) {\n throw new Error(`Payment request not found: ${requestId}`);\n }\n\n if (request.status !== 'pending' && request.status !== 'accepted') {\n throw new Error(`Payment request is not pending or accepted: ${request.status}`);\n }\n\n // Mark as accepted (don't send response yet, wait for payment)\n this.updatePaymentRequestStatus(requestId, 'accepted');\n\n try {\n // Send the payment\n const result = await this.send({\n coinId: request.coinId,\n amount: request.amount,\n recipient: request.senderPubkey,\n memo: memo || request.message,\n });\n\n // Mark as paid and send response with transfer ID\n this.updatePaymentRequestStatus(requestId, 'paid');\n await this.sendPaymentRequestResponse(requestId, 'paid', result.id);\n\n return result;\n } catch (error) {\n // Revert to pending on failure\n this.updatePaymentRequestStatus(requestId, 'pending');\n throw error;\n }\n }\n\n private updatePaymentRequestStatus(requestId: string, status: PaymentRequestStatus): void {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (request) {\n request.status = status;\n\n // Emit event\n const eventType = `payment_request:${status}` as const;\n if (eventType === 'payment_request:accepted' ||\n eventType === 'payment_request:rejected' ||\n eventType === 'payment_request:paid') {\n this.deps?.emitEvent(eventType, request);\n }\n }\n }\n\n private handleIncomingPaymentRequest(transportRequest: TransportPaymentRequest): void {\n // Check for duplicates\n if (this.paymentRequests.find((r) => r.id === transportRequest.id)) {\n return;\n }\n\n // Convert transport request to IncomingPaymentRequest\n const coinId = transportRequest.request.coinId;\n const registry = TokenRegistry.getInstance();\n const coinDef = registry.getDefinition(coinId);\n\n const request: IncomingPaymentRequest = {\n id: transportRequest.id,\n senderPubkey: transportRequest.senderTransportPubkey,\n senderNametag: transportRequest.senderNametag,\n amount: transportRequest.request.amount,\n coinId,\n symbol: coinDef?.symbol || coinId.slice(0, 8),\n message: transportRequest.request.message,\n recipientNametag: transportRequest.request.recipientNametag,\n requestId: transportRequest.request.requestId,\n timestamp: transportRequest.timestamp,\n status: 'pending',\n metadata: transportRequest.request.metadata,\n };\n\n // Add to list (newest first)\n this.paymentRequests.unshift(request);\n\n // Emit event\n this.deps?.emitEvent('payment_request:incoming', request);\n\n // Notify handlers\n for (const handler of this.paymentRequestHandlers) {\n try {\n handler(request);\n } catch (error) {\n this.log('Payment request handler error:', error);\n }\n }\n\n this.log(`Incoming payment request: ${request.id} for ${request.amount} ${request.symbol}`);\n }\n\n // ===========================================================================\n // Public API - Outgoing Payment Requests\n // ===========================================================================\n\n /**\n * Get outgoing payment requests\n * @param filter - Optional status filter\n */\n getOutgoingPaymentRequests(filter?: { status?: PaymentRequestStatus }): OutgoingPaymentRequest[] {\n const requests = Array.from(this.outgoingPaymentRequests.values());\n if (filter?.status) {\n return requests.filter((r) => r.status === filter.status);\n }\n return requests;\n }\n\n /**\n * Subscribe to payment request responses (for outgoing requests)\n * @param handler - Handler function for incoming responses\n * @returns Unsubscribe function\n */\n onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void {\n this.paymentRequestResponseHandlers.add(handler);\n return () => this.paymentRequestResponseHandlers.delete(handler);\n }\n\n /**\n * Wait for a response to a payment request\n * @param requestId - The outgoing request ID to wait for\n * @param timeoutMs - Timeout in milliseconds (default: 60000)\n * @returns Promise that resolves with the response or rejects on timeout\n */\n waitForPaymentResponse(requestId: string, timeoutMs: number = 60000): Promise<PaymentRequestResponse> {\n const outgoing = this.outgoingPaymentRequests.get(requestId);\n if (!outgoing) {\n return Promise.reject(new Error(`Outgoing payment request not found: ${requestId}`));\n }\n\n // If already has a response, return it\n if (outgoing.response) {\n return Promise.resolve(outgoing.response);\n }\n\n // Create a promise that resolves when response arrives or times out\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pendingResponseResolvers.delete(requestId);\n // Update status to expired\n const request = this.outgoingPaymentRequests.get(requestId);\n if (request && request.status === 'pending') {\n request.status = 'expired';\n }\n reject(new Error(`Payment request response timeout: ${requestId}`));\n }, timeoutMs);\n\n this.pendingResponseResolvers.set(requestId, { resolve, reject, timeout });\n });\n }\n\n /**\n * Cancel an active {@link waitForPaymentResponse} call.\n *\n * The pending promise is rejected with a `'Cancelled'` error.\n *\n * @param requestId - The outgoing request ID whose wait should be cancelled.\n */\n cancelWaitForPaymentResponse(requestId: string): void {\n const resolver = this.pendingResponseResolvers.get(requestId);\n if (resolver) {\n clearTimeout(resolver.timeout);\n resolver.reject(new Error('Cancelled'));\n this.pendingResponseResolvers.delete(requestId);\n }\n }\n\n /**\n * Remove an outgoing payment request and cancel any pending wait.\n *\n * @param requestId - ID of the outgoing request to remove.\n */\n removeOutgoingPaymentRequest(requestId: string): void {\n this.outgoingPaymentRequests.delete(requestId);\n this.cancelWaitForPaymentResponse(requestId);\n }\n\n /**\n * Remove all outgoing payment requests that are `'paid'`, `'rejected'`, or `'expired'`.\n */\n clearCompletedOutgoingPaymentRequests(): void {\n for (const [id, request] of this.outgoingPaymentRequests) {\n if (request.status === 'paid' || request.status === 'rejected' || request.status === 'expired') {\n this.outgoingPaymentRequests.delete(id);\n }\n }\n }\n\n private handlePaymentRequestResponse(transportResponse: TransportPaymentRequestResponse): void {\n // Find the outgoing request by matching requestId\n let outgoingRequest: OutgoingPaymentRequest | undefined;\n let outgoingRequestId: string | undefined;\n\n for (const [id, request] of this.outgoingPaymentRequests) {\n // Match by eventId or requestId from the response\n if (request.eventId === transportResponse.response.requestId ||\n request.id === transportResponse.response.requestId) {\n outgoingRequest = request;\n outgoingRequestId = id;\n break;\n }\n }\n\n // Convert transport response to PaymentRequestResponse\n const response: PaymentRequestResponse = {\n id: transportResponse.id,\n responderPubkey: transportResponse.responderTransportPubkey,\n requestId: transportResponse.response.requestId,\n responseType: transportResponse.response.responseType,\n message: transportResponse.response.message,\n transferId: transportResponse.response.transferId,\n timestamp: transportResponse.timestamp,\n };\n\n // Update outgoing request if found\n if (outgoingRequest && outgoingRequestId) {\n outgoingRequest.status = response.responseType === 'paid' ? 'paid' :\n response.responseType === 'accepted' ? 'accepted' :\n 'rejected';\n outgoingRequest.response = response;\n\n // Resolve pending promise if any\n const resolver = this.pendingResponseResolvers.get(outgoingRequestId);\n if (resolver) {\n clearTimeout(resolver.timeout);\n resolver.resolve(response);\n this.pendingResponseResolvers.delete(outgoingRequestId);\n }\n }\n\n // Emit event\n this.deps?.emitEvent('payment_request:response', response);\n\n // Notify handlers\n for (const handler of this.paymentRequestResponseHandlers) {\n try {\n handler(response);\n } catch (error) {\n this.log('Payment request response handler error:', error);\n }\n }\n\n this.log(`Received payment request response: ${response.id} type: ${response.responseType}`);\n }\n\n /**\n * Send a response to a payment request (used internally by accept/reject/pay methods)\n */\n private async sendPaymentRequestResponse(\n requestId: string,\n responseType: 'accepted' | 'rejected' | 'paid',\n transferId?: string\n ): Promise<void> {\n const request = this.paymentRequests.find((r) => r.id === requestId);\n if (!request) return;\n\n if (!this.deps?.transport.sendPaymentRequestResponse) {\n this.log('Transport does not support sendPaymentRequestResponse');\n return;\n }\n\n try {\n const payload: PaymentRequestResponsePayload = {\n requestId: request.requestId, // Original request ID from sender\n responseType,\n transferId,\n };\n\n await this.deps.transport.sendPaymentRequestResponse(request.senderPubkey, payload);\n this.log(`Sent payment request response: ${responseType} for ${requestId}`);\n } catch (error) {\n this.log('Failed to send payment request response:', error);\n }\n }\n\n // ===========================================================================\n // Public API - Receive\n // ===========================================================================\n\n /**\n * Fetch and process pending incoming transfers from the transport layer.\n *\n * Performs a one-shot query to fetch all pending events, processes them\n * through the existing pipeline, and resolves after all stored events\n * are handled. Useful for batch/CLI apps that need explicit receive.\n *\n * When `finalize` is true, polls resolveUnconfirmed() + load() until all\n * tokens are confirmed or the timeout expires. Otherwise calls\n * resolveUnconfirmed() once to submit pending commitments.\n *\n * @param options - Optional receive options including finalization control\n * @param callback - Optional callback invoked for each newly received transfer\n * @returns ReceiveResult with transfers and finalization metadata\n */\n async receive(\n options?: ReceiveOptions,\n callback?: (transfer: IncomingTransfer) => void,\n ): Promise<ReceiveResult> {\n this.ensureInitialized();\n\n if (!this.deps!.transport.fetchPendingEvents) {\n throw new Error('Transport provider does not support fetchPendingEvents');\n }\n\n const opts = options ?? {};\n\n // Phase 1: Fetch pending events\n // Snapshot token keys before fetch\n const tokensBefore = new Set(this.tokens.keys());\n\n // Fetch and process — events flow through handleIncomingTransfer() pipeline.\n // fetchPendingEvents() collects events until EOSE, then processes sequentially\n // with await. Event dedup in the transport layer prevents double-processing\n // with the persistent subscription.\n await this.deps!.transport.fetchPendingEvents();\n\n // Reload from storage to get a clean, consistent state.\n // Handlers save tokens during processing (with potentially different IDs for\n // V5 pending tokens vs finalized tokens). load() clears the in-memory map\n // and reloads from TXF + pending V5 storage, ensuring no duplicates.\n await this.load();\n\n // Identify newly added tokens\n const received: IncomingTransfer[] = [];\n for (const [tokenId, token] of this.tokens) {\n if (!tokensBefore.has(tokenId)) {\n const transfer: IncomingTransfer = {\n id: tokenId,\n senderPubkey: '',\n tokens: [token],\n receivedAt: Date.now(),\n };\n received.push(transfer);\n if (callback) callback(transfer);\n }\n }\n\n // Phase 2: Finalization\n const result: ReceiveResult = { transfers: received };\n\n if (opts.finalize) {\n const timeout = opts.timeout ?? 60_000;\n const pollInterval = opts.pollInterval ?? 2_000;\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n const resolution = await this.resolveUnconfirmed();\n result.finalization = resolution;\n if (opts.onProgress) opts.onProgress(resolution);\n\n // Check if any unconfirmed tokens remain\n const stillUnconfirmed = Array.from(this.tokens.values()).some(\n t => t.status === 'submitted' || t.status === 'pending'\n );\n if (!stillUnconfirmed) break;\n\n await new Promise(r => setTimeout(r, pollInterval));\n await this.load();\n }\n\n result.finalizationDurationMs = Date.now() - startTime;\n result.timedOut = Array.from(this.tokens.values()).some(\n t => t.status === 'submitted' || t.status === 'pending'\n );\n } else {\n // Non-finalize: submit commitments once (fire-and-forget style)\n result.finalization = await this.resolveUnconfirmed();\n }\n\n return result;\n }\n\n // ===========================================================================\n // Public API - Balance & Tokens\n // ===========================================================================\n\n /**\n * Set or update price provider\n */\n setPriceProvider(provider: PriceProvider): void {\n this.priceProvider = provider;\n }\n\n /**\n * Wait for all pending background operations (e.g., instant split change token creation).\n * Call this before process exit to ensure all tokens are saved.\n */\n async waitForPendingOperations(): Promise<void> {\n if (this.pendingBackgroundTasks.length > 0) {\n await Promise.allSettled(this.pendingBackgroundTasks);\n this.pendingBackgroundTasks = [];\n }\n }\n\n /**\n * Get total portfolio value in USD.\n * Returns null if PriceProvider is not configured.\n */\n async getFiatBalance(): Promise<number | null> {\n const assets = await this.getAssets();\n\n if (!this.priceProvider) {\n return null;\n }\n\n let total = 0;\n let hasAnyPrice = false;\n\n for (const asset of assets) {\n if (asset.fiatValueUsd != null) {\n total += asset.fiatValueUsd;\n hasAnyPrice = true;\n }\n }\n\n return hasAnyPrice ? total : null;\n }\n\n /**\n * Get token balances grouped by coin type.\n *\n * Returns an array of {@link Asset} objects, one per coin type held.\n * Each entry includes confirmed and unconfirmed breakdowns. Tokens with\n * status `'spent'`, `'invalid'`, or `'transferring'` are excluded.\n *\n * This is synchronous — no price data is included. Use {@link getAssets}\n * for the async version with fiat pricing.\n *\n * @param coinId - Optional coin ID to filter by (e.g. hex string). When omitted, all coin types are returned.\n * @returns Array of balance summaries (synchronous — no await needed).\n */\n getBalance(coinId?: string): Asset[] {\n return this.aggregateTokens(coinId);\n }\n\n /**\n * Get aggregated assets (tokens grouped by coinId) with price data.\n * Includes both confirmed and unconfirmed tokens with breakdown.\n */\n async getAssets(coinId?: string): Promise<Asset[]> {\n const rawAssets = this.aggregateTokens(coinId);\n\n // Fetch prices if provider is available\n if (!this.priceProvider || rawAssets.length === 0) {\n return rawAssets;\n }\n\n try {\n const registry = TokenRegistry.getInstance();\n const nameToCoins = new Map<string, string[]>(); // tokenName -> coinIds[]\n\n for (const asset of rawAssets) {\n const def = registry.getDefinition(asset.coinId);\n if (def?.name) {\n const existing = nameToCoins.get(def.name);\n if (existing) {\n existing.push(asset.coinId);\n } else {\n nameToCoins.set(def.name, [asset.coinId]);\n }\n }\n }\n\n if (nameToCoins.size > 0) {\n const tokenNames = Array.from(nameToCoins.keys());\n const prices = await this.priceProvider.getPrices(tokenNames);\n\n return rawAssets.map((raw) => {\n const def = registry.getDefinition(raw.coinId);\n const price = def?.name ? prices.get(def.name) : undefined;\n let fiatValueUsd: number | null = null;\n let fiatValueEur: number | null = null;\n\n if (price) {\n const humanAmount = Number(raw.totalAmount) / Math.pow(10, raw.decimals);\n fiatValueUsd = humanAmount * price.priceUsd;\n if (price.priceEur != null) {\n fiatValueEur = humanAmount * price.priceEur;\n }\n }\n\n return {\n ...raw,\n priceUsd: price?.priceUsd ?? null,\n priceEur: price?.priceEur ?? null,\n change24h: price?.change24h ?? null,\n fiatValueUsd,\n fiatValueEur,\n };\n });\n }\n } catch (error) {\n console.warn('[Payments] Failed to fetch prices, returning assets without price data:', error);\n }\n\n return rawAssets;\n }\n\n /**\n * Aggregate tokens by coinId with confirmed/unconfirmed breakdown.\n * Excludes tokens with status 'spent', 'invalid', or 'transferring'.\n */\n private aggregateTokens(coinId?: string): Asset[] {\n const assetsMap = new Map<string, {\n coinId: string;\n symbol: string;\n name: string;\n decimals: number;\n iconUrl?: string;\n confirmedAmount: bigint;\n unconfirmedAmount: bigint;\n confirmedTokenCount: number;\n unconfirmedTokenCount: number;\n }>();\n\n for (const token of this.tokens.values()) {\n // Skip spent, invalid, and transferring tokens\n if (token.status === 'spent' || token.status === 'invalid' || token.status === 'transferring') continue;\n if (coinId && token.coinId !== coinId) continue;\n\n const key = token.coinId;\n const amount = BigInt(token.amount);\n const isConfirmed = token.status === 'confirmed';\n const existing = assetsMap.get(key);\n\n if (existing) {\n if (isConfirmed) {\n existing.confirmedAmount += amount;\n existing.confirmedTokenCount++;\n } else {\n existing.unconfirmedAmount += amount;\n existing.unconfirmedTokenCount++;\n }\n } else {\n assetsMap.set(key, {\n coinId: token.coinId,\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n iconUrl: token.iconUrl,\n confirmedAmount: isConfirmed ? amount : 0n,\n unconfirmedAmount: isConfirmed ? 0n : amount,\n confirmedTokenCount: isConfirmed ? 1 : 0,\n unconfirmedTokenCount: isConfirmed ? 0 : 1,\n });\n }\n }\n\n return Array.from(assetsMap.values()).map((raw) => {\n const totalAmount = (raw.confirmedAmount + raw.unconfirmedAmount).toString();\n return {\n coinId: raw.coinId,\n symbol: raw.symbol,\n name: raw.name,\n decimals: raw.decimals,\n iconUrl: raw.iconUrl,\n totalAmount,\n tokenCount: raw.confirmedTokenCount + raw.unconfirmedTokenCount,\n confirmedAmount: raw.confirmedAmount.toString(),\n unconfirmedAmount: raw.unconfirmedAmount.toString(),\n confirmedTokenCount: raw.confirmedTokenCount,\n unconfirmedTokenCount: raw.unconfirmedTokenCount,\n priceUsd: null,\n priceEur: null,\n change24h: null,\n fiatValueUsd: null,\n fiatValueEur: null,\n };\n });\n }\n\n /**\n * Get all tokens, optionally filtered by coin type and/or status.\n *\n * @param filter - Optional filter criteria.\n * @param filter.coinId - Return only tokens of this coin type.\n * @param filter.status - Return only tokens with this status (e.g. `'submitted'` for unconfirmed).\n * @returns Array of matching {@link Token} objects (synchronous).\n */\n getTokens(filter?: { coinId?: string; status?: TokenStatus }): Token[] {\n let tokens = Array.from(this.tokens.values());\n\n if (filter?.coinId) {\n tokens = tokens.filter((t) => t.coinId === filter.coinId);\n }\n if (filter?.status) {\n tokens = tokens.filter((t) => t.status === filter.status);\n }\n\n return tokens;\n }\n\n /**\n * Get a single token by its local ID.\n *\n * @param id - The local UUID assigned when the token was added.\n * @returns The token, or `undefined` if not found.\n */\n getToken(id: string): Token | undefined {\n return this.tokens.get(id);\n }\n\n // ===========================================================================\n // Public API - Unconfirmed Token Resolution\n // ===========================================================================\n\n /**\n * Attempt to resolve unconfirmed (status `'submitted'`) tokens by acquiring\n * their missing aggregator proofs.\n *\n * Each unconfirmed V5 token progresses through stages:\n * `RECEIVED` → `MINT_SUBMITTED` → `MINT_PROVEN` → `TRANSFER_SUBMITTED` → `FINALIZED`\n *\n * Uses 500 ms quick-timeouts per proof check so the call returns quickly even\n * when proofs are not yet available. Tokens that exceed 50 failed attempts are\n * marked `'invalid'`.\n *\n * Automatically called (fire-and-forget) by {@link load}.\n *\n * @returns Summary with counts of resolved, still-pending, and failed tokens plus per-token details.\n */\n async resolveUnconfirmed(): Promise<UnconfirmedResolutionResult> {\n this.ensureInitialized();\n const result: UnconfirmedResolutionResult = {\n resolved: 0,\n stillPending: 0,\n failed: 0,\n details: [],\n };\n\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.() as RootTrustBase | undefined;\n if (!stClient || !trustBase) return result;\n\n const signingService = await this.createSigningService();\n\n for (const [tokenId, token] of this.tokens) {\n if (token.status !== 'submitted') continue;\n\n // Check for pending finalization metadata\n const pending = this.parsePendingFinalization(token.sdkData);\n if (!pending) {\n // Legacy commitment-only token (existing proof polling handles these)\n result.stillPending++;\n continue;\n }\n\n if (pending.type === 'v5_bundle') {\n const progress = await this.resolveV5Token(tokenId, token, pending, stClient, trustBase, signingService);\n result.details.push({ tokenId, stage: pending.stage, status: progress });\n if (progress === 'resolved') result.resolved++;\n else if (progress === 'failed') result.failed++;\n else result.stillPending++;\n }\n }\n\n if (result.resolved > 0 || result.failed > 0) {\n await this.save();\n }\n return result;\n }\n\n // ===========================================================================\n // Private - V5 Lazy Resolution Helpers\n // ===========================================================================\n\n /**\n * Process a single V5 token through its finalization stages with quick-timeout proof checks.\n */\n private async resolveV5Token(\n tokenId: string,\n token: Token,\n pending: PendingV5Finalization,\n stClient: StateTransitionClient,\n trustBase: RootTrustBase,\n signingService: SigningService\n ): Promise<'resolved' | 'pending' | 'failed'> {\n const bundle: InstantSplitBundleV5 = JSON.parse(pending.bundleJson);\n pending.attemptCount++;\n pending.lastAttemptAt = Date.now();\n\n try {\n // Stage: RECEIVED → MINT_SUBMITTED\n if (pending.stage === 'RECEIVED') {\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n const mintCommitment = await MintCommitment.create(mintData);\n const mintResponse = await stClient.submitMintCommitment(mintCommitment);\n if (mintResponse.status !== 'SUCCESS' && mintResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Mint submission failed: ${mintResponse.status}`);\n }\n pending.stage = 'MINT_SUBMITTED';\n this.updatePendingFinalization(token, pending);\n }\n\n // Stage: MINT_SUBMITTED → MINT_PROVEN\n if (pending.stage === 'MINT_SUBMITTED') {\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n const mintCommitment = await MintCommitment.create(mintData);\n const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);\n if (!proof) {\n this.updatePendingFinalization(token, pending);\n return 'pending';\n }\n pending.mintProofJson = JSON.stringify(proof);\n pending.stage = 'MINT_PROVEN';\n this.updatePendingFinalization(token, pending);\n }\n\n // Stage: MINT_PROVEN → TRANSFER_SUBMITTED\n if (pending.stage === 'MINT_PROVEN') {\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n const transferResponse = await stClient.submitTransferCommitment(transferCommitment);\n if (transferResponse.status !== 'SUCCESS' && transferResponse.status !== 'REQUEST_ID_EXISTS') {\n throw new Error(`Transfer submission failed: ${transferResponse.status}`);\n }\n pending.stage = 'TRANSFER_SUBMITTED';\n this.updatePendingFinalization(token, pending);\n }\n\n // Stage: TRANSFER_SUBMITTED → FINALIZED\n if (pending.stage === 'TRANSFER_SUBMITTED') {\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n const proof = await this.quickProofCheck(stClient, trustBase, transferCommitment);\n if (!proof) {\n this.updatePendingFinalization(token, pending);\n return 'pending';\n }\n\n // Finalize: reconstruct minted token, create recipient state, finalize\n const finalizedToken = await this.finalizeFromV5Bundle(bundle, pending, signingService, stClient, trustBase);\n\n // Replace token with confirmed version containing real SDK data\n const confirmedToken: Token = {\n id: token.id,\n coinId: token.coinId,\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n iconUrl: token.iconUrl,\n amount: token.amount,\n status: 'confirmed',\n createdAt: token.createdAt,\n updatedAt: Date.now(),\n sdkData: JSON.stringify(finalizedToken.toJSON()),\n };\n this.tokens.set(tokenId, confirmedToken);\n\n // Add to history\n await this.addToHistory({\n type: 'RECEIVED',\n amount: confirmedToken.amount,\n coinId: confirmedToken.coinId,\n symbol: confirmedToken.symbol || 'UNK',\n timestamp: Date.now(),\n senderPubkey: pending.senderPubkey,\n });\n\n this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);\n return 'resolved';\n }\n\n return 'pending';\n } catch (error) {\n console.error(`[Payments] resolveV5Token failed for ${tokenId.slice(0, 8)}:`, error);\n if (pending.attemptCount > 50) {\n token.status = 'invalid';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n return 'failed';\n }\n this.updatePendingFinalization(token, pending);\n return 'pending';\n }\n }\n\n /**\n * Non-blocking proof check with 500ms timeout.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private async quickProofCheck(\n stClient: StateTransitionClient,\n trustBase: RootTrustBase,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n commitment: any,\n timeoutMs: number = 500\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<any | null> {\n try {\n const proof = await Promise.race([\n waitInclusionProof(trustBase, stClient, commitment),\n new Promise<null>(resolve => setTimeout(() => resolve(null), timeoutMs)),\n ]);\n return proof;\n } catch {\n return null;\n }\n }\n\n /**\n * Perform V5 bundle finalization from stored bundle data and proofs.\n * Extracted from InstantSplitProcessor.processV5Bundle() steps 4-10.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private async finalizeFromV5Bundle(\n bundle: InstantSplitBundleV5,\n pending: PendingV5Finalization,\n signingService: SigningService,\n stClient: StateTransitionClient,\n trustBase: RootTrustBase\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<SdkToken<any>> {\n // Reconstruct minted token from bundle data\n const mintDataJson = JSON.parse(bundle.recipientMintData);\n const mintData = await MintTransactionData.fromJSON(mintDataJson);\n const mintCommitment = await MintCommitment.create(mintData);\n const mintProofJson = JSON.parse(pending.mintProofJson!);\n const mintProof = InclusionProof.fromJSON(mintProofJson);\n const mintTransaction = mintCommitment.toTransaction(mintProof);\n\n const tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);\n\n const tokenJson = {\n version: '2.0',\n state: senderMintedStateJson,\n genesis: mintTransaction.toJSON(),\n transactions: [],\n nametags: [],\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mintedToken = await SdkToken.fromJSON(tokenJson) as SdkToken<any>;\n\n // Create transfer transaction\n const transferCommitmentJson = JSON.parse(bundle.transferCommitment);\n const transferCommitment = await TransferCommitment.fromJSON(transferCommitmentJson);\n const transferProof = await waitInclusionProof(trustBase, stClient, transferCommitment);\n const transferTransaction = transferCommitment.toTransaction(transferProof);\n\n // Create recipient state\n const transferSalt = fromHex(bundle.transferSaltHex);\n const recipientPredicate = await UnmaskedPredicate.create(\n mintData.tokenId,\n tokenType,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const recipientState = new TokenState(recipientPredicate, null);\n\n // Handle nametag tokens for PROXY addresses\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let nametagTokens: SdkToken<any>[] = [];\n const recipientAddressStr = bundle.recipientAddressJson;\n\n if (recipientAddressStr.startsWith('PROXY://')) {\n // Try to get nametag token from bundle first\n if (bundle.nametagTokenJson) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nametagToken = await SdkToken.fromJSON(JSON.parse(bundle.nametagTokenJson)) as SdkToken<any>;\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address === recipientAddressStr) {\n nametagTokens = [nametagToken];\n }\n } catch {\n // Fall through to local nametag lookup\n }\n }\n\n // If not in bundle, try local nametag\n const localNametag = this.getNametag();\n if (nametagTokens.length === 0 && localNametag?.token) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nametagToken = await SdkToken.fromJSON(localNametag.token) as SdkToken<any>;\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address === recipientAddressStr) {\n nametagTokens = [nametagToken];\n }\n } catch {\n // No nametag available\n }\n }\n }\n\n // Finalize\n return stClient.finalizeTransaction(trustBase, mintedToken, recipientState, transferTransaction, nametagTokens);\n }\n\n /**\n * Parse pending finalization metadata from token's sdkData.\n */\n private parsePendingFinalization(sdkData: string | undefined): PendingV5Finalization | null {\n if (!sdkData) return null;\n try {\n const data = JSON.parse(sdkData);\n if (data._pendingFinalization && data._pendingFinalization.type === 'v5_bundle') {\n return data._pendingFinalization as PendingV5Finalization;\n }\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Update pending finalization metadata in token's sdkData.\n * Creates a new token object since sdkData is readonly.\n */\n private updatePendingFinalization(token: Token, pending: PendingV5Finalization): void {\n const updated: Token = {\n id: token.id,\n coinId: token.coinId,\n symbol: token.symbol,\n name: token.name,\n decimals: token.decimals,\n iconUrl: token.iconUrl,\n amount: token.amount,\n status: token.status,\n createdAt: token.createdAt,\n updatedAt: Date.now(),\n sdkData: JSON.stringify({ _pendingFinalization: pending }),\n };\n this.tokens.set(token.id, updated);\n }\n\n /**\n * Save pending V5 tokens to key-value storage.\n * These tokens can't be serialized to TXF format (no genesis/state),\n * so we persist them separately and restore on load().\n */\n private async savePendingV5Tokens(): Promise<void> {\n const pendingTokens: Token[] = [];\n for (const token of this.tokens.values()) {\n if (this.parsePendingFinalization(token.sdkData)) {\n pendingTokens.push(token);\n }\n }\n if (pendingTokens.length > 0) {\n await this.deps!.storage.set(\n STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,\n JSON.stringify(pendingTokens)\n );\n } else {\n // Clean up when no pending tokens remain\n await this.deps!.storage.set(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS, '');\n }\n }\n\n /**\n * Load pending V5 tokens from key-value storage and merge into tokens map.\n * Called during load() to restore tokens that TXF format can't represent.\n */\n private async loadPendingV5Tokens(): Promise<void> {\n const data = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);\n if (!data) return;\n\n try {\n const pendingTokens = JSON.parse(data) as Token[];\n for (const token of pendingTokens) {\n // Only restore if not already in the map (e.g., already resolved)\n if (!this.tokens.has(token.id)) {\n this.tokens.set(token.id, token);\n }\n }\n if (pendingTokens.length > 0) {\n this.log(`Restored ${pendingTokens.length} pending V5 token(s)`);\n }\n } catch {\n // Ignore corrupt data\n }\n }\n\n // ===========================================================================\n // Public API - Token Operations\n // ===========================================================================\n\n /**\n * Add a token to the wallet.\n *\n * Tokens are uniquely identified by a `(tokenId, stateHash)` composite key.\n * Duplicate detection:\n * - **Tombstoned** — rejected if the exact `(tokenId, stateHash)` pair has a tombstone.\n * - **Exact duplicate** — rejected if a token with the same composite key already exists.\n * - **State replacement** — if the same `tokenId` exists with a *different* `stateHash`,\n * the old state is archived and replaced with the incoming one.\n *\n * @param token - The token to add.\n * @param skipHistory - When `true`, do not create a `RECEIVED` transaction history entry (default `false`).\n * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.\n */\n async addToken(token: Token, skipHistory: boolean = false): Promise<boolean> {\n this.ensureInitialized();\n\n const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);\n const incomingStateHash = extractStateHashFromSdkData(token.sdkData);\n const incomingStateKey = incomingTokenId && incomingStateHash\n ? createTokenStateKey(incomingTokenId, incomingStateHash)\n : null;\n\n // Check tombstones - reject tokens with exact (tokenId, stateHash) match\n // This prevents spent tokens from being re-added via Nostr re-delivery\n // Tokens with the same tokenId but DIFFERENT stateHash are allowed (new state)\n if (incomingTokenId && incomingStateHash && this.isStateTombstoned(incomingTokenId, incomingStateHash)) {\n this.log(`Rejecting tombstoned token: ${incomingTokenId.slice(0, 8)}..._${incomingStateHash.slice(0, 8)}...`);\n return false;\n }\n\n // Check for exact duplicate (same tokenId AND same stateHash)\n if (incomingStateKey) {\n for (const [existingId, existing] of this.tokens) {\n if (isSameTokenState(existing, token)) {\n // Exact duplicate - same tokenId and same stateHash\n this.log(`Duplicate token state ignored: ${incomingTokenId?.slice(0, 8)}..._${incomingStateHash?.slice(0, 8)}...`);\n return false;\n }\n }\n }\n\n // Check for older states of the same token (same tokenId, different stateHash)\n // Replace older states with the new state\n for (const [existingId, existing] of this.tokens) {\n if (hasSameGenesisTokenId(existing, token)) {\n const existingStateHash = extractStateHashFromSdkData(existing.sdkData);\n\n // Skip if same state (already handled above)\n if (incomingStateHash && existingStateHash && incomingStateHash === existingStateHash) {\n continue;\n }\n\n // CASE 1: Existing token is spent/invalid - allow replacement\n if (existing.status === 'spent' || existing.status === 'invalid') {\n this.log(`Replacing spent/invalid token ${incomingTokenId?.slice(0, 8)}...`);\n this.tokens.delete(existingId);\n break;\n }\n\n // CASE 2: Different stateHash - this is a newer state of the token\n // Remove old state (it will be archived) and add new state\n if (incomingStateHash && existingStateHash && incomingStateHash !== existingStateHash) {\n this.log(`Token ${incomingTokenId?.slice(0, 8)}... state updated: ${existingStateHash.slice(0, 8)}... -> ${incomingStateHash.slice(0, 8)}...`);\n // Archive old state before removing\n await this.archiveToken(existing);\n this.tokens.delete(existingId);\n break;\n }\n\n // CASE 3: No state hashes available - use .id as heuristic\n if (!incomingStateHash || !existingStateHash) {\n if (existingId !== token.id) {\n this.log(`Token ${incomingTokenId?.slice(0, 8)}... .id changed, replacing`);\n await this.archiveToken(existing);\n this.tokens.delete(existingId);\n break;\n }\n }\n }\n }\n\n // Add the new token state\n this.tokens.set(token.id, token);\n\n // Archive the token (for recovery purposes)\n await this.archiveToken(token);\n\n // Add to transaction history\n if (!skipHistory && token.coinId && token.amount) {\n await this.addToHistory({\n type: 'RECEIVED',\n amount: token.amount,\n coinId: token.coinId,\n symbol: token.symbol || 'UNK',\n timestamp: token.createdAt || Date.now(),\n });\n }\n\n await this.save();\n\n this.log(`Added token ${token.id}, total: ${this.tokens.size}`);\n return true;\n }\n\n\n\n /**\n * Update an existing token or add it if not found.\n *\n * Looks up the token by genesis `tokenId` (from `sdkData`) first, then by\n * `token.id`. If no match is found, falls back to {@link addToken}.\n *\n * @param token - The token with updated data. Must include a valid `id`.\n */\n async updateToken(token: Token): Promise<void> {\n this.ensureInitialized();\n\n const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);\n let found = false;\n\n // Find by genesis tokenId first\n for (const [id, existing] of this.tokens) {\n const existingTokenId = extractTokenIdFromSdkData(existing.sdkData);\n if ((existingTokenId && incomingTokenId && existingTokenId === incomingTokenId) ||\n existing.id === token.id) {\n this.tokens.delete(id);\n this.tokens.set(token.id, token);\n found = true;\n break;\n }\n }\n\n if (!found) {\n await this.addToken(token, true);\n return;\n }\n\n // Archive the updated token\n await this.archiveToken(token);\n\n await this.save();\n this.log(`Updated token ${token.id}`);\n }\n\n /**\n * Remove a token from the wallet.\n *\n * The token is archived first, then a tombstone `(tokenId, stateHash)` is\n * created to prevent re-addition via Nostr re-delivery. A `SENT` history\n * entry is created unless `skipHistory` is `true`.\n *\n * @param tokenId - Local UUID of the token to remove.\n * @param recipientNametag - Optional nametag of the transfer recipient (for history).\n * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).\n */\n async removeToken(tokenId: string, recipientNametag?: string, skipHistory: boolean = false): Promise<void> {\n this.ensureInitialized();\n\n const token = this.tokens.get(tokenId);\n if (!token) return;\n\n // Archive before removing\n await this.archiveToken(token);\n\n // Create tombstone with exact (tokenId, stateHash) - requires both\n const tombstone = createTombstoneFromToken(token);\n if (tombstone) {\n const alreadyTombstoned = this.tombstones.some(\n t => t.tokenId === tombstone.tokenId && t.stateHash === tombstone.stateHash\n );\n if (!alreadyTombstoned) {\n this.tombstones.push(tombstone);\n this.log(`Created tombstone for ${tombstone.tokenId.slice(0, 8)}..._${tombstone.stateHash.slice(0, 8)}...`);\n }\n } else {\n // No valid tombstone could be created (missing tokenId or stateHash)\n // Token will still be removed but may be re-synced later\n this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);\n }\n\n // Remove from active tokens\n this.tokens.delete(tokenId);\n\n // Add to transaction history\n if (!skipHistory && token.coinId && token.amount) {\n await this.addToHistory({\n type: 'SENT',\n amount: token.amount,\n coinId: token.coinId,\n symbol: token.symbol || 'UNK',\n timestamp: Date.now(),\n recipientNametag,\n });\n }\n\n await this.save();\n }\n\n\n // ===========================================================================\n // Public API - Tombstones\n // ===========================================================================\n\n /**\n * Get all tombstone entries.\n *\n * Each tombstone is keyed by `(tokenId, stateHash)` and prevents a spent\n * token state from being re-added (e.g. via Nostr re-delivery).\n *\n * @returns A shallow copy of the tombstone array.\n */\n getTombstones(): TombstoneEntry[] {\n return [...this.tombstones];\n }\n\n /**\n * Check whether a specific `(tokenId, stateHash)` combination is tombstoned.\n *\n * @param tokenId - The genesis token ID.\n * @param stateHash - The state hash of the token version to check.\n * @returns `true` if the exact combination has been tombstoned.\n */\n isStateTombstoned(tokenId: string, stateHash: string): boolean {\n return this.tombstones.some(\n t => t.tokenId === tokenId && t.stateHash === stateHash\n );\n }\n\n /**\n * Merge tombstones received from a remote sync source.\n *\n * Any local token whose `(tokenId, stateHash)` matches a remote tombstone is\n * removed. The remote tombstones are then added to the local set (union merge).\n *\n * @param remoteTombstones - Tombstone entries from the remote source.\n * @returns Number of local tokens that were removed.\n */\n async mergeTombstones(remoteTombstones: TombstoneEntry[]): Promise<number> {\n this.ensureInitialized();\n\n let removedCount = 0;\n const tombstoneKeys = new Set(\n remoteTombstones.map(t => `${t.tokenId}:${t.stateHash}`)\n );\n\n // Find tokens to remove\n const tokensToRemove: Token[] = [];\n for (const token of this.tokens.values()) {\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n const currentStateHash = extractStateHashFromSdkData(token.sdkData);\n\n const key = `${sdkTokenId}:${currentStateHash}`;\n if (tombstoneKeys.has(key)) {\n tokensToRemove.push(token);\n }\n }\n\n for (const token of tokensToRemove) {\n this.tokens.delete(token.id);\n this.log(`Removed tombstoned token ${token.id.slice(0, 8)}...`);\n removedCount++;\n }\n\n // Merge tombstones (union)\n for (const remoteTombstone of remoteTombstones) {\n const alreadyExists = this.tombstones.some(\n t => t.tokenId === remoteTombstone.tokenId && t.stateHash === remoteTombstone.stateHash\n );\n if (!alreadyExists) {\n this.tombstones.push(remoteTombstone);\n }\n }\n\n if (removedCount > 0) {\n await this.save();\n }\n\n return removedCount;\n }\n\n /**\n * Remove tombstones older than `maxAge` and cap the list at 100 entries.\n *\n * @param maxAge - Maximum age in milliseconds (default: 30 days).\n */\n async pruneTombstones(maxAge?: number): Promise<void> {\n const originalCount = this.tombstones.length;\n this.tombstones = pruneTombstonesByAge(this.tombstones, maxAge);\n\n if (this.tombstones.length < originalCount) {\n await this.save();\n this.log(`Pruned tombstones from ${originalCount} to ${this.tombstones.length}`);\n }\n }\n\n // ===========================================================================\n // Public API - Archives\n // ===========================================================================\n\n /**\n * Get all archived (spent/superseded) tokens in TXF format.\n *\n * Archived tokens are kept for recovery and sync purposes. The map key is\n * the genesis token ID.\n *\n * @returns A shallow copy of the archived token map.\n */\n getArchivedTokens(): Map<string, TxfToken> {\n return new Map(this.archivedTokens);\n }\n\n /**\n * Get the best (most committed transactions) archived version of a token.\n *\n * Searches both archived and forked token maps and returns the version with\n * the highest number of committed transactions.\n *\n * @param tokenId - The genesis token ID to look up.\n * @returns The best TXF token version, or `null` if not found.\n */\n getBestArchivedVersion(tokenId: string): TxfToken | null {\n return findBestTokenVersion(tokenId, this.archivedTokens, this.forkedTokens);\n }\n\n /**\n * Merge archived tokens from a remote sync source.\n *\n * For each remote token:\n * - If missing locally, it is added.\n * - If the remote version is an incremental update of the local, it replaces it.\n * - If the histories diverge (fork), the remote version is stored via {@link storeForkedToken}.\n *\n * @param remoteArchived - Map of genesis token ID → TXF token from remote.\n * @returns Number of tokens that were updated or added locally.\n */\n async mergeArchivedTokens(remoteArchived: Map<string, TxfToken>): Promise<number> {\n let mergedCount = 0;\n\n for (const [tokenId, remoteTxf] of remoteArchived) {\n const existingArchive = this.archivedTokens.get(tokenId);\n\n if (!existingArchive) {\n this.archivedTokens.set(tokenId, remoteTxf);\n mergedCount++;\n } else if (isIncrementalUpdate(existingArchive, remoteTxf)) {\n this.archivedTokens.set(tokenId, remoteTxf);\n mergedCount++;\n } else if (!isIncrementalUpdate(remoteTxf, existingArchive)) {\n // It's a fork\n const stateHash = getCurrentStateHash(remoteTxf) || '';\n await this.storeForkedToken(tokenId, stateHash, remoteTxf);\n }\n }\n\n if (mergedCount > 0) {\n await this.save();\n }\n\n return mergedCount;\n }\n\n /**\n * Prune archived tokens to keep at most `maxCount` entries.\n *\n * Oldest entries (by insertion order) are removed first.\n *\n * @param maxCount - Maximum number of archived tokens to retain (default: 100).\n */\n async pruneArchivedTokens(maxCount: number = 100): Promise<void> {\n if (this.archivedTokens.size <= maxCount) return;\n\n const originalCount = this.archivedTokens.size;\n this.archivedTokens = pruneMapByCount(this.archivedTokens, maxCount);\n\n await this.save();\n this.log(`Pruned archived tokens from ${originalCount} to ${this.archivedTokens.size}`);\n }\n\n // ===========================================================================\n // Public API - Forked Tokens\n // ===========================================================================\n\n /**\n * Get all forked token versions.\n *\n * Forked tokens represent alternative histories detected during sync.\n * The map key is `{tokenId}_{stateHash}`.\n *\n * @returns A shallow copy of the forked tokens map.\n */\n getForkedTokens(): Map<string, TxfToken> {\n return new Map(this.forkedTokens);\n }\n\n /**\n * Store a forked token version (alternative history).\n *\n * No-op if the exact `(tokenId, stateHash)` key already exists.\n *\n * @param tokenId - Genesis token ID.\n * @param stateHash - State hash of this forked version.\n * @param txfToken - The TXF token data to store.\n */\n async storeForkedToken(tokenId: string, stateHash: string, txfToken: TxfToken): Promise<void> {\n const key = `${tokenId}_${stateHash}`;\n if (this.forkedTokens.has(key)) return;\n\n this.forkedTokens.set(key, txfToken);\n this.log(`Stored forked token ${tokenId.slice(0, 8)}... state ${stateHash.slice(0, 12)}...`);\n await this.save();\n }\n\n /**\n * Merge forked tokens from a remote sync source. Only new keys are added.\n *\n * @param remoteForked - Map of `{tokenId}_{stateHash}` → TXF token from remote.\n * @returns Number of new forked tokens added.\n */\n async mergeForkedTokens(remoteForked: Map<string, TxfToken>): Promise<number> {\n let addedCount = 0;\n\n for (const [key, remoteTxf] of remoteForked) {\n if (!this.forkedTokens.has(key)) {\n this.forkedTokens.set(key, remoteTxf);\n addedCount++;\n }\n }\n\n if (addedCount > 0) {\n await this.save();\n }\n\n return addedCount;\n }\n\n /**\n * Prune forked tokens to keep at most `maxCount` entries.\n *\n * @param maxCount - Maximum number of forked tokens to retain (default: 50).\n */\n async pruneForkedTokens(maxCount: number = 50): Promise<void> {\n if (this.forkedTokens.size <= maxCount) return;\n\n const originalCount = this.forkedTokens.size;\n this.forkedTokens = pruneMapByCount(this.forkedTokens, maxCount);\n\n await this.save();\n this.log(`Pruned forked tokens from ${originalCount} to ${this.forkedTokens.size}`);\n }\n\n // ===========================================================================\n // Public API - Transaction History\n // ===========================================================================\n\n /**\n * Get the transaction history sorted newest-first.\n *\n * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.\n */\n getHistory(): TransactionHistoryEntry[] {\n return [...this.transactionHistory].sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Append an entry to the transaction history.\n *\n * A unique `id` is auto-generated. The entry is immediately persisted to storage.\n *\n * @param entry - History entry fields (without `id`).\n */\n async addToHistory(entry: Omit<TransactionHistoryEntry, 'id'>): Promise<void> {\n this.ensureInitialized();\n\n const historyEntry: TransactionHistoryEntry = {\n id: crypto.randomUUID(),\n ...entry,\n };\n this.transactionHistory.push(historyEntry);\n\n await this.deps!.storage.set(\n STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,\n JSON.stringify(this.transactionHistory)\n );\n }\n\n // ===========================================================================\n // Public API - Nametag\n // ===========================================================================\n\n /**\n * Set the nametag data for the current identity.\n *\n * Persists to both key-value storage and file storage (lottery compatibility).\n *\n * @param nametag - The nametag data including minted token JSON.\n */\n async setNametag(nametag: NametagData): Promise<void> {\n this.ensureInitialized();\n const idx = this.nametags.findIndex(n => n.name === nametag.name);\n if (idx >= 0) {\n this.nametags[idx] = nametag;\n } else {\n this.nametags.push(nametag);\n }\n await this.save();\n this.log(`Nametag set: ${nametag.name}`);\n }\n\n /**\n * Get the current (first) nametag data.\n *\n * @returns The nametag data, or `null` if no nametag is set.\n */\n getNametag(): NametagData | null {\n return this.nametags[0] ?? null;\n }\n\n /**\n * Get all nametag data entries.\n *\n * @returns A copy of the nametags array.\n */\n getNametags(): NametagData[] {\n return [...this.nametags];\n }\n\n /**\n * Check whether a nametag is currently set.\n *\n * @returns `true` if nametag data is present.\n */\n hasNametag(): boolean {\n return this.nametags.length > 0;\n }\n\n /**\n * Remove all nametag data from memory and storage.\n */\n async clearNametag(): Promise<void> {\n this.ensureInitialized();\n this.nametags = [];\n await this.save();\n }\n\n /**\n * Mint a nametag token on-chain (like Sphere wallet and lottery)\n * This creates the nametag token required for receiving tokens via PROXY addresses\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @returns MintNametagResult with success status and token if successful\n */\n async mintNametag(nametag: string): Promise<MintNametagResult> {\n this.ensureInitialized();\n\n // Get state transition client and trust base\n const stClient = this.deps!.oracle.getStateTransitionClient?.();\n if (!stClient) {\n return {\n success: false,\n error: 'State transition client not available. Oracle provider must implement getStateTransitionClient()',\n };\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!trustBase) {\n return {\n success: false,\n error: 'Trust base not available. Oracle provider must implement getTrustBase()',\n };\n }\n\n try {\n // Create signing service\n const signingService = await this.createSigningService();\n\n // Create owner address using UnmaskedPredicateReference (same pattern as TokenSplitExecutor)\n const { UnmaskedPredicateReference } = await import('@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference');\n const { TokenType } = await import('@unicitylabs/state-transition-sdk/lib/token/TokenType');\n\n // Use a dummy token type for address creation (like Sphere wallet does)\n const UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n const tokenType = new TokenType(Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex'));\n\n const addressRef = await UnmaskedPredicateReference.create(\n tokenType,\n signingService.algorithm,\n signingService.publicKey,\n HashAlgorithm.SHA256\n );\n const ownerAddress = await addressRef.toAddress();\n\n // Create NametagMinter\n const minter = new NametagMinter({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n debug: this.moduleConfig.debug,\n });\n\n // Mint the nametag\n const result = await minter.mintNametag(nametag, ownerAddress);\n\n if (result.success && result.nametagData) {\n // Save the nametag data\n await this.setNametag(result.nametagData);\n this.log(`Nametag minted and saved: ${result.nametagData.name}`);\n\n // Emit event (use existing nametag:registered event type)\n this.deps!.emitEvent('nametag:registered', {\n nametag: result.nametagData.name,\n addressIndex: 0, // Primary address\n });\n }\n\n return result;\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log('mintNametag failed:', errorMsg);\n return {\n success: false,\n error: errorMsg,\n };\n }\n }\n\n /**\n * Check if a nametag is available for minting\n * @param nametag - The nametag to check (e.g., \"alice\" or \"@alice\")\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n this.ensureInitialized();\n\n const stClient = this.deps!.oracle.getStateTransitionClient?.();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n\n if (!stClient || !trustBase) {\n return false;\n }\n\n try {\n const signingService = await this.createSigningService();\n const minter = new NametagMinter({\n stateTransitionClient: stClient,\n trustBase,\n signingService,\n });\n\n return await minter.isNametagAvailable(nametag);\n } catch {\n return false;\n }\n }\n\n // ===========================================================================\n // Public API - Sync & Validate\n // ===========================================================================\n\n /**\n * Sync local token state with all configured token storage providers (IPFS, file, etc.).\n *\n * For each provider, the local data is packaged into TXF storage format, sent\n * to the provider's `sync()` method, and the merged result is applied locally.\n * Emits `sync:started`, `sync:completed`, and `sync:error` events.\n *\n * @returns Summary with counts of tokens added and removed during sync.\n */\n async sync(): Promise<{ added: number; removed: number }> {\n this.ensureInitialized();\n\n // Sync coalescing: if a sync is already in progress, return its promise.\n // This prevents race conditions when addTokenStorageProvider() fires a\n // fire-and-forget sync and the caller also syncs immediately after.\n if (this._syncInProgress) {\n return this._syncInProgress;\n }\n\n this._syncInProgress = this._doSync();\n try {\n return await this._syncInProgress;\n } finally {\n this._syncInProgress = null;\n }\n }\n\n private async _doSync(): Promise<{ added: number; removed: number }> {\n this.deps!.emitEvent('sync:started', { source: 'payments' });\n\n try {\n // Get all token storage providers\n const providers = this.getTokenStorageProviders();\n\n if (providers.size === 0) {\n // No providers - just save locally\n await this.save();\n this.deps!.emitEvent('sync:completed', {\n source: 'payments',\n count: this.tokens.size,\n });\n return { added: 0, removed: 0 };\n }\n\n // Create local data once\n const localData = await this.createStorageData();\n\n let totalAdded = 0;\n let totalRemoved = 0;\n\n // Sync with each provider\n for (const [providerId, provider] of providers) {\n try {\n const result = await provider.sync(localData);\n\n if (result.success && result.merged) {\n // Apply merged data from each provider\n this.loadFromStorageData(result.merged);\n totalAdded += result.added;\n totalRemoved += result.removed;\n }\n\n this.deps!.emitEvent('sync:provider', {\n providerId,\n success: result.success,\n added: result.added,\n removed: result.removed,\n });\n } catch (providerError) {\n // Log error but continue with other providers\n console.warn(`[PaymentsModule] Sync failed for provider ${providerId}:`, providerError);\n this.deps!.emitEvent('sync:provider', {\n providerId,\n success: false,\n error: providerError instanceof Error ? providerError.message : String(providerError),\n });\n }\n }\n\n // Persist merged state to primary storage so it survives process restarts\n if (totalAdded > 0 || totalRemoved > 0) {\n await this.save();\n }\n\n this.deps!.emitEvent('sync:completed', {\n source: 'payments',\n count: this.tokens.size,\n });\n\n return { added: totalAdded, removed: totalRemoved };\n } catch (error) {\n this.deps!.emitEvent('sync:error', {\n source: 'payments',\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n // ===========================================================================\n // Storage Event Subscription (Push-Based Sync)\n // ===========================================================================\n\n /**\n * Subscribe to 'storage:remote-updated' events from all token storage providers.\n * When a provider emits this event, a debounced sync is triggered.\n */\n private subscribeToStorageEvents(): void {\n // Clean up existing subscriptions\n this.unsubscribeStorageEvents();\n\n const providers = this.getTokenStorageProviders();\n for (const [providerId, provider] of providers) {\n if (provider.onEvent) {\n const unsub = provider.onEvent((event) => {\n if (event.type === 'storage:remote-updated') {\n this.log('Remote update detected from provider', providerId, event.data);\n this.debouncedSyncFromRemoteUpdate(providerId, event.data);\n }\n });\n this.storageEventUnsubscribers.push(unsub);\n }\n }\n }\n\n /**\n * Unsubscribe from all storage provider events and clear debounce timer.\n */\n private unsubscribeStorageEvents(): void {\n for (const unsub of this.storageEventUnsubscribers) {\n unsub();\n }\n this.storageEventUnsubscribers = [];\n\n if (this.syncDebounceTimer) {\n clearTimeout(this.syncDebounceTimer);\n this.syncDebounceTimer = null;\n }\n }\n\n /**\n * Debounced sync triggered by a storage:remote-updated event.\n * Waits 500ms to batch rapid updates, then performs sync.\n */\n private debouncedSyncFromRemoteUpdate(providerId: string, eventData: unknown): void {\n if (this.syncDebounceTimer) {\n clearTimeout(this.syncDebounceTimer);\n }\n\n this.syncDebounceTimer = setTimeout(() => {\n this.syncDebounceTimer = null;\n this.sync()\n .then((result) => {\n const data = eventData as { name?: string; sequence?: number; cid?: string } | undefined;\n this.deps?.emitEvent('sync:remote-update', {\n providerId,\n name: data?.name ?? '',\n sequence: data?.sequence ?? 0,\n cid: data?.cid ?? '',\n added: result.added,\n removed: result.removed,\n });\n })\n .catch((err) => {\n this.log('Auto-sync from remote update failed:', err);\n });\n }, PaymentsModule.SYNC_DEBOUNCE_MS);\n }\n\n /**\n * Get all active token storage providers\n */\n private getTokenStorageProviders(): Map<string, TokenStorageProvider<TxfStorageDataBase>> {\n // Prefer new multi-provider map\n if (this.deps!.tokenStorageProviders && this.deps!.tokenStorageProviders.size > 0) {\n return this.deps!.tokenStorageProviders;\n }\n\n // Fallback to deprecated single provider\n if (this.deps!.tokenStorage) {\n const map = new Map<string, TokenStorageProvider<TxfStorageDataBase>>();\n map.set(this.deps!.tokenStorage.id, this.deps!.tokenStorage);\n return map;\n }\n\n return new Map();\n }\n\n /**\n * Replace the set of token storage providers at runtime.\n *\n * Use when providers are added or removed dynamically (e.g. IPFS node started).\n *\n * @param providers - New map of provider ID → TokenStorageProvider.\n */\n updateTokenStorageProviders(providers: Map<string, TokenStorageProvider<TxfStorageDataBase>>): void {\n if (this.deps) {\n this.deps.tokenStorageProviders = providers;\n // Re-subscribe to storage events for new providers\n this.subscribeToStorageEvents();\n }\n }\n\n /**\n * Validate all tokens against the aggregator (oracle provider).\n *\n * Tokens that fail validation or are detected as spent are marked `'invalid'`.\n *\n * @returns Object with arrays of valid and invalid tokens.\n */\n async validate(): Promise<{ valid: Token[]; invalid: Token[] }> {\n this.ensureInitialized();\n\n const valid: Token[] = [];\n const invalid: Token[] = [];\n\n for (const token of this.tokens.values()) {\n const result = await this.deps!.oracle.validateToken(token.sdkData);\n\n if (result.valid && !result.spent) {\n valid.push(token);\n } else {\n token.status = 'invalid';\n invalid.push(token);\n }\n }\n\n if (invalid.length > 0) {\n await this.save();\n }\n\n return { valid, invalid };\n }\n\n /**\n * Get all in-progress (pending) outgoing transfers.\n *\n * @returns Array of {@link TransferResult} objects for transfers that have not yet completed.\n */\n getPendingTransfers(): TransferResult[] {\n return Array.from(this.pendingTransfers.values());\n }\n\n // ===========================================================================\n // Private: Transfer Operations\n // ===========================================================================\n\n /**\n * Detect if a string is an L3 address (not a nametag)\n * Returns true for: hex pubkeys (64+ chars), PROXY:, DIRECT: prefixed addresses\n */\n /**\n * Resolve recipient to transport pubkey for messaging.\n * Uses pre-resolved PeerInfo if available, otherwise resolves via transport.\n */\n private resolveTransportPubkey(recipient: string, peerInfo?: PeerInfo | null): string {\n // If we have PeerInfo, use it\n if (peerInfo?.transportPubkey) {\n return peerInfo.transportPubkey;\n }\n\n // Hex pubkey (64+ hex chars) — use as transport pubkey directly\n if (recipient.length >= 64 && /^[0-9a-fA-F]+$/.test(recipient)) {\n // 66-char with 02/03 prefix — strip to 32-byte x-only\n if (recipient.length === 66 && (recipient.startsWith('02') || recipient.startsWith('03'))) {\n return recipient.slice(2);\n }\n return recipient;\n }\n\n throw new Error(\n `Cannot resolve transport pubkey for \"${recipient}\". ` +\n `No binding event found. The recipient must publish their identity first.`\n );\n }\n\n /**\n * Create SDK TransferCommitment for a token transfer\n */\n private async createSdkCommitment(\n token: Token,\n recipientAddress: IAddress,\n signingService: SigningService\n ): Promise<TransferCommitment> {\n // Parse SDK token from stored data\n const tokenData = token.sdkData\n ? (typeof token.sdkData === 'string' ? JSON.parse(token.sdkData) : token.sdkData)\n : token;\n\n const sdkToken = await SdkToken.fromJSON(tokenData);\n\n // Generate random salt\n const salt = crypto.getRandomValues(new Uint8Array(32));\n\n // Create transfer commitment\n const commitment = await TransferCommitment.create(\n sdkToken,\n recipientAddress,\n salt,\n null, // recipientData\n null, // recipientDataHash\n signingService\n );\n\n return commitment;\n }\n\n /**\n * Create SigningService from identity private key\n */\n private async createSigningService(): Promise<SigningService> {\n const privateKeyHex = this.deps!.identity.privateKey;\n const privateKeyBytes = new Uint8Array(\n privateKeyHex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n );\n return SigningService.createFromSecret(privateKeyBytes);\n }\n\n /**\n * Create DirectAddress from a public key using UnmaskedPredicateReference\n */\n private async createDirectAddressFromPubkey(pubkeyHex: string): Promise<IAddress> {\n const { UnmaskedPredicateReference } = await import('@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference');\n const { TokenType } = await import('@unicitylabs/state-transition-sdk/lib/token/TokenType');\n\n // Same token type used for address creation throughout the SDK\n const UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n const tokenType = new TokenType(Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex'));\n\n // Convert hex pubkey to bytes\n const pubkeyBytes = new Uint8Array(\n pubkeyHex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))\n );\n\n // Create predicate reference with secp256k1 algorithm\n const addressRef = await UnmaskedPredicateReference.create(\n tokenType,\n 'secp256k1',\n pubkeyBytes,\n HashAlgorithm.SHA256\n );\n\n return addressRef.toAddress();\n }\n\n /**\n * Resolve recipient to IAddress for L3 transfers.\n * Uses pre-resolved PeerInfo when available to avoid redundant network queries.\n */\n private async resolveRecipientAddress(\n recipient: string,\n addressMode: 'auto' | 'direct' | 'proxy' = 'auto',\n peerInfo?: PeerInfo | null,\n ): Promise<IAddress> {\n const { AddressFactory } = await import('@unicitylabs/state-transition-sdk/lib/address/AddressFactory');\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n\n // PROXY: or DIRECT: prefixed — parse directly (explicit address overrides mode)\n if (recipient.startsWith('PROXY:') || recipient.startsWith('DIRECT:')) {\n return AddressFactory.createAddress(recipient);\n }\n\n // 66-char hex (33-byte compressed pubkey) — create DirectAddress\n if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {\n this.log(`Creating DirectAddress from 33-byte compressed pubkey`);\n return this.createDirectAddressFromPubkey(recipient);\n }\n\n // For nametag-based recipients, use PeerInfo (pre-resolved or resolve now)\n const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;\n if (!info) {\n throw new Error(\n `Recipient \"${recipient}\" not found. ` +\n `Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey.`\n );\n }\n\n // Determine nametag for PROXY address derivation\n const nametag = recipient.startsWith('@') ? recipient.slice(1)\n : info.nametag || recipient;\n\n // Force PROXY mode\n if (addressMode === 'proxy') {\n console.log(`[Payments] Using PROXY address for \"${nametag}\" (forced)`);\n return ProxyAddress.fromNameTag(nametag);\n }\n\n // Force DIRECT mode\n if (addressMode === 'direct') {\n if (!info.directAddress) {\n throw new Error(`\"${nametag}\" has no DirectAddress stored. It may be a legacy registration.`);\n }\n console.log(`[Payments] Using DirectAddress for \"${nametag}\" (forced): ${info.directAddress.slice(0, 30)}...`);\n return AddressFactory.createAddress(info.directAddress);\n }\n\n // AUTO mode: prefer directAddress, fallback to PROXY for legacy\n if (info.directAddress) {\n this.log(`Using DirectAddress for \"${nametag}\": ${info.directAddress.slice(0, 30)}...`);\n return AddressFactory.createAddress(info.directAddress);\n }\n\n this.log(`Using PROXY address for legacy nametag \"${nametag}\"`);\n return ProxyAddress.fromNameTag(nametag);\n }\n\n /**\n * Handle NOSTR-FIRST commitment-only transfer (recipient side)\n * This is called when receiving a transfer with only commitmentData and no proof yet.\n * We create the token as 'submitted', submit commitment (idempotent), and poll for proof.\n */\n private async handleCommitmentOnlyTransfer(\n transfer: IncomingTokenTransfer,\n payload: Record<string, unknown>\n ): Promise<void> {\n try {\n const sourceTokenInput = typeof payload.sourceToken === 'string'\n ? JSON.parse(payload.sourceToken as string)\n : payload.sourceToken;\n const commitmentInput = typeof payload.commitmentData === 'string'\n ? JSON.parse(payload.commitmentData as string)\n : payload.commitmentData;\n\n if (!sourceTokenInput || !commitmentInput) {\n console.warn('[Payments] Invalid NOSTR-FIRST transfer format');\n return;\n }\n\n // Parse source token info\n const tokenInfo = await parseTokenInfo(sourceTokenInput);\n\n // Create token with 'submitted' status (unconfirmed until proof received)\n const token: Token = {\n id: tokenInfo.tokenId ?? crypto.randomUUID(),\n coinId: tokenInfo.coinId,\n symbol: tokenInfo.symbol,\n name: tokenInfo.name,\n decimals: tokenInfo.decimals,\n iconUrl: tokenInfo.iconUrl,\n amount: tokenInfo.amount,\n status: 'submitted', // NOSTR-FIRST: unconfirmed until proof\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: typeof sourceTokenInput === 'string'\n ? sourceTokenInput\n : JSON.stringify(sourceTokenInput),\n };\n\n // Check tombstones - reject tokens with exact (tokenId, stateHash) match\n // This prevents spent tokens from being re-added via Nostr re-delivery\n // Tokens with the same tokenId but DIFFERENT stateHash are allowed (new state)\n const nostrTokenId = extractTokenIdFromSdkData(token.sdkData);\n const nostrStateHash = extractStateHashFromSdkData(token.sdkData);\n if (nostrTokenId && nostrStateHash && this.isStateTombstoned(nostrTokenId, nostrStateHash)) {\n this.log(`NOSTR-FIRST: Rejecting tombstoned token ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`);\n return;\n }\n\n // Add token as unconfirmed\n this.tokens.set(token.id, token);\n await this.save();\n this.log(`NOSTR-FIRST: Token ${token.id.slice(0, 8)}... added as submitted (unconfirmed)`);\n\n // Emit event for incoming transfer (even though unconfirmed)\n const incomingTransfer: IncomingTransfer = {\n id: transfer.id,\n senderPubkey: transfer.senderTransportPubkey,\n tokens: [token],\n memo: payload.memo as string | undefined,\n receivedAt: transfer.timestamp,\n };\n this.deps!.emitEvent('transfer:incoming', incomingTransfer);\n\n // Parse commitment and start proof polling\n try {\n const commitment = await TransferCommitment.fromJSON(commitmentInput);\n const requestIdBytes = commitment.requestId;\n const requestIdHex = requestIdBytes instanceof Uint8Array\n ? Array.from(requestIdBytes).map(b => b.toString(16).padStart(2, '0')).join('')\n : String(requestIdBytes);\n\n // Submit commitment to aggregator (idempotent - same as sender)\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (stClient) {\n const response = await stClient.submitTransferCommitment(commitment);\n this.log(`NOSTR-FIRST recipient commitment submit: ${response.status}`);\n }\n\n // Start polling for proof\n this.addProofPollingJob({\n tokenId: token.id,\n requestIdHex,\n commitmentJson: JSON.stringify(commitmentInput),\n startedAt: Date.now(),\n attemptCount: 0,\n lastAttemptAt: 0,\n onProofReceived: async (tokenId) => {\n // When proof arrives, finalize the token and update status\n await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, transfer.senderTransportPubkey);\n },\n });\n } catch (err) {\n console.error('[Payments] Failed to parse commitment for proof polling:', err);\n // Token remains as 'submitted' - will eventually time out\n }\n } catch (error) {\n console.error('[Payments] Failed to process NOSTR-FIRST transfer:', error);\n }\n }\n\n /**\n * Shared finalization logic for received transfers.\n * Handles both PROXY (with nametag token + address validation) and DIRECT schemes.\n */\n private async finalizeTransferToken(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n sourceToken: SdkToken<any>,\n transferTx: TransferTransaction,\n stClient: StateTransitionClient,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n trustBase: any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<SdkToken<any>> {\n const recipientAddress = transferTx.data.recipient;\n const addressScheme = recipientAddress.scheme;\n const signingService = await this.createSigningService();\n const transferSalt = transferTx.data.salt;\n\n const recipientPredicate = await UnmaskedPredicate.create(\n sourceToken.id,\n sourceToken.type,\n signingService,\n HashAlgorithm.SHA256,\n transferSalt\n );\n const recipientState = new TokenState(recipientPredicate, null);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let nametagTokens: SdkToken<any>[] = [];\n\n if (addressScheme === AddressScheme.PROXY) {\n // PROXY: Validate nametag address match (per reference impl)\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyNametag = this.getNametag();\n if (!proxyNametag?.token) {\n throw new Error('Cannot finalize PROXY transfer - no nametag token');\n }\n const nametagToken = await SdkToken.fromJSON(proxyNametag.token);\n const proxy = await ProxyAddress.fromTokenId(nametagToken.id);\n if (proxy.address !== recipientAddress.address) {\n throw new Error(\n `PROXY address mismatch: nametag resolves to ${proxy.address} ` +\n `but transfer targets ${recipientAddress.address}`\n );\n }\n nametagTokens = [nametagToken];\n }\n // DIRECT: nametagTokens stays empty []\n\n return stClient.finalizeTransaction(\n trustBase,\n sourceToken,\n recipientState,\n transferTx,\n nametagTokens\n );\n }\n\n /**\n * Finalize a received token after proof is available\n */\n private async finalizeReceivedToken(\n tokenId: string,\n sourceTokenInput: unknown,\n commitmentInput: unknown,\n senderPubkey: string\n ): Promise<void> {\n try {\n const token = this.tokens.get(tokenId);\n if (!token) {\n this.log(`Token ${tokenId} not found for finalization`);\n return;\n }\n\n // Get proof from aggregator\n const commitment = await TransferCommitment.fromJSON(commitmentInput);\n if (!this.deps!.oracle.waitForProofSdk) {\n this.log('Cannot finalize - no waitForProofSdk');\n token.status = 'confirmed'; // Mark as confirmed anyway\n token.updatedAt = Date.now();\n await this.save();\n return;\n }\n\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const transferTx = commitment.toTransaction(inclusionProof as any);\n\n // Parse source token\n const sourceToken = await SdkToken.fromJSON(sourceTokenInput);\n\n // Get state transition client\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n\n if (!stClient || !trustBase) {\n this.log('Cannot finalize - missing state transition client or trust base');\n token.status = 'confirmed';\n token.updatedAt = Date.now();\n await this.save();\n return;\n }\n\n // Finalize using shared helper (handles PROXY address validation)\n const finalizedSdkToken = await this.finalizeTransferToken(\n sourceToken, transferTx, stClient, trustBase\n );\n\n // Update token with finalized data (create new token with updated sdkData)\n const finalizedToken: Token = {\n ...token,\n status: 'confirmed',\n updatedAt: Date.now(),\n sdkData: JSON.stringify(finalizedSdkToken.toJSON()),\n };\n this.tokens.set(tokenId, finalizedToken);\n await this.save();\n\n this.log(`NOSTR-FIRST: Token ${tokenId.slice(0, 8)}... finalized and confirmed`);\n\n // Emit confirmation event\n this.deps!.emitEvent('transfer:confirmed', {\n id: crypto.randomUUID(),\n status: 'completed',\n tokens: [finalizedToken],\n tokenTransfers: [],\n });\n\n // Add to history\n await this.addToHistory({\n type: 'RECEIVED',\n amount: finalizedToken.amount,\n coinId: finalizedToken.coinId,\n symbol: finalizedToken.symbol,\n timestamp: Date.now(),\n senderPubkey,\n });\n } catch (error) {\n console.error('[Payments] Failed to finalize received token:', error);\n // Mark as confirmed anyway (user has the token)\n const token = this.tokens.get(tokenId);\n if (token && token.status === 'submitted') {\n token.status = 'confirmed';\n token.updatedAt = Date.now();\n await this.save();\n }\n }\n }\n\n private async handleIncomingTransfer(transfer: IncomingTokenTransfer): Promise<void> {\n try {\n // Check payload format - Sphere wallet sends { sourceToken, transferTx }\n // SDK format is { token, proof }\n // INSTANT_SPLIT format is { type: 'INSTANT_SPLIT', version, ... }\n const payload = transfer.payload as unknown as Record<string, unknown>;\n\n // Check for INSTANT_SPLIT bundle - may be the payload itself or nested in payload.token\n let instantBundle: InstantSplitBundle | null = null;\n if (isInstantSplitBundle(payload)) {\n instantBundle = payload as InstantSplitBundle;\n } else if (payload.token) {\n // InstantSplitExecutor wraps V5 bundle as { token: JSON.stringify(bundle), proof: null }\n try {\n const inner = typeof payload.token === 'string' ? JSON.parse(payload.token as string) : payload.token;\n if (isInstantSplitBundle(inner)) {\n instantBundle = inner as InstantSplitBundle;\n }\n } catch {\n // Not a JSON string or not a bundle - fall through\n }\n }\n\n if (instantBundle) {\n this.log('Processing INSTANT_SPLIT bundle...');\n try {\n const result = await this.processInstantSplitBundle(\n instantBundle,\n transfer.senderTransportPubkey\n );\n if (result.success) {\n this.log('INSTANT_SPLIT processed successfully');\n } else {\n console.warn('[Payments] INSTANT_SPLIT processing failed:', result.error);\n }\n } catch (err) {\n console.error('[Payments] INSTANT_SPLIT processing error:', err);\n }\n return;\n }\n\n // Check for NOSTR-FIRST commitment-only transfer (whole-token instant send)\n if (payload.sourceToken && payload.commitmentData && !payload.transferTx) {\n this.log('Processing NOSTR-FIRST commitment-only transfer...');\n await this.handleCommitmentOnlyTransfer(transfer, payload);\n return;\n }\n\n let tokenData: unknown;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let finalizedSdkToken: SdkToken<any> | null = null;\n\n if (payload.sourceToken && payload.transferTx) {\n // Sphere wallet format - needs finalization for PROXY addresses\n this.log('Processing Sphere wallet format transfer...');\n\n const sourceTokenInput = typeof payload.sourceToken === 'string'\n ? JSON.parse(payload.sourceToken as string)\n : payload.sourceToken;\n const transferTxInput = typeof payload.transferTx === 'string'\n ? JSON.parse(payload.transferTx as string)\n : payload.transferTx;\n\n if (!sourceTokenInput || !transferTxInput) {\n console.warn('[Payments] Invalid Sphere wallet transfer format');\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let sourceToken: SdkToken<any>;\n let transferTx: TransferTransaction;\n\n try {\n sourceToken = await SdkToken.fromJSON(sourceTokenInput);\n } catch (err) {\n console.error('[Payments] Failed to parse sourceToken:', err);\n return;\n }\n\n // Try multiple parsing strategies for transferTx\n // Format 1: TransferTransaction - has { data, inclusionProof }\n // Format 2: TransferCommitment - has { authenticator, requestId, transactionData }\n try {\n // Detect format based on structure\n const hasInclusionProof = transferTxInput.inclusionProof !== undefined;\n const hasData = transferTxInput.data !== undefined;\n const hasTransactionData = transferTxInput.transactionData !== undefined;\n const hasAuthenticator = transferTxInput.authenticator !== undefined;\n\n if (hasData && hasInclusionProof) {\n // Full transaction format - parse directly\n transferTx = await TransferTransaction.fromJSON(transferTxInput);\n } else if (hasTransactionData && hasAuthenticator) {\n // Commitment format - submit and wait for proof\n const commitment = await TransferCommitment.fromJSON(transferTxInput);\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n console.error('[Payments] Cannot process commitment - no state transition client');\n return;\n }\n\n const response = await stClient.submitTransferCommitment(commitment);\n if (response.status !== 'SUCCESS' && response.status !== 'REQUEST_ID_EXISTS') {\n console.error('[Payments] Transfer commitment submission failed:', response.status);\n return;\n }\n\n if (!this.deps!.oracle.waitForProofSdk) {\n console.error('[Payments] Cannot wait for proof - missing oracle method');\n return;\n }\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transferTx = commitment.toTransaction(inclusionProof as any);\n } else {\n // Unknown format - try parsing approaches\n try {\n transferTx = await TransferTransaction.fromJSON(transferTxInput);\n } catch {\n // Try commitment format as fallback\n const commitment = await TransferCommitment.fromJSON(transferTxInput);\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient || !this.deps!.oracle.waitForProofSdk) {\n throw new Error('Cannot submit commitment - missing oracle methods');\n }\n await stClient.submitTransferCommitment(commitment);\n const inclusionProof = await this.deps!.oracle.waitForProofSdk(commitment);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n transferTx = commitment.toTransaction(inclusionProof as any);\n }\n }\n } catch (err) {\n console.error('[Payments] Failed to parse transferTx:', err);\n return;\n }\n\n // Finalize using shared helper (handles PROXY address validation)\n try {\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const trustBase = (this.deps!.oracle as any).getTrustBase?.();\n if (!stClient || !trustBase) {\n console.error('[Payments] Cannot finalize - missing state transition client or trust base. Token rejected.');\n return;\n }\n finalizedSdkToken = await this.finalizeTransferToken(sourceToken, transferTx, stClient, trustBase);\n tokenData = finalizedSdkToken.toJSON();\n const addressScheme = transferTx.data.recipient.scheme;\n this.log(`${addressScheme === AddressScheme.PROXY ? 'PROXY' : 'DIRECT'} finalization successful`);\n } catch (finalizeError) {\n console.error(`[Payments] Finalization FAILED - token rejected:`, finalizeError);\n return;\n }\n } else if (payload.token) {\n // SDK format\n tokenData = payload.token;\n } else {\n console.warn('[Payments] Unknown transfer payload format');\n return;\n }\n\n // Validate token\n const validation = await this.deps!.oracle.validateToken(tokenData);\n if (!validation.valid) {\n console.warn('[Payments] Received invalid token');\n return;\n }\n\n // Parse token info from SDK data\n const tokenInfo = await parseTokenInfo(tokenData);\n\n // Create token entry\n const token: Token = {\n id: tokenInfo.tokenId ?? crypto.randomUUID(),\n coinId: tokenInfo.coinId,\n symbol: tokenInfo.symbol,\n name: tokenInfo.name,\n decimals: tokenInfo.decimals,\n iconUrl: tokenInfo.iconUrl,\n amount: tokenInfo.amount,\n status: 'confirmed',\n createdAt: Date.now(),\n updatedAt: Date.now(),\n sdkData: typeof tokenData === 'string'\n ? tokenData\n : JSON.stringify(tokenData),\n };\n\n // addToken() checks tombstones with exact (tokenId, stateHash) match\n // Tokens with same tokenId but different stateHash pass through (new state)\n await this.addToken(token);\n\n const incomingTransfer: IncomingTransfer = {\n id: transfer.id,\n senderPubkey: transfer.senderTransportPubkey,\n tokens: [token],\n memo: payload.memo as string | undefined,\n receivedAt: transfer.timestamp,\n };\n\n this.deps!.emitEvent('transfer:incoming', incomingTransfer);\n this.log(`Incoming transfer processed: ${token.id}, ${token.amount} ${token.symbol}`);\n } catch (error) {\n console.error('[Payments] Failed to process incoming transfer:', error);\n }\n }\n\n // ===========================================================================\n // Private: Archive\n // ===========================================================================\n\n private async archiveToken(token: Token): Promise<void> {\n const txf = tokenToTxf(token);\n if (!txf) return;\n\n const tokenId = txf.genesis?.data?.tokenId;\n if (!tokenId) return;\n\n const existingArchive = this.archivedTokens.get(tokenId);\n\n if (existingArchive) {\n if (isIncrementalUpdate(existingArchive, txf)) {\n this.archivedTokens.set(tokenId, txf);\n this.log(`Updated archived token ${tokenId.slice(0, 8)}...`);\n } else {\n // Fork\n const stateHash = getCurrentStateHash(txf) || '';\n await this.storeForkedToken(tokenId, stateHash, txf);\n this.log(`Archived token ${tokenId.slice(0, 8)}... is a fork`);\n }\n } else {\n this.archivedTokens.set(tokenId, txf);\n this.log(`Archived token ${tokenId.slice(0, 8)}...`);\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async save(): Promise<void> {\n // Save to TokenStorageProviders (IndexedDB/files)\n const providers = this.getTokenStorageProviders();\n if (providers.size === 0) {\n this.log('No token storage providers - tokens not persisted');\n return;\n }\n\n const data = await this.createStorageData();\n for (const [id, provider] of providers) {\n try {\n await provider.save(data);\n } catch (err) {\n console.error(`[Payments] Failed to save to provider ${id}:`, err);\n }\n }\n\n // Save pending V5 tokens separately (TXF can't represent them)\n await this.savePendingV5Tokens();\n }\n\n private async saveToOutbox(transfer: TransferResult, recipient: string): Promise<void> {\n const outbox = await this.loadOutbox();\n outbox.push({ transfer, recipient, createdAt: Date.now() });\n await this.deps!.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(outbox));\n }\n\n private async removeFromOutbox(transferId: string): Promise<void> {\n const outbox = await this.loadOutbox();\n const filtered = outbox.filter((e) => e.transfer.id !== transferId);\n await this.deps!.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(filtered));\n }\n\n private async loadOutbox(): Promise<Array<{ transfer: TransferResult; recipient: string; createdAt: number }>> {\n const data = await this.deps!.storage.get(STORAGE_KEYS_ADDRESS.OUTBOX);\n return data ? JSON.parse(data) : [];\n }\n\n private async createStorageData(): Promise<TxfStorageDataBase> {\n return await buildTxfStorageData(\n Array.from(this.tokens.values()),\n {\n version: 1,\n address: this.deps!.identity.l1Address,\n ipnsName: this.deps!.identity.ipnsName ?? '',\n },\n {\n nametags: this.nametags,\n tombstones: this.tombstones,\n archivedTokens: this.archivedTokens,\n forkedTokens: this.forkedTokens,\n }\n ) as unknown as TxfStorageDataBase;\n }\n\n private loadFromStorageData(data: TxfStorageDataBase): void {\n const parsed = parseTxfStorageData(data);\n\n // Load tombstones FIRST so we can filter tokens\n this.tombstones = parsed.tombstones;\n // Load tokens, filtering out tombstoned ones\n // NOTE: Only filter by exact (tokenId, stateHash) match to avoid over-blocking\n // When state hash is unavailable, we can't reliably distinguish old from new\n this.tokens.clear();\n for (const token of parsed.tokens) {\n const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);\n const stateHash = extractStateHashFromSdkData(token.sdkData);\n\n // Only filter if we have exact state match\n if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {\n this.log(`Skipping tombstoned token ${sdkTokenId.slice(0, 8)}... during load (exact state match)`);\n continue;\n }\n\n this.tokens.set(token.id, token);\n }\n\n // Load other data\n this.archivedTokens = parsed.archivedTokens;\n this.forkedTokens = parsed.forkedTokens;\n this.nametags = parsed.nametags;\n }\n\n // ===========================================================================\n // Private: NOSTR-FIRST Proof Polling\n // ===========================================================================\n\n /**\n * Submit commitment to aggregator and start background proof polling\n * (NOSTR-FIRST pattern: fire-and-forget submission)\n */\n private async submitAndPollForProof(\n tokenId: string,\n commitment: TransferCommitment,\n requestIdHex: string,\n onProofReceived?: (tokenId: string) => void\n ): Promise<void> {\n try {\n // Submit to aggregator\n const stClient = this.deps!.oracle.getStateTransitionClient?.() as StateTransitionClient | undefined;\n if (!stClient) {\n this.log('Cannot submit commitment - no state transition client');\n return;\n }\n\n const response = await stClient.submitTransferCommitment(commitment);\n if (response.status !== 'SUCCESS' && response.status !== 'REQUEST_ID_EXISTS') {\n this.log(`Transfer commitment submission failed: ${response.status}`);\n // Mark token as invalid since submission failed\n const token = this.tokens.get(tokenId);\n if (token) {\n token.status = 'invalid';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n await this.save();\n }\n return;\n }\n\n // Add to polling queue\n this.addProofPollingJob({\n tokenId,\n requestIdHex,\n commitmentJson: JSON.stringify(commitment.toJSON()),\n startedAt: Date.now(),\n attemptCount: 0,\n lastAttemptAt: 0,\n onProofReceived,\n });\n } catch (error) {\n this.log('submitAndPollForProof error:', error);\n }\n }\n\n /**\n * Add a proof polling job to the queue\n */\n private addProofPollingJob(job: ProofPollingJob): void {\n this.proofPollingJobs.set(job.tokenId, job);\n this.log(`Added proof polling job for token ${job.tokenId.slice(0, 8)}...`);\n this.startProofPolling();\n }\n\n /**\n * Start the proof polling interval if not already running\n */\n private startProofPolling(): void {\n if (this.proofPollingInterval) return;\n if (this.proofPollingJobs.size === 0) return;\n\n this.log('Starting proof polling...');\n this.proofPollingInterval = setInterval(\n () => this.processProofPollingQueue(),\n PaymentsModule.PROOF_POLLING_INTERVAL_MS\n );\n }\n\n /**\n * Stop the proof polling interval\n */\n private stopProofPolling(): void {\n if (this.proofPollingInterval) {\n clearInterval(this.proofPollingInterval);\n this.proofPollingInterval = null;\n this.log('Stopped proof polling');\n }\n }\n\n /**\n * Process all pending proof polling jobs\n */\n private async processProofPollingQueue(): Promise<void> {\n if (this.proofPollingJobs.size === 0) {\n this.stopProofPolling();\n return;\n }\n\n const completedJobs: string[] = [];\n\n for (const [tokenId, job] of this.proofPollingJobs) {\n try {\n job.attemptCount++;\n job.lastAttemptAt = Date.now();\n\n // Check for timeout\n if (job.attemptCount >= PaymentsModule.PROOF_POLLING_MAX_ATTEMPTS) {\n this.log(`Proof polling timeout for token ${tokenId.slice(0, 8)}...`);\n // Mark token as invalid due to timeout\n const token = this.tokens.get(tokenId);\n if (token && token.status === 'submitted') {\n token.status = 'invalid';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n }\n completedJobs.push(tokenId);\n continue;\n }\n\n // Try to get proof from aggregator using a short timeout\n const commitment = await TransferCommitment.fromJSON(JSON.parse(job.commitmentJson));\n\n // Try to get proof with a quick timeout (non-blocking check)\n let inclusionProof: unknown = null;\n try {\n // Create abort controller for quick timeout\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => abortController.abort(), 500);\n\n if (this.deps!.oracle.waitForProofSdk) {\n inclusionProof = await Promise.race([\n this.deps!.oracle.waitForProofSdk(commitment, abortController.signal),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), 500)),\n ]);\n } else {\n // Fallback: use getProof with request ID hex\n const proof = await this.deps!.oracle.getProof(job.requestIdHex);\n if (proof) {\n inclusionProof = proof;\n }\n }\n\n clearTimeout(timeoutId);\n } catch (err) {\n // Proof not ready yet or timed out\n continue;\n }\n\n if (!inclusionProof) {\n // Proof not ready yet\n continue;\n }\n\n // Proof received! Update token status\n const token = this.tokens.get(tokenId);\n if (token) {\n token.status = 'spent';\n token.updatedAt = Date.now();\n this.tokens.set(tokenId, token);\n await this.save();\n this.log(`Proof received for token ${tokenId.slice(0, 8)}..., status: spent`);\n }\n\n // Call callback if provided\n job.onProofReceived?.(tokenId);\n completedJobs.push(tokenId);\n } catch (error) {\n // Most errors mean proof is not ready yet, continue polling\n this.log(`Proof polling attempt ${job.attemptCount} for ${tokenId.slice(0, 8)}...: ${error}`);\n }\n }\n\n // Remove completed jobs\n for (const tokenId of completedJobs) {\n this.proofPollingJobs.delete(tokenId);\n }\n\n // Stop polling if no more jobs\n if (this.proofPollingJobs.size === 0) {\n this.stopProofPolling();\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureInitialized(): void {\n if (!this.deps) {\n throw new Error('PaymentsModule not initialized');\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createPaymentsModule(config?: PaymentsModuleConfig): PaymentsModule {\n return new PaymentsModule(config);\n}\n","/**\n * TokenRecoveryService\n *\n * Recovers tokens from failed or incomplete instant split operations.\n *\n * Recovery Scenarios:\n * 1. Orphaned splits: Burn completed but mints never submitted\n * 2. Lost change tokens: Mints completed but change token never saved\n * 3. Sent tokens: Recover tokens from sent Nostr events\n *\n * This service works with the storage provider to persist recovered tokens.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { Token } from '@unicitylabs/state-transition-sdk/lib/token/Token';\nimport { TokenId } from '@unicitylabs/state-transition-sdk/lib/token/TokenId';\nimport { TokenState } from '@unicitylabs/state-transition-sdk/lib/token/TokenState';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { CoinId } from '@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate';\nimport { MintCommitment } from '@unicitylabs/state-transition-sdk/lib/transaction/MintCommitment';\nimport { MintTransactionData } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData';\nimport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils';\nimport type { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport type { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient';\nimport type { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';\n\nimport type {\n InstantSplitBundleV5,\n InstantSplitV5RecoveryMetadata,\n SplitRecoveryResult,\n} from '../../types/instant-split';\nimport type { TransportProvider } from '../../transport';\nimport type { StorageProvider } from '../../storage';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TokenRecoveryServiceConfig {\n stateTransitionClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n /** Dev mode skips trust base verification */\n devMode?: boolean;\n}\n\nexport interface RecoveryDependencies {\n stClient: StateTransitionClient;\n trustBase: RootTrustBase;\n signingService: SigningService;\n devMode?: boolean;\n}\n\n/**\n * An outbox entry with V5 recovery metadata\n */\nexport interface V5OutboxEntry {\n id: string;\n splitGroupId: string;\n status: string;\n metadata?: InstantSplitV5RecoveryMetadata;\n bundleJson?: string;\n}\n\n/**\n * Options for recovering sent tokens\n */\nexport interface RecoverSentOptions {\n /** Unix timestamp to start scanning from (default: 30 days ago) */\n since?: number;\n /** Maximum number of events to scan (default: 100) */\n limit?: number;\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nasync function sha256(input: string | Uint8Array): Promise<Uint8Array> {\n const data = typeof input === 'string' ? new TextEncoder().encode(input) : input;\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n return new Uint8Array(hashBuffer);\n}\n\nfunction fromHex(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);\n }\n return bytes;\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class TokenRecoveryService {\n private client: StateTransitionClient;\n private trustBase: RootTrustBase;\n private signingService: SigningService;\n private devMode: boolean;\n\n constructor(config: TokenRecoveryServiceConfig) {\n this.client = config.stateTransitionClient;\n this.trustBase = config.trustBase;\n this.signingService = config.signingService;\n this.devMode = config.devMode ?? false;\n }\n\n /**\n * Recover change tokens from orphaned V5 splits.\n *\n * This scans outbox entries to find splits where:\n * - Nostr delivery succeeded\n * - But change token was never saved (browser crash, etc.)\n *\n * @param outboxEntries - Array of V5 outbox entries to check\n * @param onTokenRecovered - Callback when a token is recovered\n * @returns Recovery result\n */\n async recoverOrphanedSplits(\n outboxEntries: V5OutboxEntry[],\n onTokenRecovered?: (token: Token<any>, splitGroupId: string) => Promise<void>\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n for (const entry of outboxEntries) {\n // Only process entries that were sent but not completed\n if (entry.status !== 'NOSTR_SENT' && entry.status !== 'SENT') {\n continue;\n }\n\n const metadata = entry.metadata;\n if (!metadata || metadata.version !== '5.0') {\n continue;\n }\n\n try {\n console.log(`[Recovery] Processing orphaned split ${entry.splitGroupId}`);\n\n // Reconstruct the sender's mint commitment from metadata\n const senderTokenId = new TokenId(fromHex(metadata.senderTokenIdHex));\n const senderSalt = fromHex(metadata.senderSaltHex);\n\n // Try to get the mint proof from the aggregator\n // This will succeed if the background submission completed before crash\n const changeToken = await this.tryRecoverChangeToken(\n metadata.seedString,\n senderTokenId,\n senderSalt,\n metadata.changeAmount,\n entry.bundleJson\n );\n\n if (changeToken) {\n await onTokenRecovered?.(changeToken, entry.splitGroupId);\n result.changeTokensRecovered++;\n result.splitsRecovered++;\n console.log(`[Recovery] Recovered change token for split ${entry.splitGroupId}`);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push({\n splitGroupId: entry.splitGroupId,\n error: errorMessage,\n timestamp: Date.now(),\n });\n console.error(`[Recovery] Failed to recover split ${entry.splitGroupId}:`, error);\n }\n }\n\n result.durationMs = performance.now() - startTime;\n console.log(`[Recovery] Completed in ${result.durationMs.toFixed(0)}ms: ${result.changeTokensRecovered} tokens recovered`);\n\n return result;\n }\n\n /**\n * Try to recover a change token by checking if the mint proof exists.\n *\n * @param seedString - Original seed string used for split\n * @param senderTokenId - Token ID for the change token\n * @param senderSalt - Salt for the change token\n * @param changeAmount - Amount of the change token\n * @param bundleJson - Optional bundle JSON for additional context\n * @returns Recovered token or null\n */\n private async tryRecoverChangeToken(\n seedString: string,\n senderTokenId: TokenId,\n senderSalt: Uint8Array,\n changeAmount: string,\n bundleJson?: string\n ): Promise<Token<any> | null> {\n try {\n // Parse bundle for token type if available\n let tokenType: TokenType | undefined;\n let coinId: CoinId | undefined;\n\n if (bundleJson) {\n const bundle = JSON.parse(bundleJson) as InstantSplitBundleV5;\n tokenType = new TokenType(fromHex(bundle.tokenTypeHex));\n coinId = new CoinId(fromHex(bundle.coinId));\n }\n\n if (!tokenType) {\n console.log('[Recovery] Cannot recover: no token type available');\n return null;\n }\n\n // Create the predicate for the change token\n const predicate = await UnmaskedPredicate.create(\n senderTokenId,\n tokenType,\n this.signingService,\n HashAlgorithm.SHA256,\n senderSalt\n );\n const state = new TokenState(predicate, null);\n\n // Try to get the proof from the aggregator\n // The mint was submitted in background, so it might exist\n // We need to recreate the MintTransactionData to create the commitment\n // For V5 recovery, we'd need the full mint data which is in the background context\n\n // This is a simplified recovery - in production, you'd need to store\n // the full MintCommitment JSON in the outbox for complete recovery\n console.log('[Recovery] Would attempt to recover change token - mint proof lookup not implemented');\n return null;\n } catch (error) {\n console.warn('[Recovery] Failed to recover change token:', error);\n return null;\n }\n }\n\n /**\n * Recover tokens from sent Nostr events.\n *\n * This scans outgoing Nostr events to reconstruct tokens that were sent\n * but may not be properly reflected in local storage.\n *\n * @param transport - Transport provider to query events\n * @param options - Recovery options\n * @returns Recovery result\n */\n async recoverSentTokens(\n transport: TransportProvider,\n options?: RecoverSentOptions\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n // Note: Full implementation would query Nostr for sent events\n // and cross-reference with local storage to find missing tokens\n console.log('[Recovery] Sent token recovery not fully implemented');\n\n result.durationMs = performance.now() - startTime;\n return result;\n }\n\n /**\n * Recover from a split where the burn completed but mints failed.\n *\n * This is a critical recovery scenario - the original token is burned,\n * but the new tokens were never created. We attempt to recreate them.\n *\n * @param splitGroupId - The split group ID\n * @param burnRequestIdHex - The burn transaction request ID\n * @param seedString - The seed string used for split calculations\n * @param tokenType - The token type\n * @param coinId - The coin ID\n * @param splitAmount - Amount for recipient\n * @param changeAmount - Amount for sender\n * @returns Recovery result\n */\n async recoverSplitBurnFailure(\n splitGroupId: string,\n burnRequestIdHex: string,\n seedString: string,\n tokenType: TokenType,\n coinId: CoinId,\n splitAmount: bigint,\n changeAmount: bigint,\n onTokenRecovered?: (token: Token<any>, isChange: boolean) => Promise<void>\n ): Promise<SplitRecoveryResult> {\n const startTime = performance.now();\n const result: SplitRecoveryResult = {\n splitsRecovered: 0,\n changeTokensRecovered: 0,\n errors: [],\n durationMs: 0,\n };\n\n try {\n console.log(`[Recovery] Attempting burn failure recovery for ${splitGroupId}`);\n\n // Regenerate the token IDs and salts\n const recipientTokenId = new TokenId(await sha256(seedString));\n const senderTokenId = new TokenId(await sha256(seedString + '_sender'));\n const recipientSalt = await sha256(seedString + '_recipient_salt');\n const senderSalt = await sha256(seedString + '_sender_salt');\n\n // Note: Full recovery would require:\n // 1. Querying the aggregator for the burn transaction\n // 2. Recreating the mint commitments with SplitMintReason\n // 3. Submitting the mints if not already submitted\n // 4. Waiting for proofs and creating the tokens\n\n // This is a complex operation that depends on the specific failure mode\n console.log('[Recovery] Burn failure recovery not fully implemented');\n console.log(`[Recovery] Would recover: ${toHex(senderTokenId.bytes).slice(0, 16)}... (change)`);\n\n result.errors.push({\n splitGroupId,\n error: 'Burn failure recovery not fully implemented',\n timestamp: Date.now(),\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n result.errors.push({\n splitGroupId,\n error: errorMessage,\n timestamp: Date.now(),\n });\n }\n\n result.durationMs = performance.now() - startTime;\n return result;\n }\n\n /**\n * Verify a token still exists and is valid on the aggregator.\n *\n * @param token - The token to verify\n * @returns true if token is valid, false otherwise\n */\n async verifyTokenExists(token: Token<any>): Promise<boolean> {\n try {\n if (this.devMode) {\n return true;\n }\n\n const verification = await token.verify(this.trustBase);\n return verification.isSuccessful;\n } catch {\n return false;\n }\n }\n}\n\n/**\n * Factory function for creating TokenRecoveryService\n */\nexport function createTokenRecoveryService(config: TokenRecoveryServiceConfig): TokenRecoveryService {\n return new TokenRecoveryService(config);\n}\n","/**\n * Communications Module\n * Platform-independent messaging operations\n */\n\nimport type {\n DirectMessage,\n BroadcastMessage,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n} from '../../types';\nimport type { StorageProvider } from '../../storage';\nimport type { TransportProvider, IncomingMessage, IncomingBroadcast } from '../../transport';\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface CommunicationsModuleConfig {\n /** Auto-save messages */\n autoSave?: boolean;\n /** Max messages in memory */\n maxMessages?: number;\n /** Enable read receipts */\n readReceipts?: boolean;\n}\n\n// =============================================================================\n// Dependencies Interface\n// =============================================================================\n\nexport interface CommunicationsModuleDependencies {\n identity: FullIdentity;\n storage: StorageProvider;\n transport: TransportProvider;\n emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class CommunicationsModule {\n private config: Required<CommunicationsModuleConfig>;\n private deps: CommunicationsModuleDependencies | null = null;\n\n // State\n private messages: Map<string, DirectMessage> = new Map();\n private broadcasts: Map<string, BroadcastMessage> = new Map();\n\n // Subscriptions\n private unsubscribeMessages: (() => void) | null = null;\n private broadcastSubscriptions: Map<string, () => void> = new Map();\n\n // Handlers\n private dmHandlers: Set<(message: DirectMessage) => void> = new Set();\n private broadcastHandlers: Set<(message: BroadcastMessage) => void> = new Set();\n\n constructor(config?: CommunicationsModuleConfig) {\n this.config = {\n autoSave: config?.autoSave ?? true,\n maxMessages: config?.maxMessages ?? 1000,\n readReceipts: config?.readReceipts ?? true,\n };\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Initialize module with dependencies\n */\n initialize(deps: CommunicationsModuleDependencies): void {\n this.deps = deps;\n\n // Subscribe to incoming messages\n this.unsubscribeMessages = deps.transport.onMessage((msg) => {\n this.handleIncomingMessage(msg);\n });\n }\n\n /**\n * Load messages from storage\n */\n async load(): Promise<void> {\n this.ensureInitialized();\n\n const data = await this.deps!.storage.get('direct_messages');\n if (data) {\n const messages = JSON.parse(data) as DirectMessage[];\n this.messages.clear();\n for (const msg of messages) {\n this.messages.set(msg.id, msg);\n }\n }\n }\n\n /**\n * Cleanup resources\n */\n destroy(): void {\n this.unsubscribeMessages?.();\n this.unsubscribeMessages = null;\n\n for (const unsub of this.broadcastSubscriptions.values()) {\n unsub();\n }\n this.broadcastSubscriptions.clear();\n }\n\n // ===========================================================================\n // Public API - Direct Messages\n // ===========================================================================\n\n /**\n * Send direct message\n */\n async sendDM(recipient: string, content: string): Promise<DirectMessage> {\n this.ensureInitialized();\n\n // Resolve recipient\n const recipientPubkey = await this.resolveRecipient(recipient);\n\n // Send via transport\n const eventId = await this.deps!.transport.sendMessage(recipientPubkey, content);\n\n // Create message record\n const message: DirectMessage = {\n id: eventId,\n senderPubkey: this.deps!.identity.chainPubkey,\n senderNametag: this.deps!.identity.nametag,\n recipientPubkey,\n content,\n timestamp: Date.now(),\n isRead: true,\n };\n\n // Save\n this.messages.set(message.id, message);\n if (this.config.autoSave) {\n await this.save();\n }\n\n return message;\n }\n\n /**\n * Get conversation with peer\n */\n getConversation(peerPubkey: string): DirectMessage[] {\n return Array.from(this.messages.values())\n .filter(\n (m) => m.senderPubkey === peerPubkey || m.recipientPubkey === peerPubkey\n )\n .sort((a, b) => a.timestamp - b.timestamp);\n }\n\n /**\n * Get all conversations grouped by peer\n */\n getConversations(): Map<string, DirectMessage[]> {\n const conversations = new Map<string, DirectMessage[]>();\n\n for (const message of this.messages.values()) {\n const peer =\n message.senderPubkey === this.deps?.identity.chainPubkey\n ? message.recipientPubkey\n : message.senderPubkey;\n\n if (!conversations.has(peer)) {\n conversations.set(peer, []);\n }\n conversations.get(peer)!.push(message);\n }\n\n // Sort each conversation\n for (const msgs of conversations.values()) {\n msgs.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n return conversations;\n }\n\n /**\n * Mark messages as read\n */\n async markAsRead(messageIds: string[]): Promise<void> {\n for (const id of messageIds) {\n const msg = this.messages.get(id);\n if (msg) {\n msg.isRead = true;\n }\n }\n\n if (this.config.autoSave) {\n await this.save();\n }\n }\n\n /**\n * Get unread count\n */\n getUnreadCount(peerPubkey?: string): number {\n let messages = Array.from(this.messages.values()).filter(\n (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.chainPubkey\n );\n\n if (peerPubkey) {\n messages = messages.filter((m) => m.senderPubkey === peerPubkey);\n }\n\n return messages.length;\n }\n\n /**\n * Subscribe to incoming DMs\n */\n onDirectMessage(handler: (message: DirectMessage) => void): () => void {\n this.dmHandlers.add(handler);\n return () => this.dmHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Public API - Broadcasts\n // ===========================================================================\n\n /**\n * Publish broadcast message\n */\n async broadcast(content: string, tags?: string[]): Promise<BroadcastMessage> {\n this.ensureInitialized();\n\n const eventId = await this.deps!.transport.publishBroadcast?.(content, tags);\n\n const message: BroadcastMessage = {\n id: eventId ?? crypto.randomUUID(),\n authorPubkey: this.deps!.identity.chainPubkey,\n authorNametag: this.deps!.identity.nametag,\n content,\n timestamp: Date.now(),\n tags,\n };\n\n this.broadcasts.set(message.id, message);\n return message;\n }\n\n /**\n * Subscribe to broadcasts with tags\n */\n subscribeToBroadcasts(tags: string[]): () => void {\n this.ensureInitialized();\n\n const key = tags.sort().join(':');\n if (this.broadcastSubscriptions.has(key)) {\n return () => {};\n }\n\n const unsub = this.deps!.transport.subscribeToBroadcast?.(tags, (broadcast) => {\n this.handleIncomingBroadcast(broadcast);\n });\n\n if (unsub) {\n this.broadcastSubscriptions.set(key, unsub);\n }\n\n return () => {\n const sub = this.broadcastSubscriptions.get(key);\n if (sub) {\n sub();\n this.broadcastSubscriptions.delete(key);\n }\n };\n }\n\n /**\n * Get broadcasts\n */\n getBroadcasts(limit?: number): BroadcastMessage[] {\n const messages = Array.from(this.broadcasts.values())\n .sort((a, b) => b.timestamp - a.timestamp);\n\n return limit ? messages.slice(0, limit) : messages;\n }\n\n /**\n * Subscribe to incoming broadcasts\n */\n onBroadcast(handler: (message: BroadcastMessage) => void): () => void {\n this.broadcastHandlers.add(handler);\n return () => this.broadcastHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Private: Message Handling\n // ===========================================================================\n\n private handleIncomingMessage(msg: IncomingMessage): void {\n // Skip own messages\n if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;\n\n const message: DirectMessage = {\n id: msg.id,\n senderPubkey: msg.senderTransportPubkey,\n senderNametag: msg.senderNametag,\n recipientPubkey: this.deps!.identity.chainPubkey,\n content: msg.content,\n timestamp: msg.timestamp,\n isRead: false,\n };\n\n this.messages.set(message.id, message);\n\n // Emit event\n this.deps!.emitEvent('message:dm', message);\n\n // Notify handlers\n for (const handler of this.dmHandlers) {\n try {\n handler(message);\n } catch (error) {\n console.error('[Communications] Handler error:', error);\n }\n }\n\n // Auto-save\n if (this.config.autoSave) {\n this.save();\n }\n\n // Prune if needed\n this.pruneIfNeeded();\n }\n\n private handleIncomingBroadcast(incoming: IncomingBroadcast): void {\n const message: BroadcastMessage = {\n id: incoming.id,\n authorPubkey: incoming.authorTransportPubkey,\n content: incoming.content,\n timestamp: incoming.timestamp,\n tags: incoming.tags,\n };\n\n this.broadcasts.set(message.id, message);\n\n // Emit event\n this.deps!.emitEvent('message:broadcast', message);\n\n // Notify handlers\n for (const handler of this.broadcastHandlers) {\n try {\n handler(message);\n } catch (error) {\n console.error('[Communications] Handler error:', error);\n }\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async save(): Promise<void> {\n const messages = Array.from(this.messages.values());\n await this.deps!.storage.set('direct_messages', JSON.stringify(messages));\n }\n\n private pruneIfNeeded(): void {\n if (this.messages.size <= this.config.maxMessages) return;\n\n const sorted = Array.from(this.messages.entries())\n .sort(([, a], [, b]) => a.timestamp - b.timestamp);\n\n const toRemove = sorted.slice(0, sorted.length - this.config.maxMessages);\n for (const [id] of toRemove) {\n this.messages.delete(id);\n }\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private async resolveRecipient(recipient: string): Promise<string> {\n if (recipient.startsWith('@')) {\n const pubkey = await this.deps!.transport.resolveNametag?.(recipient.slice(1));\n if (!pubkey) {\n throw new Error(`Nametag not found: ${recipient}`);\n }\n return pubkey;\n }\n return recipient;\n }\n\n private ensureInitialized(): void {\n if (!this.deps) {\n throw new Error('CommunicationsModule not initialized');\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createCommunicationsModule(\n config?: CommunicationsModuleConfig\n): CommunicationsModule {\n return new CommunicationsModule(config);\n}\n","/**\n * Group Chat Module (NIP-29)\n *\n * Relay-based group chat using NIP-29 protocol on a dedicated Nostr relay.\n * Embeds its own NostrClient — does NOT share the wallet's TransportProvider.\n */\n\nimport {\n NostrClient,\n NostrKeyManager,\n Filter,\n type Event,\n} from '@unicitylabs/nostr-js-sdk';\n\nimport type {\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n} from '../../types';\nimport type { StorageProvider } from '../../storage';\nimport { STORAGE_KEYS_GLOBAL, NIP29_KINDS } from '../../constants';\n\nimport type {\n GroupData,\n GroupMessageData,\n GroupMemberData,\n GroupChatModuleConfig,\n CreateGroupOptions,\n GroupRole,\n} from './types';\nimport { GroupRole as GroupRoleEnum, GroupVisibility as GroupVisibilityEnum } from './types';\n\n// =============================================================================\n// Dependencies\n// =============================================================================\n\nexport interface GroupChatModuleDependencies {\n identity: FullIdentity;\n storage: StorageProvider;\n emitEvent: <T extends SphereEventType>(type: T, data: SphereEventMap[T]) => void;\n}\n\n// =============================================================================\n// NIP-29 Filter Helper\n// =============================================================================\n\n/**\n * Extended filter data for NIP-29 queries.\n * NIP-29 uses 'h' tags for group IDs which aren't in the standard Filter type.\n */\ninterface Nip29FilterData {\n ids?: string[];\n authors?: string[];\n kinds?: number[];\n '#e'?: string[];\n '#p'?: string[];\n '#t'?: string[];\n '#d'?: string[];\n '#h'?: string[];\n since?: number;\n until?: number;\n limit?: number;\n}\n\nfunction createNip29Filter(data: Nip29FilterData): Filter {\n return new Filter(data as ConstructorParameters<typeof Filter>[0]);\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\nexport class GroupChatModule {\n private config: Required<GroupChatModuleConfig>;\n private deps: GroupChatModuleDependencies | null = null;\n\n // Nostr connection (separate from wallet relay)\n private client: NostrClient | null = null;\n private keyManager: NostrKeyManager | null = null;\n private connected = false;\n private connecting = false;\n private connectPromise: Promise<void> | null = null;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Subscription tracking (for cleanup)\n private subscriptionIds: string[] = [];\n\n // In-memory state\n private groups: Map<string, GroupData> = new Map();\n private messages: Map<string, GroupMessageData[]> = new Map(); // groupId -> messages\n private members: Map<string, GroupMemberData[]> = new Map(); // groupId -> members\n private processedEventIds: Set<string> = new Set();\n private pendingLeaves: Set<string> = new Set();\n\n // Persistence debounce\n private persistTimer: ReturnType<typeof setTimeout> | null = null;\n private persistPromise: Promise<void> | null = null;\n\n // Relay admin cache\n private relayAdminPubkeys: Set<string> | null = null;\n private relayAdminFetchPromise: Promise<Set<string>> | null = null;\n\n // Listeners\n private messageHandlers: Set<(message: GroupMessageData) => void> = new Set();\n\n constructor(config?: GroupChatModuleConfig) {\n this.config = {\n relays: config?.relays ?? [],\n defaultMessageLimit: config?.defaultMessageLimit ?? 50,\n maxPreviousTags: config?.maxPreviousTags ?? 3,\n reconnectDelayMs: config?.reconnectDelayMs ?? 3000,\n maxReconnectAttempts: config?.maxReconnectAttempts ?? 5,\n };\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n initialize(deps: GroupChatModuleDependencies): void {\n // If re-initializing (address switch), destroy old connection\n if (this.deps) {\n this.destroyConnection();\n }\n\n this.deps = deps;\n\n // Create key manager from identity\n const secretKey = Buffer.from(deps.identity.privateKey, 'hex');\n this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);\n }\n\n async load(): Promise<void> {\n this.ensureInitialized();\n const storage = this.deps!.storage;\n\n // Load groups\n const groupsJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_GROUPS);\n if (groupsJson) {\n try {\n const parsed: GroupData[] = JSON.parse(groupsJson);\n this.groups.clear();\n for (const g of parsed) {\n this.groups.set(g.id, g);\n }\n } catch {\n // Corrupted data, start fresh\n }\n }\n\n // Load messages\n const messagesJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MESSAGES);\n if (messagesJson) {\n try {\n const parsed: GroupMessageData[] = JSON.parse(messagesJson);\n this.messages.clear();\n for (const m of parsed) {\n const groupId = m.groupId;\n if (!this.messages.has(groupId)) {\n this.messages.set(groupId, []);\n }\n this.messages.get(groupId)!.push(m);\n }\n } catch {\n // Corrupted data, start fresh\n }\n }\n\n // Load members\n const membersJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MEMBERS);\n if (membersJson) {\n try {\n const parsed: GroupMemberData[] = JSON.parse(membersJson);\n this.members.clear();\n for (const m of parsed) {\n const groupId = m.groupId;\n if (!this.members.has(groupId)) {\n this.members.set(groupId, []);\n }\n this.members.get(groupId)!.push(m);\n }\n } catch {\n // Corrupted data, start fresh\n }\n }\n\n // Load processed event IDs\n const processedJson = await storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_PROCESSED_EVENTS);\n if (processedJson) {\n try {\n const parsed: string[] = JSON.parse(processedJson);\n this.processedEventIds = new Set(parsed);\n } catch {\n // Start fresh\n }\n }\n }\n\n destroy(): void {\n this.destroyConnection();\n this.groups.clear();\n this.messages.clear();\n this.members.clear();\n this.processedEventIds.clear();\n this.pendingLeaves.clear();\n this.messageHandlers.clear();\n this.relayAdminPubkeys = null;\n this.relayAdminFetchPromise = null;\n if (this.persistTimer) {\n clearTimeout(this.persistTimer);\n this.persistTimer = null;\n }\n this.deps = null;\n }\n\n private destroyConnection(): void {\n // Cancel pending reconnect\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n // Unsubscribe all active subscriptions\n if (this.client) {\n for (const subId of this.subscriptionIds) {\n try { this.client.unsubscribe(subId); } catch { /* ignore */ }\n }\n this.subscriptionIds = [];\n try {\n this.client.disconnect();\n } catch {\n // Ignore disconnect errors\n }\n this.client = null;\n }\n this.connected = false;\n this.connecting = false;\n this.connectPromise = null;\n this.reconnectAttempts = 0;\n this.keyManager = null;\n }\n\n // ===========================================================================\n // Connection\n // ===========================================================================\n\n async connect(): Promise<void> {\n if (this.connected) return;\n\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n this.connecting = true;\n this.connectPromise = this.doConnect().finally(() => {\n this.connecting = false;\n this.connectPromise = null;\n });\n\n return this.connectPromise;\n }\n\n getConnectionStatus(): boolean {\n return this.connected;\n }\n\n private async doConnect(): Promise<void> {\n this.ensureInitialized();\n\n if (!this.keyManager) {\n const secretKey = Buffer.from(this.deps!.identity.privateKey, 'hex');\n this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);\n }\n\n // Check relay URL change and clear stale data\n const primaryRelay = this.config.relays[0];\n if (primaryRelay) {\n await this.checkAndClearOnRelayChange(primaryRelay);\n }\n\n this.client = new NostrClient(this.keyManager);\n\n try {\n await this.client.connect(...this.config.relays);\n this.connected = true;\n this.reconnectAttempts = 0;\n\n this.deps!.emitEvent('groupchat:connection', { connected: true });\n\n // Check if we have local groups\n if (this.groups.size === 0) {\n // No local groups — try to restore from relay (e.g., after wallet import)\n await this.restoreJoinedGroups();\n } else {\n // Subscribe to events for existing joined groups\n await this.subscribeToJoinedGroups();\n }\n } catch (error) {\n console.error('[GroupChat] Failed to connect to relays', error);\n this.deps!.emitEvent('groupchat:connection', { connected: false });\n this.scheduleReconnect();\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {\n console.error('[GroupChat] Max reconnection attempts reached');\n return;\n }\n\n this.reconnectAttempts++;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n if (this.deps) { // Guard against post-destroy fire\n this.connect().catch(console.error);\n }\n }, this.config.reconnectDelayMs);\n }\n\n // ===========================================================================\n // Subscription Management\n // ===========================================================================\n\n private async subscribeToJoinedGroups(): Promise<void> {\n if (!this.client) return;\n\n const groupIds = Array.from(this.groups.keys());\n if (groupIds.length === 0) return;\n\n // Subscribe to group messages\n this.trackSubscription(\n createNip29Filter({\n kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],\n '#h': groupIds,\n }),\n { onEvent: (event: Event) => this.handleGroupEvent(event) },\n );\n\n // Subscribe to group metadata changes\n this.trackSubscription(\n createNip29Filter({\n kinds: [NIP29_KINDS.GROUP_METADATA, NIP29_KINDS.GROUP_MEMBERS, NIP29_KINDS.GROUP_ADMINS],\n '#d': groupIds,\n }),\n { onEvent: (event: Event) => this.handleMetadataEvent(event) },\n );\n\n // Subscribe to moderation events\n this.trackSubscription(\n createNip29Filter({\n kinds: [NIP29_KINDS.DELETE_EVENT, NIP29_KINDS.REMOVE_USER, NIP29_KINDS.DELETE_GROUP],\n '#h': groupIds,\n }),\n { onEvent: (event: Event) => this.handleModerationEvent(event) },\n );\n }\n\n private subscribeToGroup(groupId: string): void {\n if (!this.client) return;\n\n this.trackSubscription(\n createNip29Filter({\n kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],\n '#h': [groupId],\n }),\n { onEvent: (event: Event) => this.handleGroupEvent(event) },\n );\n\n this.trackSubscription(\n createNip29Filter({\n kinds: [NIP29_KINDS.DELETE_EVENT, NIP29_KINDS.REMOVE_USER, NIP29_KINDS.DELETE_GROUP],\n '#h': [groupId],\n }),\n { onEvent: (event: Event) => this.handleModerationEvent(event) },\n );\n }\n\n // ===========================================================================\n // Event Handlers\n // ===========================================================================\n\n private handleGroupEvent(event: Event): void {\n if (this.processedEventIds.has(event.id)) return;\n\n const groupId = this.getGroupIdFromEvent(event);\n if (!groupId) return;\n\n const group = this.groups.get(groupId);\n if (!group) return;\n\n const { text: content, senderNametag } = this.unwrapMessageContent(event.content);\n\n const message: GroupMessageData = {\n id: event.id,\n groupId,\n content,\n timestamp: event.created_at * 1000,\n senderPubkey: event.pubkey,\n senderNametag: senderNametag || undefined,\n replyToId: this.extractReplyTo(event),\n previousIds: this.extractPreviousIds(event),\n };\n\n this.saveMessageToMemory(message);\n this.addProcessedEventId(event.id);\n\n // Update or create member with nametag from this message\n if (senderNametag) {\n this.updateMemberNametag(groupId, event.pubkey, senderNametag, event.created_at * 1000);\n }\n\n // Update group last message and unread count\n this.updateGroupLastMessage(groupId, content.slice(0, 100), message.timestamp);\n const myPubkey = this.getMyPublicKey();\n if (event.pubkey !== myPubkey) {\n group.unreadCount = (group.unreadCount || 0) + 1;\n }\n\n // Emit event and notify listeners\n this.deps!.emitEvent('groupchat:message', message);\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n for (const handler of this.messageHandlers) {\n try { handler(message); } catch { /* ignore handler errors */ }\n }\n\n this.schedulePersist();\n }\n\n private handleMetadataEvent(event: Event): void {\n const groupId = this.getGroupIdFromMetadataEvent(event);\n if (!groupId) return;\n\n const group = this.groups.get(groupId);\n if (!group) return;\n\n if (event.kind === NIP29_KINDS.GROUP_METADATA) {\n if (!event.content || event.content.trim() === '') return;\n try {\n const metadata = JSON.parse(event.content);\n group.name = metadata.name || group.name;\n group.description = metadata.about || group.description;\n group.picture = metadata.picture || group.picture;\n group.updatedAt = event.created_at * 1000;\n this.groups.set(groupId, group);\n this.persistGroups();\n } catch {\n // Skip malformed metadata\n }\n } else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {\n this.updateMembersFromEvent(groupId, event);\n } else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {\n this.updateAdminsFromEvent(groupId, event);\n }\n }\n\n private handleModerationEvent(event: Event): void {\n const groupId = this.getGroupIdFromEvent(event);\n if (!groupId) return;\n\n const group = this.groups.get(groupId);\n if (!group) return;\n\n if (event.kind === NIP29_KINDS.DELETE_EVENT) {\n const eTags = event.tags.filter((t: string[]) => t[0] === 'e');\n for (const tag of eTags) {\n const messageId = tag[1];\n if (messageId) {\n this.deleteMessageFromMemory(groupId, messageId);\n }\n }\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistMessages();\n } else if (event.kind === NIP29_KINDS.REMOVE_USER) {\n if (this.processedEventIds.has(event.id)) return;\n\n // Ignore events before we joined\n const eventTimestampMs = event.created_at * 1000;\n if (group.localJoinedAt && eventTimestampMs < group.localJoinedAt) {\n this.addProcessedEventId(event.id);\n return;\n }\n\n this.addProcessedEventId(event.id);\n\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n const myPubkey = this.getMyPublicKey();\n\n for (const tag of pTags) {\n const removedPubkey = tag[1];\n if (!removedPubkey) continue;\n\n if (removedPubkey === myPubkey) {\n if (this.pendingLeaves.has(groupId)) {\n // Voluntary leave\n this.pendingLeaves.delete(groupId);\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n } else {\n // Kicked by admin\n const groupName = group.name || groupId;\n this.removeGroupFromMemory(groupId);\n this.deps!.emitEvent('groupchat:kicked', { groupId, groupName });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n }\n } else {\n // Someone else was kicked\n this.removeMemberFromMemory(groupId, removedPubkey);\n }\n }\n this.schedulePersist();\n } else if (event.kind === NIP29_KINDS.DELETE_GROUP) {\n if (this.processedEventIds.has(event.id)) return;\n\n const deleteTimestampMs = event.created_at * 1000;\n if (deleteTimestampMs < group.createdAt) {\n this.addProcessedEventId(event.id);\n return;\n }\n\n this.addProcessedEventId(event.id);\n\n const groupName = group.name || groupId;\n this.removeGroupFromMemory(groupId);\n this.deps!.emitEvent('groupchat:group_deleted', { groupId, groupName });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.schedulePersist();\n }\n }\n\n private updateMembersFromEvent(groupId: string, event: Event): void {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n const existingMembers = this.members.get(groupId) || [];\n\n for (const tag of pTags) {\n const pubkey = tag[1];\n const roleFromTag = tag[3] as GroupRole | undefined;\n const existing = existingMembers.find((m) => m.pubkey === pubkey);\n const role = roleFromTag || existing?.role || GroupRoleEnum.MEMBER;\n\n const member: GroupMemberData = {\n pubkey,\n groupId,\n role,\n nametag: existing?.nametag,\n joinedAt: existing?.joinedAt || event.created_at * 1000,\n };\n\n this.saveMemberToMemory(member);\n }\n this.persistMembers();\n }\n\n private updateAdminsFromEvent(groupId: string, event: Event): void {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n const existingMembers = this.members.get(groupId) || [];\n\n for (const tag of pTags) {\n const pubkey = tag[1];\n const existing = existingMembers.find((m) => m.pubkey === pubkey);\n\n if (existing) {\n existing.role = GroupRoleEnum.ADMIN;\n this.saveMemberToMemory(existing);\n } else {\n this.saveMemberToMemory({\n pubkey,\n groupId,\n role: GroupRoleEnum.ADMIN,\n joinedAt: event.created_at * 1000,\n });\n }\n }\n this.persistMembers();\n }\n\n // ===========================================================================\n // Group Membership Restoration\n // ===========================================================================\n\n private async restoreJoinedGroups(): Promise<GroupData[]> {\n if (!this.client) return [];\n\n const myPubkey = this.getMyPublicKey();\n if (!myPubkey) return [];\n\n const groupIdsWithMembership = new Set<string>();\n\n await this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),\n {\n onEvent: (event: Event) => {\n const groupId = this.getGroupIdFromMetadataEvent(event);\n if (!groupId) return;\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n if (pTags.some((tag: string[]) => tag[1] === myPubkey)) {\n groupIdsWithMembership.add(groupId);\n }\n },\n onComplete: () => {},\n timeoutMs: 15000,\n },\n );\n\n if (groupIdsWithMembership.size === 0) return [];\n\n const restoredGroups: GroupData[] = [];\n\n for (const groupId of groupIdsWithMembership) {\n if (this.groups.has(groupId)) continue;\n\n try {\n const group = await this.fetchGroupMetadataInternal(groupId);\n if (group) {\n this.groups.set(groupId, group);\n restoredGroups.push(group);\n\n await Promise.all([\n this.fetchAndSaveMembers(groupId),\n this.fetchMessages(groupId),\n ]);\n }\n } catch {\n // Skip failed group restoration\n }\n }\n\n if (restoredGroups.length > 0) {\n await this.subscribeToJoinedGroups();\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.schedulePersist();\n }\n\n return restoredGroups;\n }\n\n // ===========================================================================\n // Public API — Groups\n // ===========================================================================\n\n async fetchAvailableGroups(): Promise<GroupData[]> {\n await this.ensureConnected();\n if (!this.client) return [];\n\n const groupsMap = new Map<string, GroupData>();\n const memberCountsMap = new Map<string, number>();\n\n await Promise.all([\n this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),\n {\n onEvent: (event: Event) => {\n const group = this.parseGroupMetadata(event);\n if (group && group.visibility === GroupVisibilityEnum.PUBLIC) {\n const existing = groupsMap.get(group.id);\n if (!existing || group.createdAt > existing.createdAt) {\n groupsMap.set(group.id, group);\n }\n }\n },\n onComplete: () => {},\n timeoutMs: 10000,\n },\n ),\n this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),\n {\n onEvent: (event: Event) => {\n const groupId = this.getGroupIdFromMetadataEvent(event);\n if (groupId) {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n memberCountsMap.set(groupId, pTags.length);\n }\n },\n onComplete: () => {},\n timeoutMs: 10000,\n },\n ),\n ]);\n\n for (const [groupId, count] of memberCountsMap) {\n const group = groupsMap.get(groupId);\n if (group) group.memberCount = count;\n }\n\n return Array.from(groupsMap.values());\n }\n\n async joinGroup(groupId: string, inviteCode?: string): Promise<boolean> {\n await this.ensureConnected();\n if (!this.client) return false;\n\n try {\n let group = await this.fetchGroupMetadataInternal(groupId);\n\n if (!group && !inviteCode) return false;\n\n const tags: string[][] = [['h', groupId]];\n if (inviteCode) tags.push(['code', inviteCode]);\n\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.JOIN_REQUEST,\n tags,\n content: '',\n });\n\n if (eventId) {\n // For hidden groups, fetch metadata now that we're a member\n if (!group) {\n group = await this.fetchGroupMetadataInternal(groupId);\n if (!group) return false;\n }\n\n group.localJoinedAt = Date.now();\n this.groups.set(groupId, group);\n this.subscribeToGroup(groupId);\n\n await Promise.all([\n this.fetchMessages(groupId),\n this.fetchAndSaveMembers(groupId),\n ]);\n\n this.deps!.emitEvent('groupchat:joined', { groupId, groupName: group.name });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistAll();\n return true;\n }\n\n return false;\n } catch (error) {\n // Handle \"already a member\" as success\n const msg = error instanceof Error ? error.message : String(error);\n if (msg.includes('already a member')) {\n const group = await this.fetchGroupMetadataInternal(groupId);\n if (group) {\n group.localJoinedAt = Date.now();\n this.groups.set(groupId, group);\n this.subscribeToGroup(groupId);\n await Promise.all([\n this.fetchMessages(groupId),\n this.fetchAndSaveMembers(groupId),\n ]);\n this.deps!.emitEvent('groupchat:joined', { groupId, groupName: group.name });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistAll();\n return true;\n }\n }\n console.error('[GroupChat] Failed to join group', error);\n return false;\n }\n }\n\n async leaveGroup(groupId: string): Promise<boolean> {\n await this.ensureConnected();\n if (!this.client) return false;\n\n try {\n this.pendingLeaves.add(groupId);\n\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.LEAVE_REQUEST,\n tags: [['h', groupId]],\n content: '',\n });\n\n if (eventId) {\n this.removeGroupFromMemory(groupId);\n this.deps!.emitEvent('groupchat:left', { groupId });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistAll();\n return true;\n }\n\n this.pendingLeaves.delete(groupId);\n return false;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (msg.includes('group not found') || msg.includes('not a member')) {\n this.removeGroupFromMemory(groupId);\n this.persistAll();\n return true;\n }\n console.error('[GroupChat] Failed to leave group', error);\n return false;\n }\n }\n\n async createGroup(options: CreateGroupOptions): Promise<GroupData | null> {\n await this.ensureConnected();\n if (!this.client) return null;\n\n const creatorPubkey = this.getMyPublicKey();\n if (!creatorPubkey) return null;\n\n const proposedGroupId = options.name\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n .slice(0, 20) || this.randomId();\n\n try {\n const isPrivate = options.visibility === GroupVisibilityEnum.PRIVATE;\n\n // Publish CREATE_GROUP first, then fetch the metadata.\n // The relay creates the group synchronously, so by the time the OK\n // response arrives the GROUP_METADATA event is queryable.\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.CREATE_GROUP,\n tags: [['h', proposedGroupId]],\n content: JSON.stringify({\n name: options.name,\n about: options.description,\n picture: options.picture,\n closed: true,\n private: isPrivate,\n hidden: isPrivate,\n }),\n });\n\n if (!eventId) return null;\n\n // Fetch the group metadata the relay created for us\n let group = await this.fetchGroupMetadataInternal(proposedGroupId);\n\n if (!group) {\n // Fallback: build group data from what we know\n group = {\n id: proposedGroupId,\n relayUrl: this.config.relays[0] || '',\n name: options.name,\n description: options.description,\n visibility: options.visibility || GroupVisibilityEnum.PUBLIC,\n createdAt: Date.now(),\n memberCount: 1,\n };\n }\n\n if (!group.name || group.name === 'Unnamed Group') {\n group.name = options.name;\n }\n if (options.description && !group.description) {\n group.description = options.description;\n }\n group.visibility = options.visibility || GroupVisibilityEnum.PUBLIC;\n group.memberCount = 1;\n\n this.groups.set(group.id, group);\n\n this.subscribeToGroup(group.id);\n\n this.client!.createAndPublishEvent({\n kind: NIP29_KINDS.JOIN_REQUEST,\n tags: [['h', group.id]],\n content: '',\n }).catch(() => {});\n\n // Fetch member/admin lists, then ensure creator is always admin.\n // The fetch can return incomplete data if the relay hasn't indexed\n // the admin event yet, so we re-assert after.\n await this.fetchAndSaveMembers(group.id).catch(() => {});\n this.saveMemberToMemory({\n pubkey: creatorPubkey,\n groupId: group.id,\n role: GroupRoleEnum.ADMIN,\n joinedAt: Date.now(),\n });\n\n this.deps!.emitEvent('groupchat:joined', { groupId: group.id, groupName: group.name });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.schedulePersist();\n\n return group;\n } catch (error) {\n console.error('[GroupChat] Failed to create group', error);\n return null;\n }\n }\n\n async deleteGroup(groupId: string): Promise<boolean> {\n await this.ensureConnected();\n if (!this.client) return false;\n\n const group = this.groups.get(groupId);\n if (!group) return false;\n\n // Relay admins can delete public groups; group admins can delete their own groups\n const canDelete = await this.canModerateGroup(groupId);\n if (!canDelete) return false;\n\n try {\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.DELETE_GROUP,\n tags: [['h', groupId]],\n content: '',\n });\n\n if (eventId) {\n const groupName = group.name || groupId;\n this.removeGroupFromMemory(groupId);\n this.deps!.emitEvent('groupchat:group_deleted', { groupId, groupName });\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistAll();\n return true;\n }\n return false;\n } catch (error) {\n console.error('[GroupChat] Failed to delete group', error);\n return false;\n }\n }\n\n async createInvite(groupId: string): Promise<string | null> {\n await this.ensureConnected();\n if (!this.client) return null;\n\n if (!this.isCurrentUserAdmin(groupId)) return null;\n\n try {\n const inviteCode = this.randomId();\n\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.CREATE_INVITE,\n tags: [\n ['h', groupId],\n ['code', inviteCode],\n ],\n content: '',\n });\n\n return eventId ? inviteCode : null;\n } catch (error) {\n console.error('[GroupChat] Failed to create invite', error);\n return null;\n }\n }\n\n // ===========================================================================\n // Public API — Messages\n // ===========================================================================\n\n async sendMessage(\n groupId: string,\n content: string,\n replyToId?: string,\n ): Promise<GroupMessageData | null> {\n await this.ensureConnected();\n if (!this.client) return null;\n\n const group = this.groups.get(groupId);\n if (!group) return null;\n\n try {\n const senderNametag = this.deps!.identity.nametag || null;\n const kind = replyToId ? NIP29_KINDS.THREAD_REPLY : NIP29_KINDS.CHAT_MESSAGE;\n\n const tags: string[][] = [['h', groupId]];\n\n // Add previous message IDs for ordering\n const groupMessages = this.messages.get(groupId) || [];\n const recentIds = groupMessages\n .slice(-this.config.maxPreviousTags)\n .map((m) => (m.id || '').slice(0, 8))\n .filter(Boolean);\n if (recentIds.length > 0) {\n tags.push(['previous', ...recentIds]);\n }\n\n if (replyToId) {\n tags.push(['e', replyToId, '', 'reply']);\n }\n\n const wrappedContent = this.wrapMessageContent(content, senderNametag);\n\n const eventId = await this.client.createAndPublishEvent({\n kind,\n tags,\n content: wrappedContent,\n });\n\n if (eventId) {\n const myPubkey = this.getMyPublicKey();\n const message: GroupMessageData = {\n id: eventId,\n groupId,\n content,\n timestamp: Date.now(),\n senderPubkey: myPubkey || '',\n senderNametag: senderNametag || undefined,\n replyToId,\n previousIds: recentIds,\n };\n\n this.saveMessageToMemory(message);\n this.addProcessedEventId(eventId);\n this.updateGroupLastMessage(groupId, content.slice(0, 100), message.timestamp);\n this.persistAll();\n return message;\n }\n return null;\n } catch (error) {\n console.error('[GroupChat] Failed to send message', error);\n return null;\n }\n }\n\n async fetchMessages(\n groupId: string,\n since?: number,\n limit?: number,\n ): Promise<GroupMessageData[]> {\n await this.ensureConnected();\n if (!this.client) return [];\n\n const fetchedMessages: GroupMessageData[] = [];\n const filterData: Nip29FilterData = {\n kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],\n '#h': [groupId],\n };\n\n if (since) filterData.since = Math.floor(since / 1000);\n if (limit) filterData.limit = limit;\n if (!limit && !since) filterData.limit = this.config.defaultMessageLimit;\n\n return this.oneshotSubscription(createNip29Filter(filterData), {\n onEvent: (event: Event) => {\n const { text: content, senderNametag } = this.unwrapMessageContent(event.content);\n\n const message: GroupMessageData = {\n id: event.id,\n groupId,\n content,\n timestamp: event.created_at * 1000,\n senderPubkey: event.pubkey,\n senderNametag: senderNametag || undefined,\n replyToId: this.extractReplyTo(event),\n previousIds: this.extractPreviousIds(event),\n };\n\n fetchedMessages.push(message);\n this.saveMessageToMemory(message);\n this.addProcessedEventId(event.id);\n\n if (senderNametag) {\n this.updateMemberNametag(groupId, event.pubkey, senderNametag, event.created_at * 1000);\n }\n },\n onComplete: () => {\n this.schedulePersist();\n return fetchedMessages;\n },\n timeoutMs: 10000,\n });\n }\n\n // ===========================================================================\n // Public API — Queries (from local state)\n // ===========================================================================\n\n getGroups(): GroupData[] {\n return Array.from(this.groups.values())\n .sort((a, b) => (b.lastMessageTime || 0) - (a.lastMessageTime || 0));\n }\n\n getGroup(groupId: string): GroupData | null {\n return this.groups.get(groupId) || null;\n }\n\n getMessages(groupId: string): GroupMessageData[] {\n return (this.messages.get(groupId) || [])\n .sort((a, b) => a.timestamp - b.timestamp);\n }\n\n getMembers(groupId: string): GroupMemberData[] {\n return (this.members.get(groupId) || [])\n .sort((a, b) => a.joinedAt - b.joinedAt);\n }\n\n getMember(groupId: string, pubkey: string): GroupMemberData | null {\n const members = this.members.get(groupId) || [];\n return members.find((m) => m.pubkey === pubkey) || null;\n }\n\n getTotalUnreadCount(): number {\n let total = 0;\n for (const group of this.groups.values()) {\n total += group.unreadCount || 0;\n }\n return total;\n }\n\n markGroupAsRead(groupId: string): void {\n const group = this.groups.get(groupId);\n if (group && (group.unreadCount || 0) > 0) {\n group.unreadCount = 0;\n this.groups.set(groupId, group);\n this.persistGroups();\n }\n }\n\n // ===========================================================================\n // Public API — Admin\n // ===========================================================================\n\n async kickUser(groupId: string, userPubkey: string, reason?: string): Promise<boolean> {\n await this.ensureConnected();\n if (!this.client) return false;\n\n const canModerate = await this.canModerateGroup(groupId);\n if (!canModerate) return false;\n\n const myPubkey = this.getMyPublicKey();\n if (myPubkey === userPubkey) return false;\n\n try {\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.REMOVE_USER,\n tags: [['h', groupId], ['p', userPubkey]],\n content: reason || '',\n });\n\n if (eventId) {\n this.removeMemberFromMemory(groupId, userPubkey);\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistMembers();\n return true;\n }\n return false;\n } catch (error) {\n console.error('[GroupChat] Failed to kick user', error);\n return false;\n }\n }\n\n async deleteMessage(groupId: string, messageId: string): Promise<boolean> {\n await this.ensureConnected();\n if (!this.client) return false;\n\n const canModerate = await this.canModerateGroup(groupId);\n if (!canModerate) return false;\n\n try {\n const eventId = await this.client.createAndPublishEvent({\n kind: NIP29_KINDS.DELETE_EVENT,\n tags: [['h', groupId], ['e', messageId]],\n content: '',\n });\n\n if (eventId) {\n this.deleteMessageFromMemory(groupId, messageId);\n this.deps!.emitEvent('groupchat:updated', {} as Record<string, never>);\n this.persistMessages();\n return true;\n }\n return false;\n } catch (error) {\n console.error('[GroupChat] Failed to delete message', error);\n return false;\n }\n }\n\n isCurrentUserAdmin(groupId: string): boolean {\n const myPubkey = this.getMyPublicKey();\n if (!myPubkey) return false;\n\n const member = this.getMember(groupId, myPubkey);\n return member?.role === GroupRoleEnum.ADMIN;\n }\n\n isCurrentUserModerator(groupId: string): boolean {\n const myPubkey = this.getMyPublicKey();\n if (!myPubkey) return false;\n\n const member = this.getMember(groupId, myPubkey);\n return member?.role === GroupRoleEnum.ADMIN || member?.role === GroupRoleEnum.MODERATOR;\n }\n\n /**\n * Check if current user can moderate a group:\n * - Group admin/moderator can always moderate their group\n * - Relay admins can moderate public groups\n */\n async canModerateGroup(groupId: string): Promise<boolean> {\n if (this.isCurrentUserAdmin(groupId) || this.isCurrentUserModerator(groupId)) {\n return true;\n }\n const group = this.groups.get(groupId);\n if (group && group.visibility === GroupVisibilityEnum.PUBLIC) {\n return this.isCurrentUserRelayAdmin();\n }\n return false;\n }\n\n async isCurrentUserRelayAdmin(): Promise<boolean> {\n const myPubkey = this.getMyPublicKey();\n if (!myPubkey) return false;\n\n const admins = await this.fetchRelayAdmins();\n return admins.has(myPubkey);\n }\n\n getCurrentUserRole(groupId: string): GroupRole | null {\n const myPubkey = this.getMyPublicKey();\n if (!myPubkey) return null;\n\n const member = this.getMember(groupId, myPubkey);\n return member?.role || null;\n }\n\n // ===========================================================================\n // Public API — Listeners\n // ===========================================================================\n\n onMessage(handler: (message: GroupMessageData) => void): () => void {\n this.messageHandlers.add(handler);\n return () => this.messageHandlers.delete(handler);\n }\n\n // ===========================================================================\n // Public API — Utilities\n // ===========================================================================\n\n getRelayUrls(): string[] {\n return this.config.relays;\n }\n\n getMyPublicKey(): string | null {\n return this.keyManager?.getPublicKeyHex() || null;\n }\n\n // ===========================================================================\n // Private — Relay Admin\n // ===========================================================================\n\n private async fetchRelayAdmins(): Promise<Set<string>> {\n if (this.relayAdminPubkeys) return this.relayAdminPubkeys;\n if (this.relayAdminFetchPromise) return this.relayAdminFetchPromise;\n\n this.relayAdminFetchPromise = this.doFetchRelayAdmins();\n const result = await this.relayAdminFetchPromise;\n this.relayAdminFetchPromise = null;\n return result;\n }\n\n private async doFetchRelayAdmins(): Promise<Set<string>> {\n await this.ensureConnected();\n if (!this.client) return new Set();\n\n const adminPubkeys = new Set<string>();\n return this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], '#d': ['', '_'] }),\n {\n onEvent: (event: Event) => {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n for (const tag of pTags) {\n if (tag[1]) adminPubkeys.add(tag[1]);\n }\n },\n onComplete: () => {\n this.relayAdminPubkeys = adminPubkeys;\n return adminPubkeys;\n },\n },\n );\n }\n\n // ===========================================================================\n // Private — Fetch Helpers\n // ===========================================================================\n\n private async fetchGroupMetadataInternal(groupId: string): Promise<GroupData | null> {\n if (!this.client) return null;\n\n let result: GroupData | null = null;\n return this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_METADATA], '#d': [groupId] }),\n {\n onEvent: (event: Event) => {\n if (!result) result = this.parseGroupMetadata(event);\n },\n onComplete: () => result,\n },\n );\n }\n\n private async fetchAndSaveMembers(groupId: string): Promise<void> {\n const [members, adminPubkeys] = await Promise.all([\n this.fetchGroupMembersInternal(groupId),\n this.fetchGroupAdminsInternal(groupId),\n ]);\n\n for (const member of members) {\n if (adminPubkeys.includes(member.pubkey)) {\n member.role = GroupRoleEnum.ADMIN;\n }\n this.saveMemberToMemory(member);\n }\n\n // Save admins not in member list\n for (const pubkey of adminPubkeys) {\n const existing = (this.members.get(groupId) || []).find((m) => m.pubkey === pubkey);\n if (!existing) {\n this.saveMemberToMemory({\n pubkey,\n groupId,\n role: GroupRoleEnum.ADMIN,\n joinedAt: Date.now(),\n });\n }\n }\n\n this.persistMembers();\n }\n\n private async fetchGroupMembersInternal(groupId: string): Promise<GroupMemberData[]> {\n if (!this.client) return [];\n\n const members: GroupMemberData[] = [];\n return this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], '#d': [groupId] }),\n {\n onEvent: (event: Event) => {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n for (const tag of pTags) {\n members.push({\n pubkey: tag[1],\n groupId,\n role: (tag[3] as GroupRole) || GroupRoleEnum.MEMBER,\n joinedAt: event.created_at * 1000,\n });\n }\n },\n onComplete: () => members,\n },\n );\n }\n\n private async fetchGroupAdminsInternal(groupId: string): Promise<string[]> {\n if (!this.client) return [];\n\n const adminPubkeys: string[] = [];\n return this.oneshotSubscription(\n new Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], '#d': [groupId] }),\n {\n onEvent: (event: Event) => {\n const pTags = event.tags.filter((t: string[]) => t[0] === 'p');\n for (const tag of pTags) {\n if (tag[1] && !adminPubkeys.includes(tag[1])) {\n adminPubkeys.push(tag[1]);\n }\n }\n },\n onComplete: () => adminPubkeys,\n },\n );\n }\n\n // ===========================================================================\n // Private — In-Memory State Helpers\n // ===========================================================================\n\n private saveMessageToMemory(message: GroupMessageData): void {\n const groupId = message.groupId;\n if (!this.messages.has(groupId)) {\n this.messages.set(groupId, []);\n }\n const msgs = this.messages.get(groupId)!;\n const idx = msgs.findIndex((m) => m.id === message.id);\n if (idx >= 0) {\n msgs[idx] = message;\n } else {\n msgs.push(message);\n // Prune oldest messages beyond limit (keep 2x defaultMessageLimit for scroll-back)\n const maxMessages = this.config.defaultMessageLimit * 2;\n if (msgs.length > maxMessages) {\n msgs.splice(0, msgs.length - maxMessages);\n }\n }\n }\n\n private deleteMessageFromMemory(groupId: string, messageId: string): void {\n const msgs = this.messages.get(groupId);\n if (msgs) {\n const idx = msgs.findIndex((m) => m.id === messageId);\n if (idx >= 0) msgs.splice(idx, 1);\n }\n }\n\n private saveMemberToMemory(member: GroupMemberData): void {\n const groupId = member.groupId;\n if (!this.members.has(groupId)) {\n this.members.set(groupId, []);\n }\n const mems = this.members.get(groupId)!;\n const idx = mems.findIndex((m) => m.pubkey === member.pubkey);\n if (idx >= 0) {\n mems[idx] = member;\n } else {\n mems.push(member);\n }\n }\n\n private removeMemberFromMemory(groupId: string, pubkey: string): void {\n const mems = this.members.get(groupId);\n if (mems) {\n const idx = mems.findIndex((m) => m.pubkey === pubkey);\n if (idx >= 0) mems.splice(idx, 1);\n }\n // Update member count\n const group = this.groups.get(groupId);\n if (group) {\n group.memberCount = (this.members.get(groupId) || []).length;\n this.groups.set(groupId, group);\n }\n }\n\n private removeGroupFromMemory(groupId: string): void {\n this.groups.delete(groupId);\n this.messages.delete(groupId);\n this.members.delete(groupId);\n }\n\n private updateGroupLastMessage(groupId: string, text: string, timestamp: number): void {\n const group = this.groups.get(groupId);\n if (group && timestamp >= (group.lastMessageTime || 0)) {\n group.lastMessageText = text;\n group.lastMessageTime = timestamp;\n this.groups.set(groupId, group);\n }\n }\n\n private updateMemberNametag(\n groupId: string,\n pubkey: string,\n nametag: string,\n joinedAt: number,\n ): void {\n const members = this.members.get(groupId) || [];\n const existing = members.find((m) => m.pubkey === pubkey);\n\n if (existing) {\n if (existing.nametag !== nametag) {\n existing.nametag = nametag;\n this.saveMemberToMemory(existing);\n }\n } else {\n this.saveMemberToMemory({\n pubkey,\n groupId,\n role: GroupRoleEnum.MEMBER,\n nametag,\n joinedAt,\n });\n }\n }\n\n private addProcessedEventId(eventId: string): void {\n this.processedEventIds.add(eventId);\n // Keep max 10,000\n if (this.processedEventIds.size > 10000) {\n const arr = Array.from(this.processedEventIds);\n this.processedEventIds = new Set(arr.slice(arr.length - 10000));\n }\n }\n\n // ===========================================================================\n // Private — Persistence\n // ===========================================================================\n\n /** Schedule a debounced persist (coalesces rapid event bursts). */\n private schedulePersist(): void {\n if (this.persistTimer) return; // Already scheduled\n this.persistTimer = setTimeout(() => {\n this.persistTimer = null;\n this.persistPromise = this.doPersistAll().catch((err) => {\n console.error('[GroupChat] Persistence error:', err);\n }).finally(() => {\n this.persistPromise = null;\n });\n }, 200);\n }\n\n /** Persist immediately (for explicit flush points). */\n private async persistAll(): Promise<void> {\n // Wait for any pending debounced persist\n if (this.persistTimer) {\n clearTimeout(this.persistTimer);\n this.persistTimer = null;\n }\n if (this.persistPromise) {\n await this.persistPromise;\n }\n await this.doPersistAll();\n }\n\n private async doPersistAll(): Promise<void> {\n await Promise.all([\n this.persistGroups(),\n this.persistMessages(),\n this.persistMembers(),\n this.persistProcessedEvents(),\n ]);\n }\n\n private async persistGroups(): Promise<void> {\n if (!this.deps) return;\n const data = Array.from(this.groups.values());\n await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_GROUPS, JSON.stringify(data));\n }\n\n private async persistMessages(): Promise<void> {\n if (!this.deps) return;\n const allMessages: GroupMessageData[] = [];\n for (const msgs of this.messages.values()) {\n allMessages.push(...msgs);\n }\n await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MESSAGES, JSON.stringify(allMessages));\n }\n\n private async persistMembers(): Promise<void> {\n if (!this.deps) return;\n const allMembers: GroupMemberData[] = [];\n for (const mems of this.members.values()) {\n allMembers.push(...mems);\n }\n await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_MEMBERS, JSON.stringify(allMembers));\n }\n\n private async persistProcessedEvents(): Promise<void> {\n if (!this.deps) return;\n const arr = Array.from(this.processedEventIds);\n await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_PROCESSED_EVENTS, JSON.stringify(arr));\n }\n\n // ===========================================================================\n // Private — Relay URL Change Detection\n // ===========================================================================\n\n private async checkAndClearOnRelayChange(currentRelayUrl: string): Promise<void> {\n if (!this.deps) return;\n\n const stored = await this.deps.storage.get(STORAGE_KEYS_GLOBAL.GROUP_CHAT_RELAY_URL);\n\n if (stored && stored !== currentRelayUrl) {\n // Relay changed — clear stale data\n this.groups.clear();\n this.messages.clear();\n this.members.clear();\n this.processedEventIds.clear();\n await this.persistAll();\n }\n\n // Also check if stored groups have different relay URL\n if (!stored) {\n for (const group of this.groups.values()) {\n if (group.relayUrl && group.relayUrl !== currentRelayUrl) {\n this.groups.clear();\n this.messages.clear();\n this.members.clear();\n this.processedEventIds.clear();\n await this.persistAll();\n break;\n }\n }\n }\n\n await this.deps.storage.set(STORAGE_KEYS_GLOBAL.GROUP_CHAT_RELAY_URL, currentRelayUrl);\n }\n\n // ===========================================================================\n // Private — Message Content Wrapping\n // ===========================================================================\n\n private wrapMessageContent(content: string, senderNametag: string | null): string {\n if (senderNametag) {\n return JSON.stringify({ senderNametag, text: content });\n }\n return content;\n }\n\n private unwrapMessageContent(content: string): { text: string; senderNametag: string | null } {\n try {\n const parsed = JSON.parse(content);\n if (typeof parsed === 'object' && parsed.text !== undefined) {\n return { text: parsed.text, senderNametag: parsed.senderNametag || null };\n }\n } catch {\n // Not JSON\n }\n return { text: content, senderNametag: null };\n }\n\n // ===========================================================================\n // Private — Event Tag Helpers\n // ===========================================================================\n\n private getGroupIdFromEvent(event: Event): string | null {\n const hTag = event.tags.find((t: string[]) => t[0] === 'h');\n return hTag ? hTag[1] : null;\n }\n\n private getGroupIdFromMetadataEvent(event: Event): string | null {\n const dTag = event.tags.find((t: string[]) => t[0] === 'd');\n if (dTag?.[1]) return dTag[1];\n const hTag = event.tags.find((t: string[]) => t[0] === 'h');\n return hTag?.[1] ?? null;\n }\n\n private extractReplyTo(event: Event): string | undefined {\n const eTag = event.tags.find((t: string[]) => t[0] === 'e' && t[3] === 'reply');\n return eTag ? eTag[1] : undefined;\n }\n\n private extractPreviousIds(event: Event): string[] | undefined {\n const previousTag = event.tags.find((t: string[]) => t[0] === 'previous');\n return previousTag ? previousTag.slice(1) : undefined;\n }\n\n private parseGroupMetadata(event: Event): GroupData | null {\n try {\n const groupId = this.getGroupIdFromMetadataEvent(event);\n if (!groupId) return null;\n\n let name = 'Unnamed Group';\n let description: string | undefined;\n let picture: string | undefined;\n let isPrivate = false;\n\n if (event.content && event.content.trim()) {\n try {\n const metadata = JSON.parse(event.content);\n name = metadata.name || name;\n description = metadata.about || metadata.description;\n picture = metadata.picture;\n isPrivate = metadata.private === true;\n } catch {\n // Not JSON, check tags\n }\n }\n\n for (const tag of event.tags) {\n if (tag[0] === 'name' && tag[1]) name = tag[1];\n if (tag[0] === 'about' && tag[1]) description = tag[1];\n if (tag[0] === 'picture' && tag[1]) picture = tag[1];\n if (tag[0] === 'private') isPrivate = true;\n if (tag[0] === 'public' && tag[1] === 'false') isPrivate = true;\n }\n\n return {\n id: groupId,\n relayUrl: this.config.relays[0] || '',\n name,\n description,\n picture,\n visibility: isPrivate ? GroupVisibilityEnum.PRIVATE : GroupVisibilityEnum.PUBLIC,\n createdAt: event.created_at * 1000,\n };\n } catch {\n return null;\n }\n }\n\n // ===========================================================================\n // Private — Utility\n // ===========================================================================\n\n /** Subscribe and track the subscription ID for cleanup. */\n private trackSubscription(filter: Filter, handlers: { onEvent: (event: Event) => void; onEndOfStoredEvents?: () => void }): string {\n const subId = this.client!.subscribe(filter, {\n onEvent: handlers.onEvent,\n onEndOfStoredEvents: handlers.onEndOfStoredEvents ?? (() => {}),\n });\n this.subscriptionIds.push(subId);\n return subId;\n }\n\n /** Subscribe for a one-shot fetch, auto-unsubscribe on EOSE or timeout. */\n private oneshotSubscription<T>(\n filter: Filter,\n opts: {\n onEvent: (event: Event) => void;\n onComplete: () => T;\n timeoutMs?: number;\n },\n ): Promise<T> {\n return new Promise((resolve) => {\n let done = false;\n let subId: string | undefined;\n\n const finish = () => {\n if (done) return;\n done = true;\n if (subId) {\n try { this.client!.unsubscribe(subId); } catch { /* ignore */ }\n const idx = this.subscriptionIds.indexOf(subId);\n if (idx >= 0) this.subscriptionIds.splice(idx, 1);\n }\n resolve(opts.onComplete());\n };\n\n subId = this.client!.subscribe(filter, {\n onEvent: (event: Event) => { if (!done) opts.onEvent(event); },\n onEndOfStoredEvents: finish,\n });\n this.subscriptionIds.push(subId);\n\n setTimeout(finish, opts.timeoutMs ?? 5000);\n });\n }\n\n private ensureInitialized(): void {\n if (!this.deps) {\n throw new Error('GroupChatModule not initialized');\n }\n }\n\n private async ensureConnected(): Promise<void> {\n if (!this.connected) {\n await this.connect();\n }\n }\n\n private randomId(): string {\n const bytes = new Uint8Array(8);\n if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\nexport function createGroupChatModule(config?: GroupChatModuleConfig): GroupChatModule {\n return new GroupChatModule(config);\n}\n","/**\n * Group Chat Types (NIP-29)\n * Plain interfaces for SDK consumers — no classes, no UI helpers.\n */\n\n// =============================================================================\n// Enums\n// =============================================================================\n\nexport const GroupRole = {\n ADMIN: 'ADMIN',\n MODERATOR: 'MODERATOR',\n MEMBER: 'MEMBER',\n} as const;\n\nexport type GroupRole = (typeof GroupRole)[keyof typeof GroupRole];\n\nexport const GroupVisibility = {\n PUBLIC: 'PUBLIC',\n PRIVATE: 'PRIVATE',\n} as const;\n\nexport type GroupVisibility = (typeof GroupVisibility)[keyof typeof GroupVisibility];\n\n// =============================================================================\n// Data Interfaces\n// =============================================================================\n\nexport interface GroupData {\n id: string;\n relayUrl: string;\n name: string;\n description?: string;\n picture?: string;\n visibility: GroupVisibility;\n createdAt: number;\n updatedAt?: number;\n memberCount?: number;\n unreadCount?: number;\n lastMessageTime?: number;\n lastMessageText?: string;\n /** When the current user joined this group locally (used to filter old events) */\n localJoinedAt?: number;\n}\n\nexport interface GroupMessageData {\n id?: string;\n groupId: string;\n content: string;\n timestamp: number;\n senderPubkey: string;\n senderNametag?: string;\n replyToId?: string;\n previousIds?: string[];\n}\n\nexport interface GroupMemberData {\n pubkey: string;\n groupId: string;\n role: GroupRole;\n nametag?: string;\n joinedAt: number;\n}\n\n// =============================================================================\n// Configuration\n// =============================================================================\n\nexport interface GroupChatModuleConfig {\n /** Override relay URLs (default: from network config) */\n relays?: string[];\n /** Default message fetch limit (default: 50) */\n defaultMessageLimit?: number;\n /** Max previous message IDs in ordering tags (default: 3) */\n maxPreviousTags?: number;\n /** Reconnect delay in ms (default: 3000) */\n reconnectDelayMs?: number;\n /** Max reconnect attempts (default: 5) */\n maxReconnectAttempts?: number;\n}\n\nexport interface CreateGroupOptions {\n name: string;\n description?: string;\n picture?: string;\n visibility?: GroupVisibility;\n}\n","/**\n * Encryption utilities for SDK2\n *\n * Provides AES-256 encryption for sensitive wallet data.\n * Uses crypto-js for cross-platform compatibility.\n */\n\nimport CryptoJS from 'crypto-js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EncryptedData {\n /** Encrypted ciphertext (base64) */\n ciphertext: string;\n /** Initialization vector (hex) */\n iv: string;\n /** Salt used for key derivation (hex) */\n salt: string;\n /** Algorithm identifier */\n algorithm: 'aes-256-cbc';\n /** Key derivation function */\n kdf: 'pbkdf2';\n /** Number of PBKDF2 iterations */\n iterations: number;\n}\n\nexport interface EncryptionOptions {\n /** Number of PBKDF2 iterations (default: 100000) */\n iterations?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default number of PBKDF2 iterations */\nconst DEFAULT_ITERATIONS = 100000;\n\n/** AES key size in bits */\nconst KEY_SIZE = 256;\n\n/** Salt size in bytes */\nconst SALT_SIZE = 16;\n\n/** IV size in bytes */\nconst IV_SIZE = 16;\n\n// =============================================================================\n// Key Derivation\n// =============================================================================\n\n/**\n * Derive encryption key from password using PBKDF2\n * @param password - User password\n * @param salt - Salt as WordArray\n * @param iterations - Number of iterations\n */\nfunction deriveKey(\n password: string,\n salt: CryptoJS.lib.WordArray,\n iterations: number\n): CryptoJS.lib.WordArray {\n return CryptoJS.PBKDF2(password, salt, {\n keySize: KEY_SIZE / 32, // WordArray uses 32-bit words\n iterations,\n hasher: CryptoJS.algo.SHA256,\n });\n}\n\n// =============================================================================\n// Encryption Functions\n// =============================================================================\n\n/**\n * Encrypt data with AES-256-CBC\n * @param plaintext - Data to encrypt (string or object)\n * @param password - Encryption password\n * @param options - Encryption options\n */\nexport function encrypt(\n plaintext: string | object,\n password: string,\n options: EncryptionOptions = {}\n): EncryptedData {\n const iterations = options.iterations ?? DEFAULT_ITERATIONS;\n\n // Convert object to JSON string if needed\n const data = typeof plaintext === 'string' ? plaintext : JSON.stringify(plaintext);\n\n // Generate random salt and IV\n const salt = CryptoJS.lib.WordArray.random(SALT_SIZE);\n const iv = CryptoJS.lib.WordArray.random(IV_SIZE);\n\n // Derive key from password\n const key = deriveKey(password, salt, iterations);\n\n // Encrypt with AES-256-CBC\n const encrypted = CryptoJS.AES.encrypt(data, key, {\n iv,\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n });\n\n return {\n ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64),\n iv: iv.toString(CryptoJS.enc.Hex),\n salt: salt.toString(CryptoJS.enc.Hex),\n algorithm: 'aes-256-cbc',\n kdf: 'pbkdf2',\n iterations,\n };\n}\n\n/**\n * Decrypt AES-256-CBC encrypted data\n * @param encryptedData - Encrypted data object\n * @param password - Decryption password\n */\nexport function decrypt(encryptedData: EncryptedData, password: string): string {\n // Parse salt and IV\n const salt = CryptoJS.enc.Hex.parse(encryptedData.salt);\n const iv = CryptoJS.enc.Hex.parse(encryptedData.iv);\n\n // Derive key from password\n const key = deriveKey(password, salt, encryptedData.iterations);\n\n // Parse ciphertext\n const ciphertext = CryptoJS.enc.Base64.parse(encryptedData.ciphertext);\n\n // Create cipher params\n const cipherParams = CryptoJS.lib.CipherParams.create({\n ciphertext,\n });\n\n // Decrypt\n const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {\n iv,\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n });\n\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n\n if (!result) {\n throw new Error('Decryption failed: invalid password or corrupted data');\n }\n\n return result;\n}\n\n/**\n * Decrypt and parse JSON data\n * @param encryptedData - Encrypted data object\n * @param password - Decryption password\n */\nexport function decryptJson<T = unknown>(encryptedData: EncryptedData, password: string): T {\n const decrypted = decrypt(encryptedData, password);\n try {\n return JSON.parse(decrypted) as T;\n } catch {\n throw new Error('Decryption failed: invalid JSON data');\n }\n}\n\n// =============================================================================\n// Simple Encryption (Password-based, for localStorage)\n// =============================================================================\n\n/**\n * Simple encryption using CryptoJS built-in password-based encryption\n * Suitable for localStorage where we don't need full EncryptedData metadata\n * @param plaintext - Data to encrypt\n * @param password - Encryption password\n */\nexport function encryptSimple(plaintext: string, password: string): string {\n return CryptoJS.AES.encrypt(plaintext, password).toString();\n}\n\n/**\n * Simple decryption\n * @param ciphertext - Encrypted string\n * @param password - Decryption password\n */\nexport function decryptSimple(ciphertext: string, password: string): string {\n const decrypted = CryptoJS.AES.decrypt(ciphertext, password);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n\n if (!result) {\n throw new Error('Decryption failed: invalid password or corrupted data');\n }\n\n return result;\n}\n\n/**\n * Decrypt data encrypted with PBKDF2-derived key (legacy JSON wallet format).\n * Compatible with webwallet's encryptWithPassword/decryptWithPassword.\n */\nexport function decryptWithSalt(ciphertext: string, password: string, salt: string): string | null {\n try {\n const key = CryptoJS.PBKDF2(password, salt, {\n keySize: 256 / 32,\n iterations: 100000,\n hasher: CryptoJS.algo.SHA256,\n }).toString();\n const decrypted = CryptoJS.AES.decrypt(ciphertext, key);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n return result || null;\n } catch {\n return null;\n }\n}\n\n// =============================================================================\n// Mnemonic Encryption (Compatible with existing wallet format)\n// =============================================================================\n\n/**\n * Encrypt mnemonic phrase for storage\n * Uses simple AES encryption compatible with existing wallet format\n * @param mnemonic - BIP39 mnemonic phrase\n * @param password - Encryption password\n */\nexport function encryptMnemonic(mnemonic: string, password: string): string {\n return encryptSimple(mnemonic, password);\n}\n\n/**\n * Decrypt mnemonic phrase from storage\n * @param encryptedMnemonic - Encrypted mnemonic string\n * @param password - Decryption password\n */\nexport function decryptMnemonic(encryptedMnemonic: string, password: string): string {\n return decryptSimple(encryptedMnemonic, password);\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Check if data looks like an EncryptedData object\n */\nexport function isEncryptedData(data: unknown): data is EncryptedData {\n if (typeof data !== 'object' || data === null) {\n return false;\n }\n const obj = data as Record<string, unknown>;\n return (\n typeof obj.ciphertext === 'string' &&\n typeof obj.iv === 'string' &&\n typeof obj.salt === 'string' &&\n obj.algorithm === 'aes-256-cbc' &&\n obj.kdf === 'pbkdf2' &&\n typeof obj.iterations === 'number'\n );\n}\n\n/**\n * Serialize EncryptedData to string for storage\n */\nexport function serializeEncrypted(data: EncryptedData): string {\n return JSON.stringify(data);\n}\n\n/**\n * Deserialize EncryptedData from string\n */\nexport function deserializeEncrypted(serialized: string): EncryptedData {\n const parsed = JSON.parse(serialized);\n if (!isEncryptedData(parsed)) {\n throw new Error('Invalid encrypted data format');\n }\n return parsed;\n}\n\n/**\n * Generate a random password/key as hex string\n * @param bytes - Number of random bytes (default: 32)\n */\nexport function generateRandomKey(bytes: number = 32): string {\n return CryptoJS.lib.WordArray.random(bytes).toString(CryptoJS.enc.Hex);\n}\n","/**\n * Address Scanning — Derive HD addresses and check L1 balances via Fulcrum.\n *\n * Used after importing BIP32/.dat wallets to discover which addresses have funds.\n */\n\nimport type { AddressInfo } from './crypto';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** Progress callback for address scanning */\nexport interface ScanAddressProgress {\n /** Number of addresses scanned so far */\n scanned: number;\n /** Total addresses to scan (maxAddresses * chains) */\n total: number;\n /** Current address being checked */\n currentAddress: string;\n /** Number of addresses found with balance */\n foundCount: number;\n /** Current gap count (consecutive empty addresses) */\n currentGap: number;\n /** Number of found addresses that have a nametag */\n nametagsFoundCount: number;\n}\n\n/** Single scanned address result */\nexport interface ScannedAddressResult {\n /** HD derivation index */\n index: number;\n /** L1 bech32 address (alpha1...) */\n address: string;\n /** Full BIP32 derivation path */\n path: string;\n /** L1 balance in ALPHA */\n balance: number;\n /** Whether this is a change address (chain 1) */\n isChange: boolean;\n /** Nametag associated with this address (resolved during scan) */\n nametag?: string;\n}\n\n/** Options for scanning addresses */\nexport interface ScanAddressesOptions {\n /** Maximum number of addresses to scan per chain (default: 50) */\n maxAddresses?: number;\n /** Stop after this many consecutive 0-balance addresses (default: 20) */\n gapLimit?: number;\n /** Also scan change addresses (chain 1) (default: true) */\n includeChange?: boolean;\n /** Progress callback */\n onProgress?: (progress: ScanAddressProgress) => void;\n /** Abort signal for cancellation */\n signal?: AbortSignal;\n /** Resolve nametag for a found address. Return nametag string or null. */\n resolveNametag?: (l1Address: string) => Promise<string | null>;\n}\n\n/** Result of scanning */\nexport interface ScanAddressesResult {\n /** All addresses found with non-zero balance */\n addresses: ScannedAddressResult[];\n /** Total balance across all found addresses (in ALPHA) */\n totalBalance: number;\n /** Number of addresses actually scanned */\n scannedCount: number;\n}\n\n// =============================================================================\n// Implementation\n// =============================================================================\n\n/**\n * Scan blockchain addresses to discover used addresses with balances.\n * Derives addresses sequentially and checks L1 balance via Fulcrum.\n * Uses gap limit to stop after N consecutive empty addresses.\n *\n * @param deriveAddress - Function to derive an address at a given index\n * @param options - Scanning options\n * @returns Scan results with found addresses and total balance\n */\nexport async function scanAddressesImpl(\n deriveAddress: (index: number, isChange: boolean) => AddressInfo,\n options: ScanAddressesOptions = {},\n): Promise<ScanAddressesResult> {\n const maxAddresses = options.maxAddresses ?? 50;\n const gapLimit = options.gapLimit ?? 20;\n const includeChange = options.includeChange ?? true;\n const { onProgress, signal, resolveNametag } = options;\n\n // Dynamic import to avoid hard dependency on L1 for non-L1 consumers\n const { connect, getBalance } = await import('../l1/network');\n await connect();\n\n const foundAddresses: ScannedAddressResult[] = [];\n let totalBalance = 0;\n let totalScanned = 0;\n let nametagsFoundCount = 0;\n\n const chains: boolean[] = includeChange ? [false, true] : [false];\n const totalToScan = maxAddresses * chains.length;\n\n for (const isChange of chains) {\n let consecutiveEmpty = 0;\n\n for (let index = 0; index < maxAddresses; index++) {\n if (signal?.aborted) break;\n\n const addrInfo = deriveAddress(index, isChange);\n totalScanned++;\n\n onProgress?.({\n scanned: totalScanned,\n total: totalToScan,\n currentAddress: addrInfo.address,\n foundCount: foundAddresses.length,\n currentGap: consecutiveEmpty,\n nametagsFoundCount,\n });\n\n try {\n const balance = await getBalance(addrInfo.address);\n\n if (balance > 0) {\n // Resolve nametag for addresses with balance\n let nametag: string | undefined;\n if (resolveNametag) {\n try {\n const tag = await resolveNametag(addrInfo.address);\n if (tag) {\n nametag = tag;\n nametagsFoundCount++;\n }\n } catch {\n // Nametag resolution failure is non-fatal\n }\n }\n\n foundAddresses.push({\n index,\n address: addrInfo.address,\n path: addrInfo.path,\n balance,\n isChange,\n nametag,\n });\n totalBalance += balance;\n consecutiveEmpty = 0;\n } else {\n consecutiveEmpty++;\n }\n } catch (err) {\n // Network error — count as empty to avoid hanging\n console.warn(`[scanAddresses] Error checking ${addrInfo.address}:`, err);\n consecutiveEmpty++;\n }\n\n if (consecutiveEmpty >= gapLimit) {\n break;\n }\n\n // Yield every 5 addresses to keep UI responsive\n if (totalScanned % 5 === 0) {\n await new Promise(resolve => setTimeout(resolve, 0));\n }\n }\n\n if (signal?.aborted) break;\n }\n\n return {\n addresses: foundAddresses,\n totalBalance,\n scannedCount: totalScanned,\n };\n}\n","/**\n * Wallet Text Format Parsing\n *\n * Parses text-based wallet backup files (UNICITY WALLET DETAILS format)\n */\n\nimport CryptoJS from 'crypto-js';\nimport type { LegacyFileParseResult, LegacyFileParsedData } from './types';\nimport { extractFromText } from '../core/utils';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst WALLET_HEADER = 'UNICITY WALLET DETAILS';\n\n// Legacy webwallet encryption parameters (for backwards compatibility)\nconst LEGACY_SALT = 'alpha_wallet_salt';\nconst LEGACY_ITERATIONS = 100000;\n\n// =============================================================================\n// Encryption/Decryption\n// =============================================================================\n\n/**\n * Derive encryption key using original webwallet parameters\n */\nfunction deriveLegacyKey(password: string): string {\n return CryptoJS.PBKDF2(password, LEGACY_SALT, {\n keySize: 256 / 32,\n iterations: LEGACY_ITERATIONS,\n hasher: CryptoJS.algo.SHA1,\n }).toString();\n}\n\n/**\n * Decrypt master key from text format\n */\nexport function decryptTextFormatKey(encryptedKey: string, password: string): string | null {\n try {\n const key = deriveLegacyKey(password);\n const decrypted = CryptoJS.AES.decrypt(encryptedKey, key);\n const result = decrypted.toString(CryptoJS.enc.Utf8);\n return result || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Encrypt master key for text format export\n */\nexport function encryptForTextFormat(masterPrivateKey: string, password: string): string {\n const key = deriveLegacyKey(password);\n return CryptoJS.AES.encrypt(masterPrivateKey, key).toString();\n}\n\n// =============================================================================\n// Export Types\n// =============================================================================\n\nexport interface WalletTextExportParams {\n masterPrivateKey: string;\n masterPrivateKeyWIF?: string;\n chainCode?: string;\n descriptorPath?: string;\n isBIP32: boolean;\n addresses: Array<{\n index: number;\n address: string;\n path?: string;\n isChange?: boolean;\n }>;\n}\n\nexport interface WalletTextExportOptions {\n password?: string;\n}\n\n// =============================================================================\n// Serialization Functions\n// =============================================================================\n\n/**\n * Format addresses for text export\n */\nfunction formatAddresses(\n addresses: WalletTextExportParams['addresses'],\n isBIP32: boolean\n): string {\n return addresses\n .map((addr, index) => {\n const path = addr.path || (isBIP32\n ? `m/84'/1'/0'/${addr.isChange ? 1 : 0}/${addr.index}`\n : `m/44'/0'/${addr.index}'`);\n return `Address ${index + 1}: ${addr.address} (Path: ${path})`;\n })\n .join('\\n');\n}\n\n/**\n * Serialize wallet to text format (unencrypted)\n */\nexport function serializeWalletToText(params: WalletTextExportParams): string {\n const {\n masterPrivateKey,\n masterPrivateKeyWIF,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n } = params;\n\n const addressesText = formatAddresses(addresses, isBIP32);\n\n let masterKeySection: string;\n\n if (isBIP32 && chainCode) {\n masterKeySection = `MASTER PRIVATE KEY (keep secret!):\n${masterPrivateKey}\n${masterPrivateKeyWIF ? `\\nMASTER PRIVATE KEY IN WIF FORMAT (for importprivkey command):\\n${masterPrivateKeyWIF}\\n` : ''}\nMASTER CHAIN CODE (for BIP32 HD wallet compatibility):\n${chainCode}\n\nDESCRIPTOR PATH: ${descriptorPath || \"84'/1'/0'\"}\n\nWALLET TYPE: BIP32 hierarchical deterministic wallet\n\nENCRYPTION STATUS: Not encrypted\nThis key is in plaintext and not protected. Anyone with this file can access your wallet.`;\n } else {\n masterKeySection = `MASTER PRIVATE KEY (keep secret!):\n${masterPrivateKey}\n${masterPrivateKeyWIF ? `\\nMASTER PRIVATE KEY IN WIF FORMAT (for importprivkey command):\\n${masterPrivateKeyWIF}\\n` : ''}\nWALLET TYPE: Standard wallet (HMAC-based)\n\nENCRYPTION STATUS: Not encrypted\nThis key is in plaintext and not protected. Anyone with this file can access your wallet.`;\n }\n\n return `${WALLET_HEADER}\n===========================\n\n${masterKeySection}\n\nYOUR ADDRESSES:\n${addressesText}\n\nGenerated on: ${new Date().toLocaleString()}\n\nWARNING: Keep your master private key safe and secure.\nAnyone with your master private key can access all your funds.`;\n}\n\n/**\n * Serialize wallet to text format (encrypted)\n */\nexport function serializeEncryptedWalletToText(params: {\n encryptedMasterKey: string;\n chainCode?: string;\n descriptorPath?: string;\n isBIP32: boolean;\n addresses: WalletTextExportParams['addresses'];\n}): string {\n const { encryptedMasterKey, chainCode, descriptorPath, isBIP32, addresses } = params;\n\n const addressesText = formatAddresses(addresses, isBIP32);\n\n let encryptedContent = `ENCRYPTED MASTER KEY (password protected):\n${encryptedMasterKey}`;\n\n if (isBIP32 && chainCode) {\n encryptedContent += `\n\nMASTER CHAIN CODE (for BIP32 HD wallet compatibility):\n${chainCode}\n\nDESCRIPTOR PATH: ${descriptorPath || \"84'/1'/0'\"}\n\nWALLET TYPE: BIP32 hierarchical deterministic wallet`;\n } else {\n encryptedContent += `\n\nWALLET TYPE: Standard wallet (HMAC-based)`;\n }\n\n return `${WALLET_HEADER}\n===========================\n\n${encryptedContent}\n\nENCRYPTION STATUS: Encrypted with password\nTo use this key, you will need the password you set in the wallet.\n\nYOUR ADDRESSES:\n${addressesText}\n\nGenerated on: ${new Date().toLocaleString()}\n\nWARNING: Keep your master private key safe and secure.\nAnyone with your master private key can access all your funds.`;\n}\n\n// =============================================================================\n// Detection\n// =============================================================================\n\n/**\n * Check if content is wallet text format\n */\nexport function isWalletTextFormat(content: string): boolean {\n return (\n content.includes(WALLET_HEADER) &&\n (content.includes('MASTER PRIVATE KEY') || content.includes('ENCRYPTED MASTER KEY'))\n );\n}\n\n/**\n * Check if text wallet is encrypted\n */\nexport function isTextWalletEncrypted(content: string): boolean {\n return content.includes('ENCRYPTED MASTER KEY');\n}\n\n// =============================================================================\n// Parse Functions\n// =============================================================================\n\n/**\n * Parse wallet from text format (unencrypted)\n */\nexport function parseWalletText(content: string): LegacyFileParseResult {\n try {\n const isEncrypted = isTextWalletEncrypted(content);\n\n if (isEncrypted) {\n // Extract encrypted key - caller needs to decrypt\n const encryptedKey = extractFromText(\n content,\n /ENCRYPTED MASTER KEY \\(password protected\\):\\s*([^\\n]+)/\n );\n\n if (!encryptedKey) {\n return {\n success: false,\n error: 'Could not find encrypted master key in backup file',\n };\n }\n\n // Return with needsPassword flag\n return {\n success: false,\n needsPassword: true,\n error: 'Password required for encrypted wallet',\n };\n }\n\n // Extract unencrypted master key\n const masterKey = extractFromText(\n content,\n /MASTER PRIVATE KEY \\(keep secret!\\):\\s*([^\\n]+)/\n );\n\n if (!masterKey) {\n return {\n success: false,\n error: 'Could not find master private key in backup file',\n };\n }\n\n // Extract chain code\n const chainCode = extractFromText(\n content,\n /MASTER CHAIN CODE \\(for (?:BIP32 HD|Alpha) wallet compatibility\\):\\s*([^\\n]+)/\n );\n\n // Extract descriptor path\n const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\\s*([^\\n]+)/);\n\n // Determine derivation mode\n const isBIP32 =\n content.includes('WALLET TYPE: BIP32 hierarchical deterministic wallet') ||\n content.includes('WALLET TYPE: Alpha descriptor wallet') ||\n !!chainCode;\n\n // BIP32 wallets without explicit descriptor path default to 84'/1'/0' (Alpha network standard).\n // The webwallet exports omit DESCRIPTOR PATH for encrypted files, so we must infer it.\n const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? \"84'/1'/0'\" : undefined);\n\n const data: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: effectiveDescriptorPath,\n derivationMode: isBIP32 ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data,\n };\n } catch (e) {\n return {\n success: false,\n error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n\n/**\n * Parse and decrypt wallet from text format\n */\nexport function parseAndDecryptWalletText(\n content: string,\n password: string\n): LegacyFileParseResult {\n try {\n const isEncrypted = isTextWalletEncrypted(content);\n\n if (!isEncrypted) {\n // Not encrypted, parse directly\n return parseWalletText(content);\n }\n\n // Extract encrypted key\n const encryptedKey = extractFromText(\n content,\n /ENCRYPTED MASTER KEY \\(password protected\\):\\s*([^\\n]+)/\n );\n\n if (!encryptedKey) {\n return {\n success: false,\n error: 'Could not find encrypted master key in backup file',\n };\n }\n\n // Decrypt\n const masterKey = decryptTextFormatKey(encryptedKey, password);\n\n if (!masterKey) {\n return {\n success: false,\n error: 'Failed to decrypt - incorrect password?',\n };\n }\n\n // Extract chain code (not encrypted)\n const chainCode = extractFromText(\n content,\n /MASTER CHAIN CODE \\(for (?:BIP32 HD|Alpha) wallet compatibility\\):\\s*([^\\n]+)/\n );\n\n // Extract descriptor path\n const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\\s*([^\\n]+)/);\n\n // Determine derivation mode\n const isBIP32 =\n content.includes('WALLET TYPE: BIP32 hierarchical deterministic wallet') ||\n content.includes('WALLET TYPE: Alpha descriptor wallet') ||\n !!chainCode;\n\n // BIP32 wallets without explicit descriptor path default to 84'/1'/0' (Alpha network standard).\n // The webwallet exports omit DESCRIPTOR PATH for encrypted files, so we must infer it.\n const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? \"84'/1'/0'\" : undefined);\n\n const data: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: effectiveDescriptorPath,\n derivationMode: isBIP32 ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data,\n };\n } catch (e) {\n return {\n success: false,\n error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n","/**\n * SDK Utility Functions\n * Pure utility functions for the wallet SDK\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** secp256k1 curve order */\nconst SECP256K1_ORDER = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141');\n\n/** Base58 alphabet */\nconst BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\n\n// =============================================================================\n// Private Key Validation\n// =============================================================================\n\n/**\n * Validate if a hex string is a valid secp256k1 private key\n * Must be 0 < key < n (curve order)\n */\nexport function isValidPrivateKey(hex: string): boolean {\n try {\n if (!/^[0-9a-fA-F]{64}$/.test(hex)) {\n return false;\n }\n const key = BigInt('0x' + hex);\n return key > 0n && key < SECP256K1_ORDER;\n } catch {\n return false;\n }\n}\n\n// =============================================================================\n// Base58 Encoding/Decoding\n// =============================================================================\n\n/**\n * Base58 encode hex string\n *\n * @example\n * ```ts\n * base58Encode('00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31')\n * // '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs'\n * ```\n */\nexport function base58Encode(hex: string): string {\n let num = BigInt('0x' + hex);\n let encoded = '';\n\n while (num > 0n) {\n const remainder = Number(num % 58n);\n num = num / 58n;\n encoded = BASE58_ALPHABET[remainder] + encoded;\n }\n\n // Add leading 1s for leading 0s in hex\n for (let i = 0; i < hex.length && hex.substring(i, i + 2) === '00'; i += 2) {\n encoded = '1' + encoded;\n }\n\n return encoded;\n}\n\n/**\n * Base58 decode string to Uint8Array\n *\n * @example\n * ```ts\n * base58Decode('1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs')\n * // Uint8Array [0, 245, 74, ...]\n * ```\n */\nexport function base58Decode(str: string): Uint8Array {\n const ALPHABET_MAP: Record<string, number> = {};\n for (let i = 0; i < BASE58_ALPHABET.length; i++) {\n ALPHABET_MAP[BASE58_ALPHABET[i]] = i;\n }\n\n // Count leading zeros (represented as '1' in base58)\n let zeros = 0;\n for (let i = 0; i < str.length && str[i] === '1'; i++) {\n zeros++;\n }\n\n // Decode from base58 to number\n let num = BigInt(0);\n for (let i = 0; i < str.length; i++) {\n const char = str[i];\n if (!(char in ALPHABET_MAP)) {\n throw new Error('Invalid base58 character: ' + char);\n }\n num = num * BigInt(58) + BigInt(ALPHABET_MAP[char]);\n }\n\n // Convert to bytes\n const bytes: number[] = [];\n while (num > 0) {\n bytes.unshift(Number(num % BigInt(256)));\n num = num / BigInt(256);\n }\n\n // Add leading zeros\n for (let i = 0; i < zeros; i++) {\n bytes.unshift(0);\n }\n\n return new Uint8Array(bytes);\n}\n\n// =============================================================================\n// Binary Pattern Search\n// =============================================================================\n\n/**\n * Find pattern in Uint8Array\n *\n * @param data - Data to search in\n * @param pattern - Pattern to find\n * @param startIndex - Start index for search\n * @returns Index of pattern or -1 if not found\n */\nexport function findPattern(\n data: Uint8Array,\n pattern: Uint8Array,\n startIndex: number = 0\n): number {\n for (let i = startIndex; i <= data.length - pattern.length; i++) {\n let found = true;\n for (let j = 0; j < pattern.length; j++) {\n if (data[i + j] !== pattern[j]) {\n found = false;\n break;\n }\n }\n if (found) return i;\n }\n return -1;\n}\n\n// =============================================================================\n// Text Parsing\n// =============================================================================\n\n/**\n * Extract value from text using regex pattern\n *\n * @param text - Text to search\n * @param pattern - Regex pattern with capture group\n * @returns Captured value or null\n */\nexport function extractFromText(text: string, pattern: RegExp): string | null {\n const match = text.match(pattern);\n return match?.[1]?.trim() ?? null;\n}\n\n// =============================================================================\n// Delay/Sleep\n// =============================================================================\n\n/**\n * Sleep for specified milliseconds\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// =============================================================================\n// Random\n// =============================================================================\n\n/**\n * Generate random hex string of specified byte length\n */\nexport function randomHex(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n if (typeof globalThis.crypto !== 'undefined') {\n globalThis.crypto.getRandomValues(bytes);\n } else {\n // Fallback for Node.js without global crypto\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require('crypto');\n const buf = nodeCrypto.randomBytes(byteLength);\n bytes.set(buf);\n }\n return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Generate random UUID v4\n */\nexport function randomUUID(): string {\n if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.randomUUID) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const nodeCrypto = require('crypto');\n return nodeCrypto.randomUUID();\n}\n","/**\n * Wallet.dat Parsing\n *\n * Parses Bitcoin Core wallet.dat files (SQLite format)\n * Extracts keys, chain codes, and descriptors\n */\n\nimport CryptoJS from 'crypto-js';\nimport type { LegacyFileParseResult, LegacyFileParsedData, DecryptionProgressCallback } from './types';\nimport { findPattern, base58Decode, isValidPrivateKey } from '../core/utils';\nimport { bytesToHex } from '../core/crypto';\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\nfunction uint8ArrayToWordArray(u8arr: Uint8Array): CryptoJS.lib.WordArray {\n const hex = bytesToHex(u8arr);\n return CryptoJS.enc.Hex.parse(hex);\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface CMasterKeyData {\n encryptedKey: Uint8Array;\n salt: Uint8Array;\n derivationMethod: number;\n iterations: number;\n position: number;\n}\n\nexport interface WalletDatInfo {\n isSQLite: boolean;\n isEncrypted: boolean;\n isDescriptorWallet: boolean;\n hasHDChain: boolean;\n descriptorKeys: string[];\n legacyKeys: string[];\n chainCode: string | null;\n descriptorPath: string | null;\n cmasterKeys: CMasterKeyData[];\n descriptorId: Uint8Array | null;\n xpubString: string | null;\n}\n\n// =============================================================================\n// Detection Functions\n// =============================================================================\n\n/**\n * Check if data is a valid SQLite database\n */\nexport function isSQLiteDatabase(data: Uint8Array): boolean {\n if (data.length < 16) return false;\n const header = new TextDecoder().decode(data.slice(0, 16));\n return header.startsWith('SQLite format 3');\n}\n\n/**\n * Check if wallet.dat is encrypted\n */\nexport function isWalletDatEncrypted(data: Uint8Array): boolean {\n const mkeyPattern = new TextEncoder().encode('mkey');\n return findPattern(data, mkeyPattern, 0) !== -1;\n}\n\n// =============================================================================\n// CMasterKey Detection\n// =============================================================================\n\nfunction findAllCMasterKeys(data: Uint8Array): CMasterKeyData[] {\n const results: CMasterKeyData[] = [];\n\n for (let pos = 0; pos < data.length - 70; pos++) {\n if (data[pos] === 0x30) {\n const saltLenPos = pos + 1 + 48;\n if (saltLenPos < data.length && data[saltLenPos] === 0x08) {\n const iterPos = saltLenPos + 1 + 8 + 4;\n if (iterPos + 4 <= data.length) {\n const iterations = data[iterPos] | (data[iterPos + 1] << 8) |\n (data[iterPos + 2] << 16) | (data[iterPos + 3] << 24);\n if (iterations >= 1000 && iterations <= 10000000) {\n const encryptedKey = data.slice(pos + 1, pos + 1 + 48);\n const salt = data.slice(saltLenPos + 1, saltLenPos + 1 + 8);\n const derivationMethod = data[saltLenPos + 1 + 8] | (data[saltLenPos + 1 + 8 + 1] << 8) |\n (data[saltLenPos + 1 + 8 + 2] << 16) | (data[saltLenPos + 1 + 8 + 3] << 24);\n\n results.push({ encryptedKey, salt, derivationMethod, iterations, position: pos });\n }\n }\n }\n }\n }\n\n return results;\n}\n\n// =============================================================================\n// Descriptor Extraction\n// =============================================================================\n\nfunction findWpkhDescriptor(data: Uint8Array): {\n descriptorId: Uint8Array | null;\n xpubString: string | null;\n descriptorPath: string | null;\n} {\n const descriptorPattern = new TextEncoder().encode('walletdescriptor');\n let descriptorIndex = 0;\n\n while ((descriptorIndex = findPattern(data, descriptorPattern, descriptorIndex)) !== -1) {\n let scanPos = descriptorIndex + descriptorPattern.length + 32;\n\n const descLen = data[scanPos];\n scanPos++;\n\n const descBytes = data.slice(scanPos, scanPos + Math.min(descLen, 200));\n let descStr = '';\n for (let i = 0; i < descBytes.length && descBytes[i] >= 32 && descBytes[i] <= 126; i++) {\n descStr += String.fromCharCode(descBytes[i]);\n }\n\n if (descStr.startsWith('wpkh(xpub') && descStr.includes('/0/*)')) {\n const xpubMatch = descStr.match(/xpub[1-9A-HJ-NP-Za-km-z]{100,}/);\n if (xpubMatch) {\n const descIdStart = descriptorIndex + descriptorPattern.length;\n const descriptorId = data.slice(descIdStart, descIdStart + 32);\n const pathMatch = descStr.match(/\\[[\\da-f]+\\/(\\d+'\\/\\d+'\\/\\d+')\\]/);\n const descriptorPath = pathMatch ? pathMatch[1] : \"84'/1'/0'\";\n\n return {\n descriptorId,\n xpubString: xpubMatch[0],\n descriptorPath,\n };\n }\n }\n\n descriptorIndex++;\n }\n\n return { descriptorId: null, xpubString: null, descriptorPath: null };\n}\n\nfunction extractChainCodeFromXpub(xpubString: string): string {\n const decoded = base58Decode(xpubString);\n return bytesToHex(decoded.slice(13, 45));\n}\n\nfunction findMasterChainCode(data: Uint8Array): string | null {\n const xpubPattern = new TextEncoder().encode('xpub');\n const base58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\n let searchPos = 0;\n\n while (searchPos < data.length) {\n const xpubIndex = findPattern(data, xpubPattern, searchPos);\n if (xpubIndex === -1) break;\n\n let xpubStr = 'xpub';\n let pos = xpubIndex + 4;\n\n while (pos < data.length && xpubStr.length < 120) {\n const char = String.fromCharCode(data[pos]);\n if (base58Chars.includes(char)) {\n xpubStr += char;\n pos++;\n } else {\n break;\n }\n }\n\n if (xpubStr.length > 100) {\n try {\n const decoded = base58Decode(xpubStr);\n const depth = decoded[4];\n if (depth === 0) {\n return bytesToHex(decoded.slice(13, 45));\n }\n } catch {\n // Invalid xpub, continue\n }\n }\n\n searchPos = xpubIndex + 4;\n }\n\n return null;\n}\n\n// =============================================================================\n// Key Extraction (Unencrypted)\n// =============================================================================\n\nfunction extractDescriptorKeys(data: Uint8Array): string[] {\n const keys: string[] = [];\n const descriptorKeyPattern = new TextEncoder().encode('walletdescriptorkey');\n\n let index = 0;\n while ((index = findPattern(data, descriptorKeyPattern, index)) !== -1) {\n for (let checkPos = index + descriptorKeyPattern.length;\n checkPos < Math.min(index + descriptorKeyPattern.length + 200, data.length - 40);\n checkPos++) {\n if (data[checkPos] === 0xd3 &&\n data[checkPos + 1] === 0x02 &&\n data[checkPos + 2] === 0x01 &&\n data[checkPos + 3] === 0x01 &&\n data[checkPos + 4] === 0x04 &&\n data[checkPos + 5] === 0x20) {\n const privKey = data.slice(checkPos + 6, checkPos + 38);\n const privKeyHex = bytesToHex(privKey);\n\n if (isValidPrivateKey(privKeyHex)) {\n keys.push(privKeyHex);\n break;\n }\n }\n }\n index++;\n }\n\n return keys;\n}\n\nfunction extractLegacyKeys(data: Uint8Array): string[] {\n const keys: string[] = [];\n const keyPattern = new TextEncoder().encode('key');\n\n let index = 0;\n while ((index = findPattern(data, keyPattern, index)) !== -1) {\n const searchPattern = new Uint8Array([0x04, 0x20]);\n for (let i = index; i < Math.min(index + 200, data.length - 34); i++) {\n if (data[i] === searchPattern[0] && data[i + 1] === searchPattern[1]) {\n const privKey = data.slice(i + 2, i + 34);\n const privKeyHex = bytesToHex(privKey);\n\n if (isValidPrivateKey(privKeyHex)) {\n keys.push(privKeyHex);\n break;\n }\n }\n }\n index++;\n }\n\n return keys;\n}\n\n// =============================================================================\n// Encrypted Key Lookup\n// =============================================================================\n\nfunction findEncryptedKeyForDescriptor(\n data: Uint8Array,\n descriptorId: Uint8Array\n): { pubkey: Uint8Array; encryptedKey: Uint8Array } | null {\n const ckeyPattern = new TextEncoder().encode('walletdescriptorckey');\n let ckeyIndex = findPattern(data, ckeyPattern, 0);\n\n while (ckeyIndex !== -1) {\n const recordDescId = data.slice(ckeyIndex + ckeyPattern.length, ckeyIndex + ckeyPattern.length + 32);\n\n if (Array.from(recordDescId).every((b, i) => b === descriptorId[i])) {\n let keyPos = ckeyIndex + ckeyPattern.length + 32;\n const pubkeyLen = data[keyPos];\n keyPos++;\n const pubkey = data.slice(keyPos, keyPos + pubkeyLen);\n\n for (let searchPos = keyPos + pubkeyLen;\n searchPos < Math.min(keyPos + pubkeyLen + 100, data.length - 50);\n searchPos++) {\n const valueLen = data[searchPos];\n if (valueLen >= 32 && valueLen <= 64) {\n const encryptedKey = data.slice(searchPos + 1, searchPos + 1 + valueLen);\n return { pubkey, encryptedKey };\n }\n }\n }\n\n ckeyIndex = findPattern(data, ckeyPattern, ckeyIndex + 1);\n }\n\n return null;\n}\n\n// =============================================================================\n// Decryption Functions\n// =============================================================================\n\n/**\n * Decrypt master key from CMasterKey structure\n */\nexport async function decryptCMasterKey(\n cmk: CMasterKeyData,\n password: string,\n onProgress?: DecryptionProgressCallback\n): Promise<string> {\n const { encryptedKey, salt, iterations } = cmk;\n\n const passwordHex = bytesToHex(new TextEncoder().encode(password));\n const saltHex = bytesToHex(salt);\n const inputHex = passwordHex + saltHex;\n\n let hash = CryptoJS.SHA512(CryptoJS.enc.Hex.parse(inputHex));\n\n const BATCH_SIZE = 1000;\n for (let i = 0; i < iterations - 1; i++) {\n hash = CryptoJS.SHA512(hash);\n if (onProgress && i % BATCH_SIZE === 0) {\n await onProgress(i, iterations);\n }\n }\n\n const derivedKey = CryptoJS.lib.WordArray.create(hash.words.slice(0, 8));\n const derivedIv = CryptoJS.lib.WordArray.create(hash.words.slice(8, 12));\n\n const encryptedWords = uint8ArrayToWordArray(encryptedKey);\n\n const decrypted = CryptoJS.AES.decrypt(\n { ciphertext: encryptedWords } as CryptoJS.lib.CipherParams,\n derivedKey,\n { iv: derivedIv, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC }\n );\n\n const result = CryptoJS.enc.Hex.stringify(decrypted);\n\n if (!result || result.length !== 64) {\n throw new Error('Master key decryption failed - incorrect password');\n }\n\n return result;\n}\n\n/**\n * Decrypt private key using decrypted master key\n */\nexport function decryptPrivateKey(\n encryptedKey: Uint8Array,\n pubkey: Uint8Array,\n masterKeyHex: string\n): string {\n const pubkeyWords = uint8ArrayToWordArray(pubkey);\n const pubkeyHashWords = CryptoJS.SHA256(CryptoJS.SHA256(pubkeyWords));\n const ivWords = CryptoJS.lib.WordArray.create(pubkeyHashWords.words.slice(0, 4));\n\n const masterKeyWords = CryptoJS.enc.Hex.parse(masterKeyHex);\n const encryptedWords = uint8ArrayToWordArray(encryptedKey);\n\n const decrypted = CryptoJS.AES.decrypt(\n { ciphertext: encryptedWords } as CryptoJS.lib.CipherParams,\n masterKeyWords,\n { iv: ivWords, padding: CryptoJS.pad.Pkcs7, mode: CryptoJS.mode.CBC }\n );\n\n return CryptoJS.enc.Hex.stringify(decrypted);\n}\n\n// =============================================================================\n// Main Parse Function\n// =============================================================================\n\n/**\n * Parse wallet.dat file\n * For encrypted wallets, returns info for decryption\n */\nexport function parseWalletDat(data: Uint8Array): LegacyFileParseResult {\n if (!isSQLiteDatabase(data)) {\n return {\n success: false,\n error: 'Invalid wallet.dat file - not an SQLite database',\n };\n }\n\n const isEncrypted = isWalletDatEncrypted(data);\n const cmasterKeys = isEncrypted ? findAllCMasterKeys(data) : [];\n const descriptorKeys = isEncrypted ? [] : extractDescriptorKeys(data);\n const legacyKeys = isEncrypted ? [] : extractLegacyKeys(data);\n\n // descriptorId is used in parseAndDecryptWalletDat for encrypted wallets\n const { descriptorId: _descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);\n void _descriptorId; // Suppress unused warning\n\n let chainCode: string | null = null;\n if (xpubString) {\n try {\n chainCode = extractChainCodeFromXpub(xpubString);\n } catch {\n chainCode = findMasterChainCode(data);\n }\n } else {\n chainCode = findMasterChainCode(data);\n }\n\n // For unencrypted wallets\n if (!isEncrypted) {\n let masterKey: string | null = null;\n\n if (descriptorKeys.length > 0) {\n masterKey = descriptorKeys[0];\n } else if (legacyKeys.length > 0) {\n masterKey = legacyKeys[0];\n }\n\n if (masterKey) {\n const parsedData: LegacyFileParsedData = {\n masterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: descriptorPath ?? \"84'/1'/0'\",\n derivationMode: chainCode ? 'bip32' : 'wif_hmac',\n };\n\n return {\n success: true,\n data: parsedData,\n };\n }\n\n return {\n success: false,\n error: 'No valid private keys found in wallet.dat file',\n };\n }\n\n // For encrypted wallets\n if (cmasterKeys.length === 0) {\n return {\n success: false,\n error: 'Encrypted wallet but no CMasterKey structures found',\n };\n }\n\n // Return encryption info for decryption\n const firstCmk = cmasterKeys[0];\n return {\n success: false,\n needsPassword: true,\n encryptionInfo: {\n iterations: firstCmk.iterations,\n salt: firstCmk.salt,\n encryptedKey: firstCmk.encryptedKey,\n },\n error: 'Password required for encrypted wallet',\n };\n}\n\n/**\n * Parse and decrypt wallet.dat file\n */\nexport async function parseAndDecryptWalletDat(\n data: Uint8Array,\n password: string,\n onProgress?: DecryptionProgressCallback\n): Promise<LegacyFileParseResult> {\n if (!isSQLiteDatabase(data)) {\n return {\n success: false,\n error: 'Invalid wallet.dat file - not an SQLite database',\n };\n }\n\n const isEncrypted = isWalletDatEncrypted(data);\n\n if (!isEncrypted) {\n // Not encrypted, parse directly\n return parseWalletDat(data);\n }\n\n const cmasterKeys = findAllCMasterKeys(data);\n if (cmasterKeys.length === 0) {\n return {\n success: false,\n error: 'Encrypted wallet but no CMasterKey structures found',\n };\n }\n\n // Try to decrypt each CMasterKey\n let masterKeyHex: string | null = null;\n for (const cmk of cmasterKeys) {\n try {\n masterKeyHex = await decryptCMasterKey(cmk, password, onProgress);\n if (masterKeyHex && masterKeyHex.length === 64) {\n break;\n }\n } catch {\n continue;\n }\n }\n\n if (!masterKeyHex || masterKeyHex.length !== 64) {\n return {\n success: false,\n error: 'Master key decryption failed - incorrect password',\n };\n }\n\n // Find descriptor info\n const { descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);\n\n let chainCode: string | null = null;\n if (xpubString) {\n try {\n chainCode = extractChainCodeFromXpub(xpubString);\n } catch {\n chainCode = findMasterChainCode(data);\n }\n } else {\n chainCode = findMasterChainCode(data);\n }\n\n // Find and decrypt BIP32 master private key\n if (!descriptorId) {\n return {\n success: false,\n error: 'Could not find native SegWit receive descriptor',\n };\n }\n\n const encryptedKeyData = findEncryptedKeyForDescriptor(data, descriptorId);\n if (!encryptedKeyData) {\n return {\n success: false,\n error: 'Could not find encrypted private key for descriptor',\n };\n }\n\n const bip32MasterKey = decryptPrivateKey(\n encryptedKeyData.encryptedKey,\n encryptedKeyData.pubkey,\n masterKeyHex\n );\n\n if (!bip32MasterKey || bip32MasterKey.length !== 64) {\n return {\n success: false,\n error: 'Could not decrypt BIP32 master private key',\n };\n }\n\n const parsedData: LegacyFileParsedData = {\n masterKey: bip32MasterKey,\n chainCode: chainCode ?? undefined,\n descriptorPath: descriptorPath ?? \"84'/1'/0'\",\n derivationMode: 'bip32',\n };\n\n return {\n success: true,\n data: parsedData,\n };\n}\n","/**\n * Sphere - Main SDK Entry Point\n *\n * Handles wallet existence checking, creation, and loading.\n *\n * @example\n * ```ts\n * import { Sphere } from '@unicitylabs/sphere-sdk';\n * import { createLocalStorageProvider, createNostrTransportProvider, createUnicityAggregatorProvider } from '@unicitylabs/sphere-sdk/impl/browser';\n *\n * const storage = createLocalStorageProvider();\n * const transport = createNostrTransportProvider();\n * const oracle = createUnicityAggregatorProvider({ url: '/rpc' });\n *\n * // Option 1: Unified init (recommended)\n * const { sphere, created, generatedMnemonic } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * mnemonic: 'your twelve words...', // optional - will load if wallet exists\n * autoGenerate: true, // generate new mnemonic if needed\n * });\n *\n * if (created && generatedMnemonic) {\n * console.log('Save this mnemonic:', generatedMnemonic);\n * }\n *\n * // Option 2: Manual create/load\n * if (await Sphere.exists(storage)) {\n * const sphere = await Sphere.load({ storage, transport, oracle });\n * } else {\n * const sphere = await Sphere.create({ mnemonic, storage, transport, oracle });\n * }\n *\n * // Use the wallet\n * await sphere.payments.send({ coinId: 'ALPHA', amount: '1000', recipient: '@alice' });\n * ```\n */\n\nimport type {\n Identity,\n FullIdentity,\n SphereEventType,\n SphereEventMap,\n SphereEventHandler,\n DerivationMode,\n WalletSource,\n WalletInfo,\n WalletJSON,\n WalletJSONExportOptions,\n TrackedAddress,\n TrackedAddressEntry,\n} from '../types';\nimport type { StorageProvider, TokenStorageProvider, TxfStorageDataBase } from '../storage';\nimport type { TransportProvider, PeerInfo } from '../transport';\nimport type { OracleProvider } from '../oracle';\nimport type { PriceProvider } from '../price';\nimport { PaymentsModule, createPaymentsModule } from '../modules/payments';\nimport { CommunicationsModule, createCommunicationsModule } from '../modules/communications';\nimport { GroupChatModule, createGroupChatModule } from '../modules/groupchat';\nimport type { GroupChatModuleConfig } from '../modules/groupchat';\nimport {\n STORAGE_KEYS_GLOBAL,\n STORAGE_KEYS_ADDRESS,\n getAddressId,\n DEFAULT_BASE_PATH,\n LIMITS,\n DEFAULT_ENCRYPTION_KEY,\n NETWORKS,\n type NetworkType,\n} from '../constants';\nimport {\n generateMnemonic as generateBip39Mnemonic,\n validateMnemonic as validateBip39Mnemonic,\n identityFromMnemonicSync,\n deriveKeyAtPath,\n deriveAddressInfo,\n getPublicKey,\n sha256,\n publicKeyToAddress,\n type MasterKey,\n type AddressInfo,\n} from './crypto';\nimport { encryptSimple, decryptSimple, decryptWithSalt } from './encryption';\nimport { scanAddressesImpl } from './scan';\nimport type { ScanAddressesOptions, ScanAddressesResult } from './scan';\nimport { vestingClassifier } from '../l1/vesting';\nimport { generateAddressFromMasterKey } from '../l1/address';\nimport {\n parseWalletText,\n parseAndDecryptWalletText,\n isWalletTextFormat,\n isTextWalletEncrypted,\n serializeWalletToText,\n serializeEncryptedWalletToText,\n encryptForTextFormat,\n} from '../serialization/wallet-text';\nimport {\n parseWalletDat,\n parseAndDecryptWalletDat,\n isSQLiteDatabase,\n isWalletDatEncrypted,\n} from '../serialization/wallet-dat';\nimport { SigningService } from '@unicitylabs/state-transition-sdk/lib/sign/SigningService';\nimport { TokenType } from '@unicitylabs/state-transition-sdk/lib/token/TokenType';\nimport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm';\nimport { UnmaskedPredicateReference } from '@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference';\nimport { normalizeNametag, isPhoneNumber } from '@unicitylabs/nostr-js-sdk';\n\nexport function isValidNametag(nametag: string): boolean {\n if (isPhoneNumber(nametag)) return true;\n return /^[a-z0-9_-]{3,20}$/.test(nametag);\n}\n\nimport type {\n LegacyFileType,\n DecryptionProgressCallback,\n} from '../serialization/types';\n\n// =============================================================================\n// Options Types\n// =============================================================================\n\n/** Options for creating a new wallet */\nexport interface SphereCreateOptions {\n /** BIP39 mnemonic (12 or 24 words) */\n mnemonic: string;\n /** Custom derivation path (default: m/44'/0'/0') */\n derivationPath?: string;\n /** Optional nametag to register for this wallet (e.g., 'alice' for @alice). Token is auto-minted. */\n nametag?: string;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n /** Group chat configuration (NIP-29). Omit to disable groupchat. */\n groupChat?: GroupChatModuleConfig | boolean;\n}\n\n/** Options for loading existing wallet */\nexport interface SphereLoadOptions {\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n /** Group chat configuration (NIP-29). Omit to disable groupchat. */\n groupChat?: GroupChatModuleConfig | boolean;\n}\n\n/** Options for importing a wallet */\nexport interface SphereImportOptions {\n /** BIP39 mnemonic to import */\n mnemonic?: string;\n /** Or master private key (hex) */\n masterKey?: string;\n /** Chain code for BIP32 (optional) */\n chainCode?: string;\n /** Custom derivation path */\n derivationPath?: string;\n /** Base path for BIP32 derivation (e.g., \"m/84'/1'/0'\" from wallet.dat) */\n basePath?: string;\n /** Derivation mode: bip32, wif_hmac, legacy_hmac */\n derivationMode?: DerivationMode;\n /** Optional nametag to register for this wallet (e.g., 'alice' for @alice). Token is auto-minted. */\n nametag?: string;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Optional token storage provider */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /** Group chat configuration (NIP-29). Omit to disable groupchat. */\n groupChat?: GroupChatModuleConfig | boolean;\n}\n\n/** L1 (ALPHA blockchain) configuration */\nexport interface L1Config {\n /** Fulcrum WebSocket URL (default: wss://fulcrum.alpha.unicity.network:50004) */\n electrumUrl?: string;\n /** Default fee rate in sat/byte (default: 10) */\n defaultFeeRate?: number;\n /** Enable vesting classification (default: true) */\n enableVesting?: boolean;\n}\n\n/** Options for unified init (auto-create or load) */\nexport interface SphereInitOptions {\n /** Storage provider instance */\n storage: StorageProvider;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** Optional token storage provider (for IPFS sync) */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** BIP39 mnemonic - if wallet doesn't exist, use this to create */\n mnemonic?: string;\n /** Auto-generate mnemonic if wallet doesn't exist and no mnemonic provided */\n autoGenerate?: boolean;\n /** Custom derivation path (default: m/44'/0'/0') */\n derivationPath?: string;\n /** Optional nametag to register (only on create). Token is auto-minted. */\n nametag?: string;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n /** Optional price provider for fiat conversion */\n price?: PriceProvider;\n /**\n * Network type (mainnet, testnet, dev) - informational only.\n * Actual network configuration comes from provider URLs.\n * Use createBrowserProviders({ network: 'testnet' }) to set up testnet providers.\n */\n network?: NetworkType;\n /**\n * Group chat configuration (NIP-29).\n * - `true`: Enable with network-default relays\n * - `GroupChatModuleConfig`: Enable with custom config\n * - Omit/undefined: No groupchat module\n */\n groupChat?: GroupChatModuleConfig | boolean;\n}\n\n/** Result of init operation */\nexport interface SphereInitResult {\n /** The initialized Sphere instance */\n sphere: Sphere;\n /** Whether wallet was newly created */\n created: boolean;\n /** Generated mnemonic (only if autoGenerate was used) */\n generatedMnemonic?: string;\n}\n\n// =============================================================================\n// L3 Predicate Address Derivation\n// =============================================================================\n\n/** Token type for Unicity network (used for L3 predicate address derivation) */\nconst UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n\n/**\n * Derive L3 predicate address (DIRECT://...) from private key\n * Uses UnmaskedPredicateReference for stable wallet address\n */\nasync function deriveL3PredicateAddress(privateKey: string): Promise<string> {\n const secret = Buffer.from(privateKey, 'hex');\n const signingService = await SigningService.createFromSecret(secret);\n\n const tokenTypeBytes = Buffer.from(UNICITY_TOKEN_TYPE_HEX, 'hex');\n const tokenType = new TokenType(tokenTypeBytes);\n\n const predicateRef = UnmaskedPredicateReference.create(\n tokenType,\n signingService.algorithm,\n signingService.publicKey,\n HashAlgorithm.SHA256\n );\n\n return (await (await predicateRef).toAddress()).toString();\n}\n\n// =============================================================================\n// Mutable Identity (internal use only)\n// =============================================================================\n\n/** Mutable version of FullIdentity for internal state management */\ntype MutableFullIdentity = {\n -readonly [K in keyof FullIdentity]: FullIdentity[K];\n};\n\n// =============================================================================\n// Sphere Class\n// =============================================================================\n\nexport class Sphere {\n // Singleton\n private static instance: Sphere | null = null;\n\n // State\n private _initialized = false;\n private _identity: MutableFullIdentity | null = null;\n private _masterKey: MasterKey | null = null;\n private _mnemonic: string | null = null;\n private _source: WalletSource = 'unknown';\n private _derivationMode: DerivationMode = 'bip32';\n private _basePath: string = DEFAULT_BASE_PATH;\n private _currentAddressIndex: number = 0;\n /** Registry of all tracked (activated) addresses, keyed by HD index */\n private _trackedAddresses: Map<number, TrackedAddress> = new Map();\n /** Reverse lookup: addressId -> HD index */\n private _addressIdToIndex: Map<string, number> = new Map();\n /** Nametag cache: addressId -> (nametagIndex -> nametag). Separate from tracked addresses. */\n private _addressNametags: Map<string, Map<number, string>> = new Map();\n /** Cached PROXY address (computed once when nametag is set) */\n private _cachedProxyAddress: string | undefined = undefined;\n\n // Providers\n private _storage: StorageProvider;\n private _tokenStorageProviders: Map<string, TokenStorageProvider<TxfStorageDataBase>> = new Map();\n private _transport: TransportProvider;\n private _oracle: OracleProvider;\n private _priceProvider: PriceProvider | null;\n\n // Modules\n private _payments: PaymentsModule;\n private _communications: CommunicationsModule;\n private _groupChat: GroupChatModule | null = null;\n\n // Events\n private eventHandlers: Map<SphereEventType, Set<SphereEventHandler<SphereEventType>>> = new Map();\n\n // ===========================================================================\n // Constructor (private)\n // ===========================================================================\n\n private constructor(\n storage: StorageProvider,\n transport: TransportProvider,\n oracle: OracleProvider,\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>,\n l1Config?: L1Config,\n priceProvider?: PriceProvider,\n groupChatConfig?: GroupChatModuleConfig,\n ) {\n this._storage = storage;\n this._transport = transport;\n this._oracle = oracle;\n this._priceProvider = priceProvider ?? null;\n\n // Initialize token storage providers map\n if (tokenStorage) {\n this._tokenStorageProviders.set(tokenStorage.id, tokenStorage);\n }\n\n this._payments = createPaymentsModule({ l1: l1Config });\n this._communications = createCommunicationsModule();\n this._groupChat = groupChatConfig ? createGroupChatModule(groupChatConfig) : null;\n }\n\n // ===========================================================================\n // Static Methods - Wallet Management\n // ===========================================================================\n\n /**\n * Check if wallet exists in storage\n */\n static async exists(storage: StorageProvider): Promise<boolean> {\n try {\n // Ensure storage is connected before checking\n if (!storage.isConnected()) {\n await storage.connect();\n }\n\n // Check for mnemonic or master_key directly\n // These are saved with 'default' address before identity is set\n const mnemonic = await storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);\n if (mnemonic) return true;\n\n const masterKey = await storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n if (masterKey) return true;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Initialize wallet - auto-loads existing or creates new\n *\n * @example\n * ```ts\n * // Load existing or create with provided mnemonic\n * const { sphere, created } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * mnemonic: 'your twelve words...',\n * });\n *\n * // Load existing or auto-generate new mnemonic\n * const { sphere, created, generatedMnemonic } = await Sphere.init({\n * storage,\n * transport,\n * oracle,\n * autoGenerate: true,\n * });\n * if (generatedMnemonic) {\n * console.log('Save this mnemonic:', generatedMnemonic);\n * }\n * ```\n */\n static async init(options: SphereInitOptions): Promise<SphereInitResult> {\n // Resolve groupChat config: true → use network-default relays\n const groupChat = Sphere.resolveGroupChatConfig(options.groupChat, options.network);\n\n const walletExists = await Sphere.exists(options.storage);\n\n if (walletExists) {\n // Load existing wallet\n const sphere = await Sphere.load({\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n price: options.price,\n groupChat,\n });\n return { sphere, created: false };\n }\n\n // Need to create new wallet\n let mnemonic = options.mnemonic;\n let generatedMnemonic: string | undefined;\n\n if (!mnemonic) {\n if (options.autoGenerate) {\n // Auto-generate mnemonic\n mnemonic = Sphere.generateMnemonic();\n generatedMnemonic = mnemonic;\n } else {\n throw new Error(\n 'No wallet exists and no mnemonic provided. ' +\n 'Provide a mnemonic or set autoGenerate: true.'\n );\n }\n }\n\n const sphere = await Sphere.create({\n mnemonic,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n derivationPath: options.derivationPath,\n nametag: options.nametag,\n l1: options.l1,\n price: options.price,\n groupChat,\n });\n\n return { sphere, created: true, generatedMnemonic };\n }\n\n /**\n * Resolve groupChat config from init/create/load options.\n * - `true` → use network-default relays\n * - `GroupChatModuleConfig` → pass through\n * - `undefined` → no groupchat\n */\n /**\n * Resolve GroupChat config from Sphere.init() options.\n * Note: impl/shared/resolvers.ts has a similar resolver for provider-level config\n * (different input shape: { enabled?, relays? }). Both fill relay URLs from network defaults.\n */\n private static resolveGroupChatConfig(\n config: GroupChatModuleConfig | boolean | undefined,\n network?: NetworkType,\n ): GroupChatModuleConfig | undefined {\n if (!config) return undefined;\n if (config === true) {\n const netConfig = network ? NETWORKS[network] : NETWORKS.mainnet;\n return { relays: [...netConfig.groupRelays] };\n }\n // If relays not specified, fill from network defaults\n if (!config.relays || config.relays.length === 0) {\n const netConfig = network ? NETWORKS[network] : NETWORKS.mainnet;\n return { ...config, relays: [...netConfig.groupRelays] };\n }\n return config;\n }\n\n /**\n * Create new wallet with mnemonic\n */\n static async create(options: SphereCreateOptions): Promise<Sphere> {\n // Validate mnemonic\n if (!options.mnemonic || !Sphere.validateMnemonic(options.mnemonic)) {\n throw new Error('Invalid mnemonic');\n }\n\n // Check if wallet already exists\n if (await Sphere.exists(options.storage)) {\n throw new Error('Wallet already exists. Use Sphere.load() or Sphere.clear() first.');\n }\n\n const groupChatConfig = Sphere.resolveGroupChatConfig(options.groupChat, options.network);\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n groupChatConfig,\n );\n\n // Store encrypted mnemonic\n await sphere.storeMnemonic(options.mnemonic, options.derivationPath);\n\n // Initialize identity from mnemonic\n await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);\n\n // Initialize everything\n await sphere.initializeProviders();\n await sphere.initializeModules();\n\n // Mark wallet as created only after successful initialization\n // This prevents \"Wallet already exists\" errors if init fails partway through\n await sphere.finalizeWalletCreation();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // Track address 0 in the registry\n await sphere.ensureAddressTracked(0);\n\n // Register nametag if provided, otherwise try recovery then publish\n if (options.nametag) {\n // registerNametag publishes identity binding WITH nametag atomically\n // (calling syncIdentityWithTransport before this would race — both replaceable\n // events get the same created_at second and relay keeps the one without nametag)\n await sphere.registerNametag(options.nametag);\n } else {\n // Try to recover nametag BEFORE publishing — publishIdentityBinding uses\n // kind 30078 (replaceable event), so a bare binding would overwrite the\n // existing one that contains encrypted_nametag, making recovery impossible.\n await sphere.recoverNametagFromTransport();\n // Now publish identity binding (with recovered nametag if found)\n await sphere.syncIdentityWithTransport();\n }\n\n return sphere;\n }\n\n /**\n * Load existing wallet from storage\n */\n static async load(options: SphereLoadOptions): Promise<Sphere> {\n // Check if wallet exists\n if (!(await Sphere.exists(options.storage))) {\n throw new Error('No wallet found. Use Sphere.create() to create a new wallet.');\n }\n\n const groupChatConfig = Sphere.resolveGroupChatConfig(options.groupChat, options.network);\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n groupChatConfig,\n );\n\n // Load identity from storage\n await sphere.loadIdentityFromStorage();\n\n // Initialize everything\n await sphere.initializeProviders();\n await sphere.initializeModules();\n\n // Publish identity binding via transport\n await sphere.syncIdentityWithTransport();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // If nametag name exists but token is missing, try to mint it.\n // This handles the case where the token was lost from IndexedDB.\n if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {\n console.log(`[Sphere] Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);\n try {\n const result = await sphere.mintNametag(sphere._identity.nametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully on load`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed:`, err);\n }\n }\n\n return sphere;\n }\n\n /**\n * Import wallet from mnemonic or master key\n */\n static async import(options: SphereImportOptions): Promise<Sphere> {\n if (!options.mnemonic && !options.masterKey) {\n throw new Error('Either mnemonic or masterKey is required');\n }\n\n console.log('[Sphere.import] Starting import...');\n\n // Clear existing wallet if any (including token data)\n console.log('[Sphere.import] Clearing existing wallet data...');\n await Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });\n console.log('[Sphere.import] Clear done');\n\n // Reconnect storage after clear (clear may have called destroy() on the\n // previous instance which disconnects the shared storage provider)\n if (!options.storage.isConnected()) {\n console.log('[Sphere.import] Reconnecting storage...');\n await options.storage.connect();\n console.log('[Sphere.import] Storage reconnected');\n }\n\n const groupChatConfig = Sphere.resolveGroupChatConfig(options.groupChat);\n\n const sphere = new Sphere(\n options.storage,\n options.transport,\n options.oracle,\n options.tokenStorage,\n options.l1,\n options.price,\n groupChatConfig,\n );\n\n if (options.mnemonic) {\n // Validate and store mnemonic\n if (!Sphere.validateMnemonic(options.mnemonic)) {\n throw new Error('Invalid mnemonic');\n }\n console.log('[Sphere.import] Storing mnemonic...');\n await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);\n console.log('[Sphere.import] Initializing identity from mnemonic...');\n await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);\n } else if (options.masterKey) {\n // Store master key directly\n console.log('[Sphere.import] Storing master key...');\n await sphere.storeMasterKey(\n options.masterKey,\n options.chainCode,\n options.derivationPath,\n options.basePath,\n options.derivationMode\n );\n console.log('[Sphere.import] Initializing identity from master key...');\n await sphere.initializeIdentityFromMasterKey(\n options.masterKey,\n options.chainCode,\n options.derivationPath\n );\n }\n\n // Initialize everything\n console.log('[Sphere.import] Initializing providers...');\n await sphere.initializeProviders();\n console.log('[Sphere.import] Providers initialized. Initializing modules...');\n await sphere.initializeModules();\n console.log('[Sphere.import] Modules initialized');\n\n // Try to recover nametag from transport (if no nametag provided and wallet previously had one)\n if (!options.nametag) {\n console.log('[Sphere.import] Recovering nametag from transport...');\n await sphere.recoverNametagFromTransport();\n console.log('[Sphere.import] Nametag recovery done');\n // Publish identity binding (with recovered nametag if found)\n await sphere.syncIdentityWithTransport();\n }\n\n // Mark wallet as created only after successful initialization\n console.log('[Sphere.import] Finalizing wallet creation...');\n await sphere.finalizeWalletCreation();\n\n sphere._initialized = true;\n Sphere.instance = sphere;\n\n // Track address 0 in the registry\n console.log('[Sphere.import] Tracking address 0...');\n await sphere.ensureAddressTracked(0);\n\n // Register nametag if provided (this overrides any recovered nametag)\n if (options.nametag) {\n console.log('[Sphere.import] Registering nametag...');\n await sphere.registerNametag(options.nametag);\n }\n\n // Auto-sync with token storage providers (e.g., IPFS) to recover tokens\n if (sphere._tokenStorageProviders.size > 0) {\n try {\n const syncResult = await sphere._payments.sync();\n console.log(`[Sphere.import] Auto-sync: +${syncResult.added} -${syncResult.removed}`);\n } catch (err) {\n console.warn('[Sphere.import] Auto-sync failed (non-fatal):', err);\n }\n }\n\n console.log('[Sphere.import] Import complete');\n return sphere;\n }\n\n /**\n * Clear all SDK-owned wallet data from storage.\n *\n * Removes wallet keys, per-address data, and optionally token storage.\n * Does NOT affect application-level data stored outside the SDK.\n *\n * @param storageOrOptions - StorageProvider (backward compatible) or options object\n *\n * @example\n * // New usage (recommended) - clears wallet keys AND token data\n * await Sphere.clear({\n * storage: providers.storage,\n * tokenStorage: providers.tokenStorage,\n * });\n *\n * @example\n * // Legacy usage - clears only wallet keys\n * await Sphere.clear(storage);\n */\n static async clear(\n storageOrOptions: StorageProvider | { storage: StorageProvider; tokenStorage?: TokenStorageProvider<TxfStorageDataBase> },\n ): Promise<void> {\n const storage = 'get' in storageOrOptions ? storageOrOptions as StorageProvider : storageOrOptions.storage;\n const tokenStorage = 'get' in storageOrOptions ? undefined : storageOrOptions.tokenStorage;\n\n // Ensure storage is connected (may have been disconnected by a previous destroy() cycle)\n if (!storage.isConnected()) {\n await storage.connect();\n }\n\n // Clear global wallet data\n console.log('[Sphere.clear] Removing storage keys...');\n await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);\n await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);\n await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);\n await storage.remove(STORAGE_KEYS_GLOBAL.BASE_PATH);\n await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);\n await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);\n await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_EXISTS);\n await storage.remove(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);\n await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n // Per-address data\n await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);\n await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);\n console.log('[Sphere.clear] Storage keys removed');\n\n // Clear token storage if provided (with timeout to prevent IndexedDB deadlock)\n if (tokenStorage?.clear) {\n console.log('[Sphere.clear] Clearing token storage...');\n try {\n await Promise.race([\n tokenStorage.clear(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('tokenStorage.clear() timed out after 2s')), 2000),\n ),\n ]);\n console.log('[Sphere.clear] Token storage cleared');\n } catch (err) {\n console.warn('[Sphere.clear] Token storage clear failed/timed out:', err);\n }\n }\n\n // Clear L1 vesting cache\n console.log('[Sphere.clear] Destroying vesting classifier...');\n await vestingClassifier.destroy();\n console.log('[Sphere.clear] Vesting classifier destroyed');\n\n if (Sphere.instance) {\n console.log('[Sphere.clear] Destroying Sphere instance...');\n await Sphere.instance.destroy();\n console.log('[Sphere.clear] Sphere instance destroyed');\n } else {\n console.log('[Sphere.clear] No Sphere instance to destroy');\n }\n }\n\n /**\n * Get current instance\n */\n static getInstance(): Sphere | null {\n return Sphere.instance;\n }\n\n /**\n * Check if initialized\n */\n static isInitialized(): boolean {\n return Sphere.instance?._initialized ?? false;\n }\n\n /**\n * Validate mnemonic using BIP39\n */\n static validateMnemonic(mnemonic: string): boolean {\n return validateBip39Mnemonic(mnemonic);\n }\n\n /**\n * Generate new BIP39 mnemonic\n * @param strength - 128 for 12 words, 256 for 24 words\n */\n static generateMnemonic(strength: 128 | 256 = 128): string {\n return generateBip39Mnemonic(strength);\n }\n\n // ===========================================================================\n // Public Properties - Modules\n // ===========================================================================\n\n /** Payments module (L3 + L1) */\n get payments(): PaymentsModule {\n this.ensureReady();\n return this._payments;\n }\n\n /** Communications module */\n get communications(): CommunicationsModule {\n this.ensureReady();\n return this._communications;\n }\n\n /** Group chat module (NIP-29). Null if not configured. */\n get groupChat(): GroupChatModule | null {\n return this._groupChat;\n }\n\n // ===========================================================================\n // Public Properties - State\n // ===========================================================================\n\n /** Current identity (public info only) */\n get identity(): Identity | null {\n if (!this._identity) return null;\n return {\n chainPubkey: this._identity.chainPubkey,\n l1Address: this._identity.l1Address,\n directAddress: this._identity.directAddress,\n ipnsName: this._identity.ipnsName,\n nametag: this._identity.nametag,\n };\n }\n\n /** Is ready */\n get isReady(): boolean {\n return this._initialized;\n }\n\n // ===========================================================================\n // Public Methods - Providers Access\n // ===========================================================================\n\n getStorage(): StorageProvider {\n return this._storage;\n }\n\n /**\n * Get first token storage provider (for backward compatibility)\n * @deprecated Use getTokenStorageProviders() for multiple providers\n */\n getTokenStorage(): TokenStorageProvider<TxfStorageDataBase> | undefined {\n const providers = Array.from(this._tokenStorageProviders.values());\n return providers.length > 0 ? providers[0] : undefined;\n }\n\n /**\n * Get all token storage providers\n */\n getTokenStorageProviders(): Map<string, TokenStorageProvider<TxfStorageDataBase>> {\n return new Map(this._tokenStorageProviders);\n }\n\n /**\n * Add a token storage provider dynamically (e.g., from UI)\n * Provider will be initialized and connected automatically\n */\n async addTokenStorageProvider(provider: TokenStorageProvider<TxfStorageDataBase>): Promise<void> {\n if (this._tokenStorageProviders.has(provider.id)) {\n throw new Error(`Token storage provider '${provider.id}' already exists`);\n }\n\n // Set identity if wallet is initialized\n if (this._identity) {\n provider.setIdentity(this._identity);\n await provider.initialize();\n }\n\n this._tokenStorageProviders.set(provider.id, provider);\n\n // Update payments module with new providers\n if (this._initialized) {\n this._payments.updateTokenStorageProviders(this._tokenStorageProviders);\n }\n }\n\n /**\n * Remove a token storage provider dynamically\n */\n async removeTokenStorageProvider(providerId: string): Promise<boolean> {\n const provider = this._tokenStorageProviders.get(providerId);\n if (!provider) {\n return false;\n }\n\n // Shutdown provider gracefully\n await provider.shutdown();\n\n this._tokenStorageProviders.delete(providerId);\n\n // Update payments module\n if (this._initialized) {\n this._payments.updateTokenStorageProviders(this._tokenStorageProviders);\n }\n\n return true;\n }\n\n /**\n * Check if a token storage provider is registered\n */\n hasTokenStorageProvider(providerId: string): boolean {\n return this._tokenStorageProviders.has(providerId);\n }\n\n /**\n * Set or update the price provider after initialization\n */\n setPriceProvider(provider: PriceProvider): void {\n this._priceProvider = provider;\n this._payments.setPriceProvider(provider);\n }\n\n getTransport(): TransportProvider {\n return this._transport;\n }\n\n getAggregator(): OracleProvider {\n return this._oracle;\n }\n\n /**\n * Check if wallet has BIP32 master key for HD derivation\n */\n hasMasterKey(): boolean {\n return this._masterKey !== null;\n }\n\n // ===========================================================================\n // Public Methods - Multi-Address Derivation\n // ===========================================================================\n\n /**\n * Get the base derivation path used by this wallet (e.g., \"m/44'/0'/0'\")\n */\n getBasePath(): string {\n return this._basePath;\n }\n\n /**\n * Get the default address path (first external address)\n * Returns path like \"m/44'/0'/0'/0/0\"\n */\n getDefaultAddressPath(): string {\n return `${this._basePath}/0/0`;\n }\n\n /**\n * Get current derivation mode\n */\n getDerivationMode(): DerivationMode {\n return this._derivationMode;\n }\n\n /**\n * Get the mnemonic phrase (for backup purposes)\n * Returns null if wallet was imported from file (masterKey only)\n */\n getMnemonic(): string | null {\n return this._mnemonic;\n }\n\n /**\n * Get wallet info for backup/export purposes\n */\n getWalletInfo(): WalletInfo {\n let address0: string | null = null;\n try {\n if (this._masterKey) {\n address0 = this.deriveAddress(0).address;\n } else if (this._identity) {\n address0 = this._identity.l1Address;\n }\n } catch {\n // Ignore errors\n }\n\n return {\n source: this._source,\n hasMnemonic: this._mnemonic !== null,\n hasChainCode: !!this._masterKey?.chainCode,\n derivationMode: this._derivationMode,\n basePath: this._basePath,\n address0,\n };\n }\n\n /**\n * Export wallet to JSON format for backup\n *\n * @example\n * ```ts\n * // Export with mnemonic (if available)\n * const json = sphere.exportToJSON();\n *\n * // Export with encryption\n * const encrypted = sphere.exportToJSON({ password: 'secret' });\n *\n * // Export multiple addresses\n * const multi = sphere.exportToJSON({ addressCount: 5 });\n * ```\n */\n exportToJSON(options: WalletJSONExportOptions = {}): WalletJSON {\n this.ensureReady();\n\n if (!this._masterKey && !this._identity) {\n throw new Error('Wallet not initialized');\n }\n\n // Build addresses array\n const addressCount = options.addressCount || 1;\n const addresses: Array<{\n address: string;\n publicKey: string;\n path: string;\n index: number;\n }> = [];\n\n for (let i = 0; i < addressCount; i++) {\n try {\n const addr = this.deriveAddress(i, false);\n addresses.push({\n address: addr.address,\n publicKey: addr.publicKey,\n path: addr.path,\n index: addr.index,\n });\n } catch {\n // Stop if we can't derive more addresses (e.g., no masterKey)\n if (i === 0 && this._identity) {\n addresses.push({\n address: this._identity.l1Address,\n publicKey: this._identity.chainPubkey,\n path: this.getDefaultAddressPath(),\n index: 0,\n });\n }\n break;\n }\n }\n\n // Build wallet data\n let masterPrivateKey: string | undefined;\n let chainCode: string | undefined;\n\n if (this._masterKey) {\n masterPrivateKey = this._masterKey.privateKey;\n chainCode = this._masterKey.chainCode || undefined;\n }\n\n // Prepare mnemonic (optionally encrypt)\n let mnemonic: string | undefined;\n let encrypted = false;\n\n if (this._mnemonic && options.includeMnemonic !== false) {\n if (options.password) {\n mnemonic = encryptSimple(this._mnemonic, options.password);\n encrypted = true;\n } else {\n mnemonic = this._mnemonic;\n }\n }\n\n // Encrypt master key if password provided\n if (masterPrivateKey && options.password) {\n masterPrivateKey = encryptSimple(masterPrivateKey, options.password);\n encrypted = true;\n }\n\n return {\n version: '1.0',\n type: 'sphere-wallet',\n createdAt: new Date().toISOString(),\n wallet: {\n masterPrivateKey,\n chainCode,\n addresses,\n isBIP32: this._derivationMode === 'bip32',\n descriptorPath: this._basePath.replace(/^m\\//, ''),\n },\n mnemonic,\n encrypted,\n source: this._source,\n derivationMode: this._derivationMode,\n };\n }\n\n /**\n * Export wallet to text format for backup\n *\n * @example\n * ```ts\n * // Export unencrypted\n * const text = sphere.exportToTxt();\n *\n * // Export with encryption\n * const encrypted = sphere.exportToTxt({ password: 'secret' });\n *\n * // Export multiple addresses\n * const multi = sphere.exportToTxt({ addressCount: 5 });\n * ```\n */\n exportToTxt(options: { password?: string; addressCount?: number } = {}): string {\n this.ensureReady();\n\n if (!this._masterKey && !this._identity) {\n throw new Error('Wallet not initialized');\n }\n\n // Build addresses array\n const addressCount = options.addressCount || 1;\n const addresses: Array<{\n index: number;\n address: string;\n path: string;\n isChange: boolean;\n }> = [];\n\n for (let i = 0; i < addressCount; i++) {\n try {\n const addr = this.deriveAddress(i, false);\n addresses.push({\n address: addr.address,\n path: addr.path,\n index: addr.index,\n isChange: false,\n });\n } catch {\n // Stop if we can't derive more addresses\n if (i === 0 && this._identity) {\n addresses.push({\n address: this._identity.l1Address,\n path: this.getDefaultAddressPath(),\n index: 0,\n isChange: false,\n });\n }\n break;\n }\n }\n\n const masterPrivateKey = this._masterKey?.privateKey || '';\n const chainCode = this._masterKey?.chainCode || undefined;\n const isBIP32 = this._derivationMode === 'bip32';\n const descriptorPath = this._basePath.replace(/^m\\//, '');\n\n // If password provided, encrypt\n if (options.password) {\n const encryptedMasterKey = encryptForTextFormat(masterPrivateKey, options.password);\n return serializeEncryptedWalletToText({\n encryptedMasterKey,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n });\n }\n\n // Unencrypted export\n return serializeWalletToText({\n masterPrivateKey,\n chainCode,\n descriptorPath,\n isBIP32,\n addresses,\n });\n }\n\n /**\n * Import wallet from JSON backup\n *\n * @returns Object with success status and optionally recovered mnemonic\n *\n * @example\n * ```ts\n * const json = '{\"version\":\"1.0\",...}';\n * const { success, mnemonic } = await Sphere.importFromJSON({\n * jsonContent: json,\n * password: 'secret', // if encrypted\n * storage, transport, oracle,\n * });\n * ```\n */\n static async importFromJSON(options: {\n jsonContent: string;\n password?: string;\n storage: StorageProvider;\n transport: TransportProvider;\n oracle: OracleProvider;\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n l1?: L1Config;\n }): Promise<{ success: boolean; mnemonic?: string; error?: string }> {\n try {\n const data = JSON.parse(options.jsonContent) as WalletJSON;\n\n if (data.version !== '1.0' || data.type !== 'sphere-wallet') {\n return { success: false, error: 'Invalid wallet format' };\n }\n\n // Decrypt if needed\n let mnemonic = data.mnemonic;\n let masterKey = data.wallet.masterPrivateKey;\n\n if (data.encrypted && options.password) {\n if (mnemonic) {\n const decrypted = decryptSimple(mnemonic, options.password);\n if (!decrypted) {\n return { success: false, error: 'Failed to decrypt mnemonic - wrong password?' };\n }\n mnemonic = decrypted;\n }\n if (masterKey) {\n const decrypted = decryptSimple(masterKey, options.password);\n if (!decrypted) {\n return { success: false, error: 'Failed to decrypt master key - wrong password?' };\n }\n masterKey = decrypted;\n }\n } else if (data.encrypted && !options.password) {\n return { success: false, error: 'Password required for encrypted wallet' };\n }\n\n // Determine base path\n const basePath = data.wallet.descriptorPath\n ? `m/${data.wallet.descriptorPath}`\n : DEFAULT_BASE_PATH;\n\n // Import using mnemonic if available (preferred)\n if (mnemonic) {\n await Sphere.import({\n mnemonic,\n basePath,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n return { success: true, mnemonic };\n }\n\n // Otherwise import using master key\n if (masterKey) {\n await Sphere.import({\n masterKey,\n chainCode: data.wallet.chainCode,\n basePath,\n derivationMode: data.derivationMode || (data.wallet.isBIP32 ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n return { success: true };\n }\n\n return { success: false, error: 'No mnemonic or master key in wallet data' };\n } catch (e) {\n return {\n success: false,\n error: e instanceof Error ? e.message : 'Failed to parse wallet JSON',\n };\n }\n }\n\n /**\n * Import wallet from legacy file (.dat, .txt, or mnemonic text)\n *\n * Supports:\n * - Bitcoin Core wallet.dat files (SQLite format, encrypted or unencrypted)\n * - Text backup files (UNICITY WALLET DETAILS format)\n * - Plain mnemonic text (12 or 24 words)\n *\n * @returns Object with success status, created Sphere instance, and optionally recovered mnemonic\n *\n * @example\n * ```ts\n * // Import from .dat file\n * const fileBuffer = await file.arrayBuffer();\n * const result = await Sphere.importFromLegacyFile({\n * fileContent: new Uint8Array(fileBuffer),\n * fileName: 'wallet.dat',\n * password: 'wallet-password', // if encrypted\n * storage, transport, oracle,\n * });\n *\n * // Import from .txt file\n * const textContent = await file.text();\n * const result = await Sphere.importFromLegacyFile({\n * fileContent: textContent,\n * fileName: 'backup.txt',\n * storage, transport, oracle,\n * });\n * ```\n */\n static async importFromLegacyFile(options: {\n /** File content - Uint8Array for .dat, string for .txt */\n fileContent: string | Uint8Array;\n /** File name (used for type detection) */\n fileName: string;\n /** Password for encrypted files */\n password?: string;\n /** Progress callback for long decryption operations */\n onDecryptProgress?: DecryptionProgressCallback;\n /** Storage provider instance */\n storage: StorageProvider;\n /** Transport provider instance */\n transport: TransportProvider;\n /** Oracle provider instance */\n oracle: OracleProvider;\n /** Optional token storage provider */\n tokenStorage?: TokenStorageProvider<TxfStorageDataBase>;\n /** Optional nametag to register */\n nametag?: string;\n /** L1 (ALPHA blockchain) configuration */\n l1?: L1Config;\n }): Promise<{\n success: boolean;\n sphere?: Sphere;\n mnemonic?: string;\n needsPassword?: boolean;\n error?: string;\n }> {\n const { fileContent, fileName, password, onDecryptProgress } = options;\n\n // Detect file type\n const fileType = Sphere.detectLegacyFileType(fileName, fileContent);\n\n if (fileType === 'unknown') {\n return { success: false, error: 'Unknown file format' };\n }\n\n // Handle mnemonic text\n if (fileType === 'mnemonic') {\n const mnemonic = (fileContent as string).trim().toLowerCase().split(/\\s+/).join(' ');\n if (!Sphere.validateMnemonic(mnemonic)) {\n return { success: false, error: 'Invalid mnemonic phrase' };\n }\n\n const sphere = await Sphere.import({\n mnemonic,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere, mnemonic };\n }\n\n // Handle .dat file\n if (fileType === 'dat') {\n const data = fileContent instanceof Uint8Array\n ? fileContent\n : new TextEncoder().encode(fileContent);\n\n let parseResult;\n\n if (password) {\n parseResult = await parseAndDecryptWalletDat(data, password, onDecryptProgress);\n } else {\n parseResult = parseWalletDat(data);\n }\n\n if (parseResult.needsPassword && !password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n\n if (!parseResult.success || !parseResult.data) {\n return { success: false, error: parseResult.error };\n }\n\n const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;\n\n // Build base path from descriptor path\n const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: derivationMode || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere };\n }\n\n // Handle .txt file\n if (fileType === 'txt') {\n const content = typeof fileContent === 'string'\n ? fileContent\n : new TextDecoder().decode(fileContent);\n\n let parseResult;\n\n if (password) {\n parseResult = parseAndDecryptWalletText(content, password);\n } else if (isTextWalletEncrypted(content)) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n } else {\n parseResult = parseWalletText(content);\n }\n\n if (parseResult.needsPassword && !password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n\n if (!parseResult.success || !parseResult.data) {\n return { success: false, error: parseResult.error };\n }\n\n const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;\n\n const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: derivationMode || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n\n return { success: true, sphere };\n }\n\n // Handle JSON\n if (fileType === 'json') {\n const content = typeof fileContent === 'string'\n ? fileContent\n : new TextDecoder().decode(fileContent);\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content);\n } catch {\n return { success: false, error: 'Invalid JSON file' };\n }\n\n // sphere-wallet format — delegate to importFromJSON\n if (parsed.type === 'sphere-wallet') {\n const result = await Sphere.importFromJSON({\n jsonContent: content,\n password,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n l1: options.l1,\n });\n\n if (result.success) {\n const sphere = Sphere.getInstance();\n return { success: true, sphere: sphere!, mnemonic: result.mnemonic };\n }\n\n if (!password && result.error?.includes('Password required')) {\n return { success: false, needsPassword: true, error: result.error };\n }\n\n return { success: false, error: result.error };\n }\n\n // Legacy flat JSON format (webwallet export)\n let masterKey: string | undefined;\n let mnemonic: string | undefined;\n\n if (parsed.encrypted && typeof parsed.encrypted === 'object') {\n // Encrypted legacy JSON — needs password + salt-based PBKDF2 decryption\n if (!password) {\n return { success: false, needsPassword: true, error: 'Password required for encrypted wallet' };\n }\n const enc = parsed.encrypted as { masterPrivateKey?: string; mnemonic?: string; salt?: string };\n if (!enc.salt || !enc.masterPrivateKey) {\n return { success: false, error: 'Invalid encrypted wallet format' };\n }\n const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);\n if (!decryptedKey) {\n return { success: false, error: 'Failed to decrypt - incorrect password?' };\n }\n masterKey = decryptedKey;\n if (enc.mnemonic) {\n mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? undefined;\n }\n } else {\n // Unencrypted legacy JSON\n masterKey = parsed.masterPrivateKey as string | undefined;\n mnemonic = parsed.mnemonic as string | undefined;\n }\n\n if (!masterKey) {\n return { success: false, error: 'No master key found in wallet JSON' };\n }\n\n const chainCode = parsed.chainCode as string | undefined;\n const descriptorPath = parsed.descriptorPath as string | undefined;\n const derivationMode = (parsed.derivationMode as string | undefined);\n const isBIP32 = derivationMode === 'bip32' || !!chainCode;\n const basePath = descriptorPath\n ? `m/${descriptorPath}`\n : (isBIP32 ? \"m/84'/1'/0'\" : DEFAULT_BASE_PATH);\n\n if (mnemonic) {\n const sphere = await Sphere.import({\n mnemonic,\n basePath,\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n return { success: true, sphere, mnemonic };\n }\n\n const sphere = await Sphere.import({\n masterKey,\n chainCode,\n basePath,\n derivationMode: (derivationMode as DerivationMode) || (chainCode ? 'bip32' : 'wif_hmac'),\n storage: options.storage,\n transport: options.transport,\n oracle: options.oracle,\n tokenStorage: options.tokenStorage,\n nametag: options.nametag,\n l1: options.l1,\n });\n return { success: true, sphere };\n }\n\n return { success: false, error: 'Unsupported file type' };\n }\n\n /**\n * Detect legacy file type from filename and content\n */\n static detectLegacyFileType(fileName: string, content: string | Uint8Array): LegacyFileType {\n // .dat files are binary\n if (fileName.endsWith('.dat')) {\n return 'dat';\n }\n\n // Check content for type detection\n const textContent = typeof content === 'string'\n ? content\n : (content.length < 1000 ? new TextDecoder().decode(content) : '');\n\n // Check for JSON\n if (fileName.endsWith('.json')) {\n return 'json';\n }\n\n try {\n const trimmed = textContent.trim();\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n JSON.parse(trimmed);\n return 'json';\n }\n } catch {\n // Not JSON\n }\n\n // Check for mnemonic (12 or 24 words)\n const words = textContent.trim().split(/\\s+/);\n if (\n (words.length === 12 || words.length === 24) &&\n words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))\n ) {\n return 'mnemonic';\n }\n\n // Check for text wallet format\n if (isWalletTextFormat(textContent)) {\n return 'txt';\n }\n\n // Check for SQLite (binary .dat)\n if (content instanceof Uint8Array && isSQLiteDatabase(content)) {\n return 'dat';\n }\n\n return 'unknown';\n }\n\n /**\n * Check if a legacy file is encrypted\n */\n static isLegacyFileEncrypted(fileName: string, content: string | Uint8Array): boolean {\n const fileType = Sphere.detectLegacyFileType(fileName, content);\n\n if (fileType === 'dat' && content instanceof Uint8Array) {\n return isWalletDatEncrypted(content);\n }\n\n if (fileType === 'txt') {\n const textContent = typeof content === 'string'\n ? content\n : new TextDecoder().decode(content);\n return isTextWalletEncrypted(textContent);\n }\n\n if (fileType === 'json') {\n try {\n const textContent = typeof content === 'string'\n ? content\n : new TextDecoder().decode(content);\n const data = JSON.parse(textContent);\n return !!data.encrypted;\n } catch {\n return false;\n }\n }\n\n return false;\n }\n\n /**\n * Get the current active address index\n *\n * @example\n * ```ts\n * const currentIndex = sphere.getCurrentAddressIndex();\n * console.log(currentIndex); // 0\n *\n * await sphere.switchToAddress(2);\n * console.log(sphere.getCurrentAddressIndex()); // 2\n * ```\n */\n getCurrentAddressIndex(): number {\n return this._currentAddressIndex;\n }\n\n /**\n * Get primary nametag for a specific address\n *\n * @param addressId - Address identifier (DIRECT://xxx), defaults to current address\n * @returns Primary nametag (index 0) or undefined if not registered\n */\n getNametagForAddress(addressId?: string): string | undefined {\n const id = addressId ?? this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (!id) return undefined;\n return this._addressNametags.get(id)?.get(0);\n }\n\n /**\n * Get all nametags for a specific address\n *\n * @param addressId - Address identifier (DIRECT://xxx), defaults to current address\n * @returns Map of nametagIndex to nametag, or undefined if no nametags\n */\n getNametagsForAddress(addressId?: string): Map<number, string> | undefined {\n const id = addressId ?? this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (!id) return undefined;\n const nametags = this._addressNametags.get(id);\n return nametags && nametags.size > 0 ? new Map(nametags) : undefined;\n }\n\n /**\n * Get all registered address nametags\n * @deprecated Use getActiveAddresses() or getAllTrackedAddresses() instead\n * @returns Map of addressId to (nametagIndex -> nametag)\n */\n getAllAddressNametags(): Map<string, Map<number, string>> {\n const result = new Map<string, Map<number, string>>();\n for (const [addressId, nametags] of this._addressNametags.entries()) {\n if (nametags.size > 0) {\n result.set(addressId, new Map(nametags));\n }\n }\n return result;\n }\n\n /**\n * Get all active (non-hidden) tracked addresses.\n * Returns addresses that have been activated through create, switchToAddress,\n * registerNametag, or nametag recovery.\n *\n * @returns Array of TrackedAddress entries sorted by index, excluding hidden ones\n */\n getActiveAddresses(): TrackedAddress[] {\n this.ensureReady();\n const result: TrackedAddress[] = [];\n for (const entry of this._trackedAddresses.values()) {\n if (!entry.hidden) {\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n result.push({ ...entry, nametag });\n }\n }\n return result.sort((a, b) => a.index - b.index);\n }\n\n /**\n * Get all tracked addresses, including hidden ones.\n *\n * @returns Array of all TrackedAddress entries sorted by index\n */\n getAllTrackedAddresses(): TrackedAddress[] {\n this.ensureReady();\n const result: TrackedAddress[] = [];\n for (const entry of this._trackedAddresses.values()) {\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n result.push({ ...entry, nametag });\n }\n return result.sort((a, b) => a.index - b.index);\n }\n\n /**\n * Get tracked address info by index.\n *\n * @param index - Address index\n * @returns TrackedAddress or undefined if not tracked\n */\n getTrackedAddress(index: number): TrackedAddress | undefined {\n this.ensureReady();\n const entry = this._trackedAddresses.get(index);\n if (!entry) return undefined;\n const nametag = this._addressNametags.get(entry.addressId)?.get(0);\n return { ...entry, nametag };\n }\n\n /**\n * Set visibility of a tracked address.\n * Hidden addresses are not returned by getActiveAddresses() but remain tracked.\n *\n * @param index - Address index to hide/unhide\n * @param hidden - true to hide, false to show\n * @throws Error if address index is not tracked\n */\n async setAddressHidden(index: number, hidden: boolean): Promise<void> {\n this.ensureReady();\n const entry = this._trackedAddresses.get(index);\n if (!entry) {\n throw new Error(`Address at index ${index} is not tracked. Switch to it first.`);\n }\n if (entry.hidden === hidden) return;\n\n (entry as { hidden: boolean }).hidden = hidden;\n await this.persistTrackedAddresses();\n\n const eventType = hidden ? 'address:hidden' : 'address:unhidden';\n this.emitEvent(eventType, { index, addressId: entry.addressId });\n }\n\n /**\n * Switch to a different address by index\n * This changes the active identity to the derived address at the specified index.\n *\n * @param index - Address index to switch to (0, 1, 2, ...)\n *\n * @example\n * ```ts\n * // Switch to second address\n * await sphere.switchToAddress(1);\n * console.log(sphere.identity?.address); // alpha1... (address at index 1)\n *\n * // Register nametag for this address\n * await sphere.registerNametag('bob');\n *\n * // Switch back to first address\n * await sphere.switchToAddress(0);\n * ```\n */\n async switchToAddress(index: number, options?: { nametag?: string }): Promise<void> {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code. Cannot switch addresses.');\n }\n\n if (index < 0) {\n throw new Error('Address index must be non-negative');\n }\n\n // If nametag requested, normalize and validate format early\n const newNametag = options?.nametag ? this.cleanNametag(options.nametag) : undefined;\n if (newNametag && !isValidNametag(newNametag)) {\n throw new Error('Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.');\n }\n\n // Derive the address at the given index\n const addressInfo = this.deriveAddress(index, false);\n\n // Generate IPNS name from public key hash\n const ipnsHash = sha256(addressInfo.publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);\n\n // Ensure address is tracked in the registry\n await this.ensureAddressTracked(index);\n const addressId = getAddressId(predicateAddress);\n\n // If nametag requested, check availability and store it BEFORE building identity\n if (newNametag) {\n const existing = await this._transport.resolveNametag?.(newNametag);\n if (existing) {\n throw new Error(`Nametag @${newNametag} is already taken`);\n }\n\n // Pre-populate nametag cache so identity is built WITH nametag\n let nametags = this._addressNametags.get(addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(addressId, nametags);\n }\n nametags.set(0, newNametag);\n }\n\n const nametag = this._addressNametags.get(addressId)?.get(0);\n\n // Update identity\n this._identity = {\n privateKey: addressInfo.privateKey,\n chainPubkey: addressInfo.publicKey,\n l1Address: addressInfo.address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n nametag,\n };\n\n // Update current index\n this._currentAddressIndex = index;\n await this._updateCachedProxyAddress();\n\n // Persist current index\n await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());\n\n // Re-initialize providers with new identity\n this._storage.setIdentity(this._identity);\n await this._transport.setIdentity(this._identity);\n\n // Update token storage providers and re-open databases for new address\n for (const provider of this._tokenStorageProviders.values()) {\n provider.setIdentity(this._identity);\n await provider.initialize();\n }\n\n // Re-initialize modules with new identity\n await this.reinitializeModulesForNewAddress();\n\n // Publish identity binding (with nametag if present — single atomic publish)\n if (this._identity.nametag) {\n await this.syncIdentityWithTransport();\n }\n\n // If new nametag was registered, persist cache and mint token\n if (newNametag) {\n await this.persistAddressNametags();\n\n if (!this._payments.hasNametag()) {\n console.log(`[Sphere] Minting nametag token for @${newNametag}...`);\n try {\n const result = await this.mintNametag(newNametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed:`, err);\n }\n }\n\n this.emitEvent('nametag:registered', {\n nametag: newNametag,\n addressIndex: index,\n });\n } else if (this._identity.nametag && !this._payments.hasNametag()) {\n // Existing address with nametag but missing token — mint it\n console.log(`[Sphere] Nametag @${this._identity.nametag} has no token after switch, minting...`);\n try {\n const result = await this.mintNametag(this._identity.nametag);\n if (result.success) {\n console.log(`[Sphere] Nametag token minted successfully after switch`);\n } else {\n console.warn(`[Sphere] Could not mint nametag token after switch: ${result.error}`);\n }\n } catch (err) {\n console.warn(`[Sphere] Nametag token mint failed after switch:`, err);\n }\n }\n\n this.emitEvent('identity:changed', {\n l1Address: this._identity.l1Address,\n directAddress: this._identity.directAddress,\n chainPubkey: this._identity.chainPubkey,\n nametag: this._identity.nametag,\n addressIndex: index,\n });\n\n console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);\n }\n\n /**\n * Re-initialize modules after address switch\n */\n private async reinitializeModulesForNewAddress(): Promise<void> {\n const emitEvent = this.emitEvent.bind(this);\n\n this._payments.initialize({\n identity: this._identity!,\n storage: this._storage,\n tokenStorageProviders: this._tokenStorageProviders,\n transport: this._transport,\n oracle: this._oracle,\n emitEvent,\n chainCode: this._masterKey?.chainCode || undefined,\n price: this._priceProvider ?? undefined,\n });\n\n this._communications.initialize({\n identity: this._identity!,\n storage: this._storage,\n transport: this._transport,\n emitEvent,\n });\n\n this._groupChat?.initialize({\n identity: this._identity!,\n storage: this._storage,\n emitEvent,\n });\n\n await this._payments.load();\n await this._communications.load();\n await this._groupChat?.load();\n }\n\n /**\n * Derive address at a specific index\n *\n * @param index - Address index (0, 1, 2, ...)\n * @param isChange - Whether this is a change address (default: false)\n * @returns Address info with privateKey, publicKey, address, path, index\n *\n * @example\n * ```ts\n * // Derive first receiving address\n * const addr0 = sphere.deriveAddress(0);\n * console.log(addr0.address); // alpha1...\n *\n * // Derive second receiving address\n * const addr1 = sphere.deriveAddress(1);\n *\n * // Derive change address\n * const change = sphere.deriveAddress(0, true);\n * ```\n */\n deriveAddress(index: number, isChange: boolean = false): AddressInfo {\n this.ensureReady();\n return this._deriveAddressInternal(index, isChange);\n }\n\n /**\n * Internal address derivation without ensureReady() check.\n * Used during initialization (loadTrackedAddresses, ensureAddressTracked)\n * when _initialized is still false.\n */\n private _deriveAddressInternal(index: number, isChange: boolean = false): AddressInfo {\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code');\n }\n\n // WIF/HMAC mode: legacy HMAC-SHA512 derivation (no chain code, no change addresses)\n if (this._derivationMode === 'wif_hmac') {\n return generateAddressFromMasterKey(this._masterKey.privateKey, index);\n }\n\n const info = deriveAddressInfo(\n this._masterKey,\n this._basePath,\n index,\n isChange\n );\n\n // Convert to proper bech32 address format\n return {\n ...info,\n address: publicKeyToAddress(info.publicKey, 'alpha'),\n };\n }\n\n /**\n * Derive address at a full BIP32 path\n *\n * @param path - Full BIP32 path like \"m/44'/0'/0'/0/5\"\n * @returns Address info\n *\n * @example\n * ```ts\n * const addr = sphere.deriveAddressAtPath(\"m/44'/0'/0'/0/5\");\n * ```\n */\n deriveAddressAtPath(path: string): AddressInfo {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('HD derivation requires master key with chain code');\n }\n\n // Parse path to extract index\n const match = path.match(/\\/(\\d+)$/);\n const index = match ? parseInt(match[1], 10) : 0;\n\n const derived = deriveKeyAtPath(\n this._masterKey.privateKey,\n this._masterKey.chainCode,\n path\n );\n\n const publicKey = getPublicKey(derived.privateKey);\n\n return {\n privateKey: derived.privateKey,\n publicKey,\n address: publicKeyToAddress(publicKey, 'alpha'),\n path,\n index,\n };\n }\n\n /**\n * Derive multiple addresses starting from index 0\n *\n * @param count - Number of addresses to derive\n * @param includeChange - Include change addresses (default: false)\n * @returns Array of address info\n *\n * @example\n * ```ts\n * // Get first 5 receiving addresses\n * const addresses = sphere.deriveAddresses(5);\n *\n * // Get 5 receiving + 5 change addresses\n * const allAddresses = sphere.deriveAddresses(5, true);\n * ```\n */\n deriveAddresses(count: number, includeChange: boolean = false): AddressInfo[] {\n const addresses: AddressInfo[] = [];\n\n for (let i = 0; i < count; i++) {\n addresses.push(this.deriveAddress(i, false));\n }\n\n if (includeChange) {\n for (let i = 0; i < count; i++) {\n addresses.push(this.deriveAddress(i, true));\n }\n }\n\n return addresses;\n }\n\n /**\n * Scan blockchain addresses to discover used addresses with balances.\n * Derives addresses sequentially and checks L1 balance via Fulcrum.\n * Uses gap limit to stop after N consecutive empty addresses.\n *\n * @param options - Scanning options\n * @returns Scan results with found addresses and total balance\n *\n * @example\n * ```ts\n * const result = await sphere.scanAddresses({\n * maxAddresses: 100,\n * gapLimit: 20,\n * onProgress: (p) => console.log(`Scanned ${p.scanned}/${p.total}, found ${p.foundCount}`),\n * });\n * console.log(`Found ${result.addresses.length} addresses, total: ${result.totalBalance} ALPHA`);\n * ```\n */\n async scanAddresses(options: ScanAddressesOptions = {}): Promise<ScanAddressesResult> {\n this.ensureReady();\n\n if (!this._masterKey) {\n throw new Error('Address scanning requires HD master key');\n }\n\n // Auto-provide nametag resolver from transport if caller didn't supply one\n const resolveNametag = options.resolveNametag ?? (\n this._transport.resolveAddressInfo\n ? async (l1Address: string): Promise<string | null> => {\n try {\n const info = await this._transport.resolveAddressInfo!(l1Address);\n return info?.nametag ?? null;\n } catch { return null; }\n }\n : undefined\n );\n\n return scanAddressesImpl(\n (index, isChange) => this._deriveAddressInternal(index, isChange),\n { ...options, resolveNametag },\n );\n }\n\n /**\n * Bulk-track scanned addresses with visibility and nametag data.\n * Selected addresses get `hidden: false`, unselected get `hidden: true`.\n * Performs only 2 storage writes total (tracked addresses + nametags).\n */\n async trackScannedAddresses(\n entries: Array<{ index: number; hidden: boolean; nametag?: string }>,\n ): Promise<void> {\n this.ensureReady();\n\n for (const { index, hidden, nametag } of entries) {\n const tracked = await this.ensureAddressTracked(index);\n\n if (nametag) {\n let nametags = this._addressNametags.get(tracked.addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(tracked.addressId, nametags);\n }\n if (!nametags.has(0)) nametags.set(0, nametag);\n }\n\n if (tracked.hidden !== hidden) {\n (tracked as { hidden: boolean }).hidden = hidden;\n }\n }\n\n await this.persistTrackedAddresses();\n await this.persistAddressNametags();\n }\n\n // ===========================================================================\n // Public Methods - Status\n // ===========================================================================\n\n getStatus(): {\n storage: { connected: boolean };\n transport: { connected: boolean };\n oracle: { connected: boolean };\n } {\n return {\n storage: { connected: this._storage.isConnected() },\n transport: { connected: this._transport.isConnected() },\n oracle: { connected: this._oracle.isConnected() },\n };\n }\n\n async reconnect(): Promise<void> {\n await this._transport.disconnect();\n await this._transport.connect();\n\n this.emitEvent('connection:changed', {\n provider: 'transport',\n connected: true,\n });\n }\n\n // ===========================================================================\n // Public Methods - Events\n // ===========================================================================\n\n on<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): () => void {\n if (!this.eventHandlers.has(type)) {\n this.eventHandlers.set(type, new Set());\n }\n this.eventHandlers.get(type)!.add(handler as SphereEventHandler<SphereEventType>);\n\n return () => {\n this.eventHandlers.get(type)?.delete(handler as SphereEventHandler<SphereEventType>);\n };\n }\n\n off<T extends SphereEventType>(type: T, handler: SphereEventHandler<T>): void {\n this.eventHandlers.get(type)?.delete(handler as SphereEventHandler<SphereEventType>);\n }\n\n // ===========================================================================\n // Public Methods - Sync\n // ===========================================================================\n\n async sync(): Promise<void> {\n this.ensureReady();\n await this._payments.sync();\n }\n\n // ===========================================================================\n // Public Methods - Nametag\n // ===========================================================================\n\n /**\n * Get current nametag (if registered)\n */\n getNametag(): string | undefined {\n return this._identity?.nametag;\n }\n\n /**\n * Check if nametag is registered\n */\n hasNametag(): boolean {\n return !!this._identity?.nametag;\n }\n\n /**\n * Get the PROXY address for the current nametag\n * PROXY addresses are derived from the nametag hash and require\n * the nametag token to claim funds sent to them\n * @returns PROXY address string or undefined if no nametag\n */\n getProxyAddress(): string | undefined {\n return this._cachedProxyAddress;\n }\n\n /**\n * Resolve any identifier to full peer information.\n * Accepts @nametag, bare nametag, DIRECT://, PROXY://, L1 address, or transport pubkey.\n *\n * @example\n * ```ts\n * const peer = await sphere.resolve('@alice');\n * const peer = await sphere.resolve('DIRECT://...');\n * const peer = await sphere.resolve('alpha1...');\n * const peer = await sphere.resolve('ab12cd...'); // 64-char hex transport pubkey\n * ```\n */\n async resolve(identifier: string): Promise<PeerInfo | null> {\n this.ensureReady();\n return this._transport.resolve?.(identifier) ?? null;\n }\n\n /** Compute and cache the PROXY address from the current nametag */\n private async _updateCachedProxyAddress(): Promise<void> {\n const nametag = this._identity?.nametag;\n if (!nametag) {\n this._cachedProxyAddress = undefined;\n return;\n }\n const { ProxyAddress } = await import('@unicitylabs/state-transition-sdk/lib/address/ProxyAddress');\n const proxyAddr = await ProxyAddress.fromNameTag(nametag);\n this._cachedProxyAddress = proxyAddr.toString();\n }\n\n /**\n * Register a nametag for the current active address\n * Each address can have its own independent nametag\n *\n * @example\n * ```ts\n * // Register nametag for first address (index 0)\n * await sphere.registerNametag('alice');\n *\n * // Switch to second address and register different nametag\n * await sphere.switchToAddress(1);\n * await sphere.registerNametag('bob');\n *\n * // Now:\n * // - Address 0 has nametag @alice\n * // - Address 1 has nametag @bob\n * ```\n */\n async registerNametag(nametag: string): Promise<void> {\n this.ensureReady();\n\n // Normalize and validate nametag format\n const cleanNametag = this.cleanNametag(nametag);\n if (!isValidNametag(cleanNametag)) {\n throw new Error('Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.');\n }\n\n // Check if current address already has a nametag\n if (this._identity?.nametag) {\n throw new Error(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`);\n }\n\n // Publish identity binding with nametag (updates existing binding event)\n if (this._transport.publishIdentityBinding) {\n const success = await this._transport.publishIdentityBinding(\n this._identity!.chainPubkey,\n this._identity!.l1Address,\n this._identity!.directAddress || '',\n cleanNametag,\n );\n if (!success) {\n throw new Error('Failed to register nametag. It may already be taken.');\n }\n }\n\n // Update identity\n this._identity!.nametag = cleanNametag;\n await this._updateCachedProxyAddress();\n\n // Update nametag cache\n const currentAddressId = this._trackedAddresses.get(this._currentAddressIndex)?.addressId;\n if (currentAddressId) {\n let nametags = this._addressNametags.get(currentAddressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(currentAddressId, nametags);\n }\n nametags.set(0, cleanNametag);\n }\n\n // Persist nametag cache\n await this.persistAddressNametags();\n\n // Mint nametag token on-chain if not already minted\n // Required for receiving tokens via @nametag (PROXY address finalization)\n if (!this._payments.hasNametag()) {\n console.log(`[Sphere] Minting nametag token for @${cleanNametag}...`);\n const result = await this.mintNametag(cleanNametag);\n if (!result.success) {\n console.warn(`[Sphere] Failed to mint nametag token: ${result.error}`);\n // Don't throw - nametag is published via transport, token can be minted later\n } else {\n console.log(`[Sphere] Nametag token minted successfully`);\n }\n }\n\n this.emitEvent('nametag:registered', {\n nametag: cleanNametag,\n addressIndex: this._currentAddressIndex,\n });\n console.log(`[Sphere] Nametag registered for address ${this._currentAddressIndex}:`, cleanNametag);\n }\n\n /**\n * Persist tracked addresses to storage (only minimal fields via StorageProvider)\n */\n private async persistTrackedAddresses(): Promise<void> {\n const entries: TrackedAddressEntry[] = [];\n for (const entry of this._trackedAddresses.values()) {\n entries.push({\n index: entry.index,\n hidden: entry.hidden,\n createdAt: entry.createdAt,\n updatedAt: entry.updatedAt,\n });\n }\n await this._storage.saveTrackedAddresses(entries);\n }\n\n /**\n * Mint a nametag token on-chain (like Sphere wallet and lottery)\n * This creates the nametag token required for receiving tokens via PROXY addresses (@nametag)\n *\n * @param nametag - The nametag to mint (e.g., \"alice\" or \"@alice\")\n * @returns MintNametagResult with success status and token if successful\n *\n * @example\n * ```typescript\n * // Mint nametag token for receiving via @alice\n * const result = await sphere.mintNametag('alice');\n * if (result.success) {\n * console.log('Nametag minted:', result.nametagData?.name);\n * } else {\n * console.error('Mint failed:', result.error);\n * }\n * ```\n */\n async mintNametag(nametag: string): Promise<import('../modules/payments').MintNametagResult> {\n this.ensureReady();\n return this._payments.mintNametag(nametag);\n }\n\n /**\n * Check if a nametag is available for minting\n * @param nametag - The nametag to check (e.g., \"alice\" or \"@alice\")\n * @returns true if available, false if taken or error\n */\n async isNametagAvailable(nametag: string): Promise<boolean> {\n this.ensureReady();\n return this._payments.isNametagAvailable(nametag);\n }\n\n /**\n * Load tracked addresses from storage.\n * Falls back to migrating from old ADDRESS_NAMETAGS format.\n */\n private async loadTrackedAddresses(): Promise<void> {\n this._trackedAddresses.clear();\n this._addressIdToIndex.clear();\n\n try {\n // Load minimal entries from storage\n const entries = await this._storage.loadTrackedAddresses();\n if (entries.length > 0) {\n for (const stored of entries) {\n // Derive address fields from index (internal: no ensureReady check)\n const addrInfo = this._deriveAddressInternal(stored.index, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n const entry: TrackedAddress = {\n ...stored,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n };\n this._trackedAddresses.set(entry.index, entry);\n this._addressIdToIndex.set(addressId, entry.index);\n }\n return;\n }\n\n // Fall back to old ADDRESS_NAMETAGS format and migrate\n const oldData = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n if (oldData) {\n const parsed = JSON.parse(oldData) as Record<string, unknown>;\n await this.migrateFromOldNametagFormat(parsed);\n await this.persistTrackedAddresses();\n }\n } catch {\n // Ignore parse errors - start fresh\n }\n }\n\n /**\n * Migrate from old ADDRESS_NAMETAGS format to tracked addresses.\n * Scans HD indices 0..19 to match addressIds from the old format.\n * Populates both _trackedAddresses and _addressNametags.\n */\n private async migrateFromOldNametagFormat(\n parsed: Record<string, unknown>\n ): Promise<void> {\n const addressIdToNametags = new Map<string, Record<string, string>>();\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'object' && value !== null) {\n addressIdToNametags.set(key, value as Record<string, string>);\n }\n }\n\n if (addressIdToNametags.size === 0 || !this._masterKey) return;\n\n const SCAN_LIMIT = 20;\n for (let i = 0; i < SCAN_LIMIT && addressIdToNametags.size > 0; i++) {\n try {\n const addrInfo = this._deriveAddressInternal(i, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n if (addressIdToNametags.has(addressId)) {\n const nametagsObj = addressIdToNametags.get(addressId)!;\n\n // Populate nametag cache\n const nametagMap = new Map<number, string>();\n for (const [idx, tag] of Object.entries(nametagsObj)) {\n nametagMap.set(parseInt(idx, 10), tag);\n }\n if (nametagMap.size > 0) {\n this._addressNametags.set(addressId, nametagMap);\n }\n\n // Create tracked address entry\n const now = Date.now();\n const entry: TrackedAddress = {\n index: i,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n nametag: nametagMap.get(0),\n hidden: false,\n createdAt: now,\n updatedAt: now,\n };\n\n this._trackedAddresses.set(i, entry);\n this._addressIdToIndex.set(addressId, i);\n addressIdToNametags.delete(addressId);\n }\n } catch {\n // Skip indices that fail to derive\n }\n }\n\n // Persist nametag cache separately\n await this.persistAddressNametags();\n }\n\n /**\n * Ensure an address is tracked in the registry.\n * If not yet tracked, derives full info and creates the entry.\n */\n private async ensureAddressTracked(index: number): Promise<TrackedAddress> {\n const existing = this._trackedAddresses.get(index);\n if (existing) return existing;\n\n const addrInfo = this._deriveAddressInternal(index, false);\n const directAddress = await deriveL3PredicateAddress(addrInfo.privateKey);\n const addressId = getAddressId(directAddress);\n\n const now = Date.now();\n const nametag = this._addressNametags.get(addressId)?.get(0);\n const entry: TrackedAddress = {\n index,\n addressId,\n l1Address: addrInfo.address,\n directAddress,\n chainPubkey: addrInfo.publicKey,\n nametag,\n hidden: false,\n createdAt: now,\n updatedAt: now,\n };\n\n this._trackedAddresses.set(index, entry);\n this._addressIdToIndex.set(addressId, index);\n await this.persistTrackedAddresses();\n\n this.emitEvent('address:activated', { address: { ...entry } });\n return entry;\n }\n\n /**\n * Persist nametag cache to storage.\n * Format: { addressId: { \"0\": \"alice\", \"1\": \"alice2\" } }\n */\n private async persistAddressNametags(): Promise<void> {\n const result: Record<string, Record<string, string>> = {};\n for (const [addressId, nametags] of this._addressNametags.entries()) {\n const obj: Record<string, string> = {};\n for (const [idx, tag] of nametags.entries()) {\n obj[idx.toString()] = tag;\n }\n result[addressId] = obj;\n }\n await this._storage.set(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS, JSON.stringify(result));\n }\n\n /**\n * Load nametag cache from storage.\n */\n private async loadAddressNametags(): Promise<void> {\n this._addressNametags.clear();\n try {\n const data = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);\n if (!data) return;\n const parsed = JSON.parse(data) as Record<string, Record<string, string>>;\n for (const [addressId, nametags] of Object.entries(parsed)) {\n const map = new Map<number, string>();\n for (const [idx, tag] of Object.entries(nametags)) {\n map.set(parseInt(idx, 10), tag);\n }\n this._addressNametags.set(addressId, map);\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n /**\n * Publish identity binding via transport.\n * Always publishes base identity (chainPubkey, l1Address, directAddress).\n * If nametag is set, also publishes nametag hash, proxy address, encrypted nametag.\n */\n private async syncIdentityWithTransport(): Promise<void> {\n if (!this._transport.publishIdentityBinding) {\n return; // Transport doesn't support identity binding\n }\n\n try {\n const nametag = this._identity?.nametag;\n const success = await this._transport.publishIdentityBinding(\n this._identity!.chainPubkey,\n this._identity!.l1Address,\n this._identity!.directAddress || '',\n nametag || undefined,\n );\n if (success) {\n console.log(`[Sphere] Identity binding published${nametag ? ` with nametag @${nametag}` : ''}`);\n } else if (nametag) {\n console.warn(`[Sphere] Nametag @${nametag} is taken by another pubkey`);\n }\n } catch (error) {\n // Don't fail wallet load on identity sync errors\n console.warn(`[Sphere] Identity binding sync failed:`, error);\n }\n }\n\n /**\n * Recover nametag from transport after wallet import.\n * Searches for encrypted nametag events authored by this wallet's pubkey\n * and decrypts them to restore the nametag association.\n */\n private async recoverNametagFromTransport(): Promise<void> {\n // Skip if already has a nametag\n if (this._identity?.nametag) {\n return;\n }\n\n let recoveredNametag: string | null = null;\n\n // Strategy 1: Decrypt nametag from own Nostr binding events (private-key based)\n if (this._transport.recoverNametag) {\n try {\n recoveredNametag = await this._transport.recoverNametag();\n } catch {\n // Non-fatal — try fallback\n }\n }\n\n // Strategy 2: Forward lookup by L1 address hash (public, same as scanAddresses).\n // Covers edge cases where the encrypted binding event was lost from relay.\n if (!recoveredNametag && this._transport.resolveAddressInfo && this._identity?.l1Address) {\n try {\n const info = await this._transport.resolveAddressInfo(this._identity.l1Address);\n if (info?.nametag) {\n recoveredNametag = info.nametag;\n }\n } catch {\n // Non-fatal\n }\n }\n\n if (!recoveredNametag) {\n return;\n }\n\n try {\n // Update identity with recovered nametag\n if (this._identity) {\n (this._identity as MutableFullIdentity).nametag = recoveredNametag;\n await this._updateCachedProxyAddress();\n }\n\n // Update nametag cache\n const entry = await this.ensureAddressTracked(this._currentAddressIndex);\n let nametags = this._addressNametags.get(entry.addressId);\n if (!nametags) {\n nametags = new Map();\n this._addressNametags.set(entry.addressId, nametags);\n }\n const nextIndex = nametags.size;\n nametags.set(nextIndex, recoveredNametag);\n await this.persistAddressNametags();\n\n // Note: no need to re-publish here — callers follow up with\n // syncIdentityWithTransport() which will publish WITH the recovered nametag.\n\n this.emitEvent('nametag:recovered', { nametag: recoveredNametag });\n } catch {\n // Don't fail wallet import on nametag recovery errors\n }\n }\n\n /**\n * Strip @ prefix and normalize a nametag (lowercase, phone E.164, strip @unicity suffix).\n */\n private cleanNametag(raw: string): string {\n const stripped = raw.startsWith('@') ? raw.slice(1) : raw;\n return normalizeNametag(stripped);\n }\n\n // ===========================================================================\n // Public Methods - Lifecycle\n // ===========================================================================\n\n async destroy(): Promise<void> {\n this._payments.destroy();\n this._communications.destroy();\n this._groupChat?.destroy();\n\n await this._transport.disconnect();\n await this._storage.disconnect();\n await this._oracle.disconnect();\n\n this._initialized = false;\n this._identity = null;\n this._trackedAddresses.clear();\n this._addressIdToIndex.clear();\n this._addressNametags.clear();\n this.eventHandlers.clear();\n\n if (Sphere.instance === this) {\n Sphere.instance = null;\n }\n }\n\n // ===========================================================================\n // Private: Storage\n // ===========================================================================\n\n private async storeMnemonic(mnemonic: string, derivationPath?: string, basePath?: string): Promise<void> {\n // TODO: Encrypt with user password/PIN\n const encrypted = this.encrypt(mnemonic);\n await this._storage.set(STORAGE_KEYS_GLOBAL.MNEMONIC, encrypted);\n\n // Store mnemonic in memory for getMnemonic()\n this._mnemonic = mnemonic;\n this._source = 'mnemonic';\n this._derivationMode = 'bip32';\n\n if (derivationPath) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);\n }\n\n const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;\n this._basePath = effectiveBasePath;\n await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);\n // Note: WALLET_EXISTS is set in finalizeWalletCreation() after successful initialization\n }\n\n private async storeMasterKey(\n masterKey: string,\n chainCode?: string,\n derivationPath?: string,\n basePath?: string,\n derivationMode?: DerivationMode\n ): Promise<void> {\n const encrypted = this.encrypt(masterKey);\n await this._storage.set(STORAGE_KEYS_GLOBAL.MASTER_KEY, encrypted);\n\n // Set source and derivation mode\n this._source = 'file';\n this._mnemonic = null;\n\n // Determine derivation mode from chain code if not specified\n if (derivationMode) {\n this._derivationMode = derivationMode;\n } else {\n this._derivationMode = chainCode ? 'bip32' : 'wif_hmac';\n }\n\n if (chainCode) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.CHAIN_CODE, chainCode);\n }\n\n if (derivationPath) {\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);\n }\n\n const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;\n this._basePath = effectiveBasePath;\n await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);\n await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);\n // Note: WALLET_EXISTS is set in finalizeWalletCreation() after successful initialization\n }\n\n /**\n * Mark wallet as fully created (after successful initialization)\n * This is called at the end of create()/import() to ensure wallet is only\n * marked as existing after all initialization steps succeed.\n */\n private async finalizeWalletCreation(): Promise<void> {\n await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_EXISTS, 'true');\n }\n\n // ===========================================================================\n // Private: Identity Initialization\n // ===========================================================================\n\n private async loadIdentityFromStorage(): Promise<void> {\n // Load keys that are saved with 'default' address (before identity is set)\n const encryptedMnemonic = await this._storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);\n const encryptedMasterKey = await this._storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);\n const chainCode = await this._storage.get(STORAGE_KEYS_GLOBAL.CHAIN_CODE);\n const derivationPath = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);\n const savedBasePath = await this._storage.get(STORAGE_KEYS_GLOBAL.BASE_PATH);\n const savedDerivationMode = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);\n const savedSource = await this._storage.get(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);\n const savedAddressIndex = await this._storage.get(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX);\n\n // Restore wallet metadata\n this._basePath = savedBasePath ?? DEFAULT_BASE_PATH;\n this._derivationMode = (savedDerivationMode as DerivationMode) ?? 'bip32';\n this._source = (savedSource as WalletSource) ?? 'unknown';\n this._currentAddressIndex = savedAddressIndex ? parseInt(savedAddressIndex, 10) : 0;\n\n if (encryptedMnemonic) {\n const mnemonic = this.decrypt(encryptedMnemonic);\n if (!mnemonic) {\n throw new Error('Failed to decrypt mnemonic');\n }\n this._mnemonic = mnemonic;\n this._source = 'mnemonic';\n await this.initializeIdentityFromMnemonic(mnemonic, derivationPath ?? undefined);\n } else if (encryptedMasterKey) {\n const masterKey = this.decrypt(encryptedMasterKey);\n if (!masterKey) {\n throw new Error('Failed to decrypt master key');\n }\n this._mnemonic = null;\n if (this._source === 'unknown') {\n this._source = 'file';\n }\n await this.initializeIdentityFromMasterKey(\n masterKey,\n chainCode ?? undefined,\n derivationPath ?? undefined\n );\n } else {\n throw new Error('No wallet data found in storage');\n }\n\n // Now that identity is restored, set it on storage so subsequent reads use correct address\n if (this._identity) {\n this._storage.setIdentity(this._identity);\n }\n\n // Load tracked addresses registry (with migration from old format)\n await this.loadTrackedAddresses();\n // Load nametag cache\n await this.loadAddressNametags();\n\n // Ensure current address is tracked\n const trackedEntry = await this.ensureAddressTracked(this._currentAddressIndex);\n const nametag = this._addressNametags.get(trackedEntry.addressId)?.get(0);\n\n // If we have a saved address index > 0 and master key, re-derive identity\n if (this._currentAddressIndex > 0 && this._masterKey) {\n const addressInfo = this._deriveAddressInternal(this._currentAddressIndex, false);\n const ipnsHash = sha256(addressInfo.publicKey, 'hex').slice(0, 40);\n const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);\n\n this._identity = {\n privateKey: addressInfo.privateKey,\n chainPubkey: addressInfo.publicKey,\n l1Address: addressInfo.address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n nametag,\n };\n this._storage.setIdentity(this._identity);\n console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);\n } else if (this._identity && nametag) {\n // Restore nametag from cache\n this._identity.nametag = nametag;\n }\n await this._updateCachedProxyAddress();\n }\n\n private async initializeIdentityFromMnemonic(\n mnemonic: string,\n derivationPath?: string\n ): Promise<void> {\n // Use base path (e.g., m/44'/0'/0') and append chain/index\n const basePath = derivationPath ?? DEFAULT_BASE_PATH;\n const fullPath = `${basePath}/0/0`;\n\n // Generate master key from mnemonic using BIP39/BIP32\n const masterKey = identityFromMnemonicSync(mnemonic);\n\n // Derive key at full path (e.g., m/44'/0'/0'/0/0)\n const derivedKey = deriveKeyAtPath(\n masterKey.privateKey,\n masterKey.chainCode,\n fullPath\n );\n\n // Get public key from derived private key\n const publicKey = getPublicKey(derivedKey.privateKey);\n\n // Generate proper bech32 address\n const address = publicKeyToAddress(publicKey, 'alpha');\n\n // Generate IPNS name from public key hash\n const ipnsHash = sha256(publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(derivedKey.privateKey);\n\n this._identity = {\n privateKey: derivedKey.privateKey,\n chainPubkey: publicKey,\n l1Address: address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n };\n\n // Store master key info for future derivations\n this._masterKey = masterKey;\n }\n\n private async initializeIdentityFromMasterKey(\n masterKey: string,\n chainCode?: string,\n _derivationPath?: string\n ): Promise<void> {\n // Use _basePath (already set by storeMasterKey) for consistency with deriveAddress/scan.\n // Previously used derivationPath param which was undefined for file imports,\n // causing identity to derive at DEFAULT_BASE_PATH instead of the wallet's actual path.\n const basePath = this._basePath;\n const fullPath = `${basePath}/0/0`;\n\n let privateKey: string;\n\n if (chainCode) {\n // Full BIP32 derivation with chain code\n const derivedKey = deriveKeyAtPath(masterKey, chainCode, fullPath);\n privateKey = derivedKey.privateKey;\n\n this._masterKey = {\n privateKey: masterKey,\n chainCode,\n };\n } else {\n // WIF/HMAC derivation without chain code\n // Uses HMAC-SHA512(masterKey, path) to derive child keys (legacy webwallet format)\n const addr0 = generateAddressFromMasterKey(masterKey, 0);\n privateKey = addr0.privateKey;\n\n // Store masterKey for future deriveAddress() calls (chainCode unused in wif_hmac mode)\n this._masterKey = {\n privateKey: masterKey,\n chainCode: '',\n };\n }\n\n const publicKey = getPublicKey(privateKey);\n const address = publicKeyToAddress(publicKey, 'alpha');\n const ipnsHash = sha256(publicKey, 'hex').slice(0, 40);\n\n // Derive L3 predicate address (DIRECT://...)\n const predicateAddress = await deriveL3PredicateAddress(privateKey);\n\n this._identity = {\n privateKey,\n chainPubkey: publicKey,\n l1Address: address,\n directAddress: predicateAddress,\n ipnsName: '12D3KooW' + ipnsHash,\n };\n }\n\n // ===========================================================================\n // Private: Provider & Module Initialization\n // ===========================================================================\n\n private async initializeProviders(): Promise<void> {\n // Set identity on providers\n this._storage.setIdentity(this._identity!);\n await this._transport.setIdentity(this._identity!);\n\n // Set identity on all token storage providers\n for (const provider of this._tokenStorageProviders.values()) {\n provider.setIdentity(this._identity!);\n }\n\n // Connect providers (skip if already connected, e.g. after setIdentity reconnect)\n if (!this._storage.isConnected()) {\n await this._storage.connect();\n }\n if (!this._transport.isConnected()) {\n await this._transport.connect();\n }\n await this._oracle.initialize();\n\n // Initialize all token storage providers\n for (const provider of this._tokenStorageProviders.values()) {\n await provider.initialize();\n }\n }\n\n private async initializeModules(): Promise<void> {\n const emitEvent = this.emitEvent.bind(this);\n\n this._payments.initialize({\n identity: this._identity!,\n storage: this._storage,\n tokenStorageProviders: this._tokenStorageProviders,\n transport: this._transport,\n oracle: this._oracle,\n emitEvent,\n // Pass chain code for L1 HD derivation\n chainCode: this._masterKey?.chainCode || undefined,\n price: this._priceProvider ?? undefined,\n });\n\n this._communications.initialize({\n identity: this._identity!,\n storage: this._storage,\n transport: this._transport,\n emitEvent,\n });\n\n this._groupChat?.initialize({\n identity: this._identity!,\n storage: this._storage,\n emitEvent,\n });\n\n await this._payments.load();\n await this._communications.load();\n await this._groupChat?.load();\n }\n\n // ===========================================================================\n // Private: Helpers\n // ===========================================================================\n\n private ensureReady(): void {\n if (!this._initialized) {\n throw new Error('Sphere not initialized');\n }\n }\n\n private emitEvent<T extends SphereEventType>(type: T, data: SphereEventMap[T]): void {\n const handlers = this.eventHandlers.get(type);\n if (!handlers) return;\n\n for (const handler of handlers) {\n try {\n (handler as SphereEventHandler<T>)(data);\n } catch (error) {\n console.error('[Sphere] Event handler error:', error);\n }\n }\n }\n\n // ===========================================================================\n // Private: Encryption\n // ===========================================================================\n\n private encrypt(data: string): string {\n // Use AES-256 encryption with default key\n // TODO: Add password parameter to create/load for user-provided encryption\n return encryptSimple(data, DEFAULT_ENCRYPTION_KEY);\n }\n\n private decrypt(encrypted: string): string | null {\n try {\n return decryptSimple(encrypted, DEFAULT_ENCRYPTION_KEY);\n } catch {\n return null;\n }\n }\n}\n\n// =============================================================================\n// Convenience Exports\n// =============================================================================\n\nexport const createSphere = Sphere.create.bind(Sphere);\nexport const loadSphere = Sphere.load.bind(Sphere);\nexport const importSphere = Sphere.import.bind(Sphere);\nexport const initSphere = Sphere.init.bind(Sphere);\nexport const getSphere = Sphere.getInstance.bind(Sphere);\nexport const sphereExists = Sphere.exists.bind(Sphere);\n","/**\n * Currency Utilities\n * Conversion between human-readable amounts and smallest units (bigint)\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Default token decimals (18 for most tokens) */\nexport const DEFAULT_TOKEN_DECIMALS = 18;\n\n// =============================================================================\n// Conversion Functions\n// =============================================================================\n\n/**\n * Convert human-readable amount to smallest unit (bigint)\n *\n * @example\n * ```ts\n * toSmallestUnit('1.5', 18) // 1500000000000000000n\n * toSmallestUnit('100', 6) // 100000000n\n * ```\n */\nexport function toSmallestUnit(amount: number | string, decimals: number = DEFAULT_TOKEN_DECIMALS): bigint {\n if (!amount) return 0n;\n\n try {\n const str = amount.toString();\n const [integer, fraction = ''] = str.split('.');\n\n // Pad fraction to exact decimal places, truncate if longer\n const paddedFraction = fraction.padEnd(decimals, '0').slice(0, decimals);\n\n return BigInt(integer + paddedFraction);\n } catch {\n return 0n;\n }\n}\n\n/**\n * Convert smallest unit (bigint) to human-readable string\n *\n * @example\n * ```ts\n * toHumanReadable(1500000000000000000n, 18) // '1.5'\n * toHumanReadable(100000000n, 6) // '100'\n * ```\n */\nexport function toHumanReadable(amount: bigint | string, decimals: number = DEFAULT_TOKEN_DECIMALS): string {\n const str = amount.toString().padStart(decimals + 1, '0');\n const integer = str.slice(0, -decimals) || '0';\n const fraction = str.slice(-decimals).replace(/0+$/, '');\n\n return fraction ? `${integer}.${fraction}` : integer;\n}\n\n/**\n * Format amount for display with optional symbol\n *\n * @example\n * ```ts\n * formatAmount(1500000000000000000n, { decimals: 18, symbol: 'ALPHA' })\n * // '1.5 ALPHA'\n * ```\n */\nexport function formatAmount(\n amount: bigint | string,\n options: {\n decimals?: number;\n symbol?: string;\n maxFractionDigits?: number;\n } = {}\n): string {\n const { decimals = DEFAULT_TOKEN_DECIMALS, symbol, maxFractionDigits } = options;\n\n let readable = toHumanReadable(amount, decimals);\n\n // Limit fraction digits if specified\n if (maxFractionDigits !== undefined) {\n const [int, frac] = readable.split('.');\n if (frac && frac.length > maxFractionDigits) {\n readable = maxFractionDigits > 0 ? `${int}.${frac.slice(0, maxFractionDigits)}` : int;\n }\n }\n\n return symbol ? `${readable} ${symbol}` : readable;\n}\n\n// =============================================================================\n// Export as namespace for convenience\n// =============================================================================\n\nexport const CurrencyUtils = {\n toSmallestUnit,\n toHumanReadable,\n format: formatAmount,\n};\n","export * from './Sphere';\nexport * from './scan';\nexport * from './crypto';\nexport * from './encryption';\nexport * from './currency';\nexport * from './bech32';\nexport * from './utils';\n"],"mappings":"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","names":["CryptoJS","generateMnemonic","validateMnemonic","mnemonicToSeed","mnemonicToSeedSync","mnemonicToEntropy","entropyToMnemonic","bytesToHex","CryptoJS","CryptoJS","CryptoJS","CryptoJS","elliptic","ec","elliptic","CryptoJS","CoinId","sha256","Token","TokenId","TokenState","HashAlgorithm","UnmaskedPredicate","waitInclusionProof","DEFAULT_DERIVATION_PATH","bytesToHex","Token","TokenId","TokenState","CoinId","TokenCoinData","TokenSplitBuilder","HashAlgorithm","UnmaskedPredicate","UnmaskedPredicateReference","TransferCommitment","waitInclusionProof","sha256","toHex","fromHex","Token","TokenState","TokenType","HashAlgorithm","UnmaskedPredicate","TransferCommitment","MintCommitment","MintTransactionData","waitInclusionProof","fromHex","MintTransactionData","MintCommitment","waitInclusionProof","TokenType","Token","TransferCommitment","UnmaskedPredicate","HashAlgorithm","TokenState","tokenJson","SdkToken","CoinId","TransferCommitment","TransferTransaction","UnmaskedPredicate","TokenState","HashAlgorithm","TokenType","MintCommitment","MintTransactionData","waitInclusionProof","fromHex","pending","result","requestId","UnmaskedPredicateReference","UNICITY_TOKEN_TYPE_HEX","token","TokenId","TokenState","TokenType","CoinId","HashAlgorithm","UnmaskedPredicate","broadcast","CryptoJS","encrypt","decrypt","connect","getBalance","CryptoJS","CryptoJS","CryptoJS","bytesToHex","CryptoJS","SigningService","TokenType","HashAlgorithm","UnmaskedPredicateReference","normalizeNametag","UNICITY_TOKEN_TYPE_HEX","sphere","validateMnemonic","generateMnemonic"]}