@veridex/sdk 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EVMClient-Bmy9czkE.d.mts → EVMClient-DqDiFak8.d.mts} +1 -1
- package/dist/auth/prepareAuth.d.mts +5 -4
- package/dist/auth/prepareAuth.js +143 -1422
- package/dist/auth/prepareAuth.js.map +1 -1
- package/dist/auth/prepareAuth.mjs +17 -96
- package/dist/auth/prepareAuth.mjs.map +1 -1
- package/dist/chains/aptos/index.d.mts +1 -1
- package/dist/chains/aptos/index.js +11 -33
- package/dist/chains/aptos/index.js.map +1 -1
- package/dist/chains/aptos/index.mjs +2 -2
- package/dist/chains/avalanche/index.d.mts +2 -2
- package/dist/chains/avalanche/index.js +19 -115
- package/dist/chains/avalanche/index.js.map +1 -1
- package/dist/chains/avalanche/index.mjs +3 -3
- package/dist/chains/evm/index.d.mts +5 -4
- package/dist/chains/evm/index.js +19 -115
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/chains/evm/index.mjs +2 -2
- package/dist/chains/solana/index.d.mts +1 -1
- package/dist/chains/solana/index.js +11 -33
- package/dist/chains/solana/index.js.map +1 -1
- package/dist/chains/solana/index.mjs +2 -2
- package/dist/chains/stacks/index.d.mts +1 -1
- package/dist/chains/stacks/index.js +11 -38
- package/dist/chains/stacks/index.js.map +1 -1
- package/dist/chains/stacks/index.mjs +2 -2
- package/dist/chains/starknet/index.d.mts +36 -2
- package/dist/chains/starknet/index.js +208 -37
- package/dist/chains/starknet/index.js.map +1 -1
- package/dist/chains/starknet/index.mjs +9 -4
- package/dist/chains/stellar/index.d.mts +2 -1
- package/dist/chains/sui/index.d.mts +1 -1
- package/dist/chains/sui/index.js +11 -27
- package/dist/chains/sui/index.js.map +1 -1
- package/dist/chains/sui/index.mjs +2 -2
- package/dist/{chunk-ZNIVNPGF.mjs → chunk-3PCAVZUZ.mjs} +173 -37
- package/dist/chunk-3PCAVZUZ.mjs.map +1 -0
- package/dist/{chunk-DQOW3G7M.mjs → chunk-6BINP4RT.mjs} +48 -9
- package/dist/{chunk-DQOW3G7M.mjs.map → chunk-6BINP4RT.mjs.map} +1 -1
- package/dist/{chunk-7KN6Y4GX.mjs → chunk-AK5HWYX4.mjs} +12 -40
- package/dist/chunk-AK5HWYX4.mjs.map +1 -0
- package/dist/{chunk-QVTADARB.mjs → chunk-FPSCAGYS.mjs} +12 -35
- package/dist/chunk-FPSCAGYS.mjs.map +1 -0
- package/dist/{chunk-USVZH4QC.mjs → chunk-H3WRT6Y4.mjs} +20 -117
- package/dist/chunk-H3WRT6Y4.mjs.map +1 -0
- package/dist/{chunk-GXJUY2SC.mjs → chunk-MNH22BM2.mjs} +12 -35
- package/dist/chunk-MNH22BM2.mjs.map +1 -0
- package/dist/chunk-N42LPFPJ.mjs +177 -0
- package/dist/chunk-N42LPFPJ.mjs.map +1 -0
- package/dist/{chunk-D57UJ4L5.mjs → chunk-NKSRF45N.mjs} +12 -29
- package/dist/chunk-NKSRF45N.mjs.map +1 -0
- package/dist/{chunk-BJH5HJL5.mjs → chunk-SKZLZFE2.mjs} +91 -65
- package/dist/chunk-SKZLZFE2.mjs.map +1 -0
- package/dist/{chunk-NVRAQPSF.mjs → chunk-UF5NWMSQ.mjs} +20 -4
- package/dist/chunk-UF5NWMSQ.mjs.map +1 -0
- package/dist/chunk-WC4X3MAO.mjs +58 -0
- package/dist/chunk-WC4X3MAO.mjs.map +1 -0
- package/dist/{chunk-5FH67KLG.mjs → chunk-XHDOSV2F.mjs} +2 -2
- package/dist/{index-CKKUV4J7.d.mts → index-D1nb2JV9.d.mts} +2 -2
- package/dist/index-qyOqd8tx.d.mts +517 -0
- package/dist/index.d.mts +30 -366
- package/dist/index.js +2412 -2486
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +986 -55
- package/dist/index.mjs.map +1 -1
- package/dist/passkey-fh8bo2w-.d.mts +241 -0
- package/dist/passkey.d.mts +2 -182
- package/dist/passkey.js +86 -7
- package/dist/passkey.js.map +1 -1
- package/dist/passkey.mjs +2 -1
- package/dist/payload.d.mts +22 -28
- package/dist/payload.js +50 -9
- package/dist/payload.js.map +1 -1
- package/dist/payload.mjs +7 -3
- package/dist/platform/index.d.mts +3 -0
- package/dist/platform/index.js +216 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +21 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/{portfolio-W2FQ2G3O.mjs → portfolio-MLP2JIFK.mjs} +2 -3
- package/dist/queries/index.mjs +324 -10
- package/dist/queries/index.mjs.map +1 -1
- package/dist/{types-DP2CQT8p.d.mts → types-CdlEbixV.d.mts} +12 -1
- package/dist/types.d.mts +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -1
- package/dist/chunk-72ZA3OYQ.mjs +0 -20
- package/dist/chunk-72ZA3OYQ.mjs.map +0 -1
- package/dist/chunk-7KN6Y4GX.mjs.map +0 -1
- package/dist/chunk-BJH5HJL5.mjs.map +0 -1
- package/dist/chunk-D57UJ4L5.mjs.map +0 -1
- package/dist/chunk-GXJUY2SC.mjs.map +0 -1
- package/dist/chunk-NVRAQPSF.mjs.map +0 -1
- package/dist/chunk-QVTADARB.mjs.map +0 -1
- package/dist/chunk-USVZH4QC.mjs.map +0 -1
- package/dist/chunk-XDIIFIW6.mjs +0 -330
- package/dist/chunk-XDIIFIW6.mjs.map +0 -1
- package/dist/chunk-ZNIVNPGF.mjs.map +0 -1
- package/dist/chunk-ZXMTPBSP.mjs +0 -857
- package/dist/chunk-ZXMTPBSP.mjs.map +0 -1
- /package/dist/{chunk-5FH67KLG.mjs.map → chunk-XHDOSV2F.mjs.map} +0 -0
- /package/dist/{portfolio-W2FQ2G3O.mjs.map → portfolio-MLP2JIFK.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chains/stacks/StacksSigner.ts","../src/chains/stacks/StacksAddressUtils.ts","../src/chains/stacks/StacksClient.ts","../src/chains/stacks/StacksPostConditions.ts"],"sourcesContent":["/**\n * Veridex Protocol SDK - Stacks Signature Format Conversion\n *\n * Converts WebAuthn/Passkey signatures to Clarity-compatible formats.\n *\n * Key conversions:\n * - WebAuthn DER-encoded signature → 64-byte compact (buff 64) for secp256r1-verify\n * - Uncompressed pubkey (x, y) → 33-byte compressed (buff 33)\n * - Session key signing via secp256k1 → 65-byte recoverable (buff 65)\n */\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** secp256r1 curve order (P-256) */\nconst P256_ORDER = BigInt(\n '0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551'\n);\n\n/** secp256r1 half-order for low-S normalization */\nconst P256_HALF_ORDER = P256_ORDER / 2n;\n\n// ============================================================================\n// Public Key Compression\n// ============================================================================\n\n/**\n * Compress a P-256 public key from (x, y) coordinates to 33-byte compressed format.\n * The prefix byte is 0x02 if y is even, 0x03 if y is odd.\n *\n * @param x - P-256 public key X coordinate\n * @param y - P-256 public key Y coordinate\n * @returns 33-byte compressed public key\n */\nexport function compressPublicKey(x: bigint, y: bigint): Uint8Array {\n const prefix = y % 2n === 0n ? 0x02 : 0x03;\n const xBytes = bigintToBytes(x, 32);\n const compressed = new Uint8Array(33);\n compressed[0] = prefix;\n compressed.set(xBytes, 1);\n return compressed;\n}\n\n// ============================================================================\n// Signature Conversion\n// ============================================================================\n\n/**\n * Convert (r, s) bigint pair to 64-byte compact signature for secp256r1-verify.\n * Applies low-S normalization (required by Clarity's secp256r1-verify).\n *\n * @param r - Signature r component\n * @param s - Signature s component\n * @returns 64-byte compact signature buffer\n */\nexport function rsToCompactSignature(r: bigint, s: bigint): Uint8Array {\n // Low-S normalization: if s > half-order, use order - s\n const normalizedS = s > P256_HALF_ORDER ? P256_ORDER - s : s;\n\n const compact = new Uint8Array(64);\n compact.set(bigintToBytes(r, 32), 0);\n compact.set(bigintToBytes(normalizedS, 32), 32);\n return compact;\n}\n\n/**\n * Parse a DER-encoded ECDSA signature into (r, s) components.\n * WebAuthn signatures are typically DER-encoded.\n *\n * DER format: 0x30 [total-len] 0x02 [r-len] [r] 0x02 [s-len] [s]\n *\n * @param der - DER-encoded signature bytes\n * @returns Object with r and s as bigints\n */\nexport function parseDERSignature(der: Uint8Array): { r: bigint; s: bigint } {\n if (der[0] !== 0x30) {\n throw new Error('Invalid DER signature: expected SEQUENCE tag 0x30');\n }\n\n let offset = 2; // Skip SEQUENCE tag and length\n\n // Parse r\n if (der[offset] !== 0x02) {\n throw new Error('Invalid DER signature: expected INTEGER tag 0x02 for r');\n }\n offset++;\n const rLen = der[offset]!;\n offset++;\n const rBytes = der.slice(offset, offset + rLen);\n offset += rLen;\n\n // Parse s\n if (der[offset] !== 0x02) {\n throw new Error('Invalid DER signature: expected INTEGER tag 0x02 for s');\n }\n offset++;\n const sLen = der[offset]!;\n offset++;\n const sBytes = der.slice(offset, offset + sLen);\n\n return {\n r: bytesToBigint(rBytes),\n s: bytesToBigint(sBytes),\n };\n}\n\n/**\n * Convert a DER-encoded signature to 64-byte compact format.\n * Combines DER parsing with compact encoding and low-S normalization.\n *\n * @param der - DER-encoded signature bytes\n * @returns 64-byte compact signature buffer\n */\nexport function derToCompactSignature(der: Uint8Array): Uint8Array {\n const { r, s } = parseDERSignature(der);\n return rsToCompactSignature(r, s);\n}\n\n// ============================================================================\n// Key Hash Computation\n// ============================================================================\n\n/**\n * Compute the key hash (SHA-256 of compressed public key).\n * This matches the Clarity contract's `(sha256 compressed-pubkey)`.\n *\n * @param compressedPubkey - 33-byte compressed public key\n * @returns 32-byte key hash as hex string (with 0x prefix)\n */\nexport async function computeKeyHash(compressedPubkey: Uint8Array): Promise<string> {\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', compressedPubkey.buffer as ArrayBuffer);\n const hashArray = new Uint8Array(hashBuffer);\n return '0x' + bytesToHex(hashArray);\n}\n\n/**\n * Compute the key hash from (x, y) public key coordinates.\n * Compresses the key first, then SHA-256 hashes it.\n *\n * @param x - P-256 public key X coordinate\n * @param y - P-256 public key Y coordinate\n * @returns 32-byte key hash as hex string (with 0x prefix)\n */\nexport async function computeKeyHashFromCoords(x: bigint, y: bigint): Promise<string> {\n const compressed = compressPublicKey(x, y);\n return computeKeyHash(compressed);\n}\n\n// ============================================================================\n// Message Hash Construction\n// ============================================================================\n\n/**\n * Build a registration message hash.\n * Format: SHA-256(\"veridex:register:<nonce>\")\n *\n * @param nonce - Registration nonce (typically 0 for first registration)\n * @returns 32-byte message hash\n */\nexport async function buildRegistrationHash(nonce: number): Promise<Uint8Array> {\n const message = `veridex:register:${nonce}`;\n const encoded = new TextEncoder().encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded.buffer as ArrayBuffer);\n return new Uint8Array(hashBuffer);\n}\n\n/**\n * Build a session registration message hash.\n * Format: SHA-256(\"veridex:session:<session-key-hash>:<duration>:<max-value>:<nonce>\")\n *\n * @param sessionKeyHash - Hex string of session key hash\n * @param duration - Session duration in blocks\n * @param maxValue - Maximum spending value in microSTX\n * @param nonce - Identity nonce\n * @returns 32-byte message hash\n */\nexport async function buildSessionRegistrationHash(\n sessionKeyHash: string,\n duration: number,\n maxValue: bigint,\n nonce: number\n): Promise<Uint8Array> {\n const cleanHash = sessionKeyHash.replace('0x', '');\n const message = `veridex:session:${cleanHash}:${duration}:${maxValue}:${nonce}`;\n const encoded = new TextEncoder().encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded.buffer as ArrayBuffer);\n return new Uint8Array(hashBuffer);\n}\n\n/**\n * Build a session revocation message hash.\n * Format: SHA-256(\"veridex:revoke:<session-hash>:<nonce>\")\n *\n * @param sessionHash - Hex string of session hash to revoke\n * @param nonce - Identity nonce\n * @returns 32-byte message hash\n */\nexport async function buildRevocationHash(\n sessionHash: string,\n nonce: number\n): Promise<Uint8Array> {\n const cleanHash = sessionHash.replace('0x', '');\n const message = `veridex:revoke:${cleanHash}:${nonce}`;\n const encoded = new TextEncoder().encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded.buffer as ArrayBuffer);\n return new Uint8Array(hashBuffer);\n}\n\n/**\n * Build an execute action message hash.\n * Format: SHA-256(\"veridex:execute:<action-type>:<amount>:<recipient>:<nonce>\")\n *\n * @param actionType - Action type (1=STX transfer, 2=sBTC transfer)\n * @param amount - Amount in base units\n * @param recipient - Stacks principal address\n * @param nonce - Identity nonce\n * @returns 32-byte message hash\n */\nexport async function buildExecuteHash(\n actionType: number,\n amount: bigint,\n recipient: string,\n nonce: number\n): Promise<Uint8Array> {\n const message = `veridex:execute:${actionType}:${amount}:${recipient}:${nonce}`;\n const encoded = new TextEncoder().encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded.buffer as ArrayBuffer);\n return new Uint8Array(hashBuffer);\n}\n\n/**\n * Build a withdrawal message hash.\n * Format: SHA-256(\"veridex:withdraw:<amount>:<recipient>:<nonce>\")\n *\n * @param amount - Amount in microSTX\n * @param recipient - Stacks principal address\n * @param nonce - Identity nonce (from spoke contract, not used for passkey withdrawals but kept for consistency)\n * @returns 32-byte message hash\n */\nexport async function buildWithdrawalHash(\n amount: bigint,\n recipient: string,\n nonce: number\n): Promise<Uint8Array> {\n const message = `veridex:withdraw:${amount}:${recipient}:${nonce}`;\n const encoded = new TextEncoder().encode(message);\n const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoded.buffer as ArrayBuffer);\n return new Uint8Array(hashBuffer);\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\n/**\n * Convert a bigint to a fixed-length byte array (big-endian).\n */\nfunction bigintToBytes(value: bigint, length: number): Uint8Array {\n const hex = value.toString(16).padStart(length * 2, '0');\n const bytes = new Uint8Array(length);\n for (let i = 0; i < length; i++) {\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Convert a byte array to a bigint (big-endian).\n * Handles leading zero bytes (positive integers in DER encoding).\n */\nfunction bytesToBigint(bytes: Uint8Array): bigint {\n // Skip leading zero bytes (DER positive integer padding)\n let start = 0;\n while (start < bytes.length - 1 && bytes[start] === 0) {\n start++;\n }\n let result = 0n;\n for (let i = start; i < bytes.length; i++) {\n result = (result << 8n) | BigInt(bytes[i]!);\n }\n return result;\n}\n\n/**\n * Convert a byte array to a hex string (no prefix).\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 * Convert a hex string to a byte array.\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const clean = hex.replace('0x', '');\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n","/**\n * Veridex Protocol SDK - Stacks Address Utilities\n *\n * Validates and manipulates Stacks principal addresses.\n * Stacks uses c32check encoding for addresses.\n *\n * Address formats:\n * - Standard principal: SP/ST + 33 chars (mainnet/testnet)\n * - Contract principal: SP/ST + 33 chars + \".\" + contract-name\n */\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Stacks mainnet address prefix */\nexport const STACKS_MAINNET_PREFIX = 'SP';\n\n/** Stacks testnet address prefix */\nexport const STACKS_TESTNET_PREFIX = 'ST';\n\n/** c32 character set used by Stacks addresses */\nconst C32_ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/**\n * Validate a Stacks principal address (standard or contract).\n *\n * @param address - Address to validate\n * @returns true if the address is a valid Stacks principal\n */\nexport function isValidStacksPrincipal(address: string): boolean {\n if (!address || typeof address !== 'string') {\n return false;\n }\n\n // Check for contract principal (address.contract-name)\n const parts = address.split('.');\n if (parts.length > 2) {\n return false;\n }\n\n const standardPart = parts[0]!;\n const contractName = parts[1];\n\n // Validate standard principal part\n if (!isValidStandardPrincipal(standardPart)) {\n return false;\n }\n\n // Validate contract name if present\n if (contractName !== undefined) {\n if (!isValidContractName(contractName)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate a standard Stacks principal (not contract).\n *\n * @param address - Standard principal address\n * @returns true if valid\n */\nexport function isValidStandardPrincipal(address: string): boolean {\n if (!address || address.length < 5) {\n return false;\n }\n\n // Must start with SP (mainnet) or ST (testnet)\n const prefix = address.slice(0, 2);\n if (prefix !== STACKS_MAINNET_PREFIX && prefix !== STACKS_TESTNET_PREFIX) {\n return false;\n }\n\n // Standard principals are typically 41 characters (SP/ST + 39 c32 chars)\n // but can vary slightly. Check reasonable bounds.\n if (address.length < 38 || address.length > 42) {\n return false;\n }\n\n // Verify all characters after prefix are valid c32\n const body = address.slice(1).toUpperCase();\n for (const char of body) {\n if (!C32_ALPHABET.includes(char)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Validate a Clarity contract name.\n * Contract names must be 1-128 chars, alphanumeric + hyphens, starting with alpha.\n *\n * @param name - Contract name to validate\n * @returns true if valid\n */\nexport function isValidContractName(name: string): boolean {\n if (!name || name.length === 0 || name.length > 128) {\n return false;\n }\n\n // Must start with a letter\n if (!/^[a-zA-Z]/.test(name)) {\n return false;\n }\n\n // Only alphanumeric and hyphens\n if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(name)) {\n return false;\n }\n\n return true;\n}\n\n// ============================================================================\n// Address Utilities\n// ============================================================================\n\n/**\n * Get the network type from a Stacks address.\n *\n * @param address - Stacks principal address\n * @returns 'mainnet' or 'testnet'\n */\nexport function getNetworkFromAddress(address: string): 'mainnet' | 'testnet' {\n const prefix = address.slice(0, 2);\n if (prefix === STACKS_MAINNET_PREFIX) {\n return 'mainnet';\n }\n return 'testnet';\n}\n\n/**\n * Build a contract principal from deployer address and contract name.\n *\n * @param deployerAddress - Standard principal of the deployer\n * @param contractName - Name of the contract\n * @returns Contract principal in \"address.contract-name\" format\n */\nexport function getContractPrincipal(\n deployerAddress: string,\n contractName: string\n): string {\n if (!isValidStandardPrincipal(deployerAddress)) {\n throw new Error(`Invalid deployer address: ${deployerAddress}`);\n }\n if (!isValidContractName(contractName)) {\n throw new Error(`Invalid contract name: ${contractName}`);\n }\n return `${deployerAddress}.${contractName}`;\n}\n\n/**\n * Parse a contract principal into deployer address and contract name.\n *\n * @param contractPrincipal - Full contract principal (e.g., \"ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.veridex-spoke\")\n * @returns Object with address and contractName\n */\nexport function parseContractPrincipal(contractPrincipal: string): {\n address: string;\n contractName: string;\n} {\n const dotIndex = contractPrincipal.indexOf('.');\n if (dotIndex === -1) {\n throw new Error(\n `Not a contract principal: ${contractPrincipal}. Expected format: address.contract-name`\n );\n }\n\n return {\n address: contractPrincipal.slice(0, dotIndex),\n contractName: contractPrincipal.slice(dotIndex + 1),\n };\n}\n\n/**\n * Check if an address is a contract principal.\n *\n * @param address - Address to check\n * @returns true if the address contains a contract name\n */\nexport function isContractPrincipal(address: string): boolean {\n return address.includes('.');\n}\n\n/**\n * Get the explorer URL for a Stacks transaction.\n *\n * @param txId - Transaction ID (hex string)\n * @param network - 'mainnet' or 'testnet'\n * @returns Full explorer URL\n */\nexport function getStacksExplorerTxUrl(\n txId: string,\n network: 'mainnet' | 'testnet' = 'testnet'\n): string {\n const cleanTxId = txId.startsWith('0x') ? txId : `0x${txId}`;\n const chainParam = network === 'testnet' ? '&chain=testnet' : '';\n return `https://explorer.hiro.so/txid/${cleanTxId}?${chainParam}`;\n}\n\n/**\n * Get the explorer URL for a Stacks address.\n *\n * @param address - Stacks principal address\n * @param network - 'mainnet' or 'testnet'\n * @returns Full explorer URL\n */\nexport function getStacksExplorerAddressUrl(\n address: string,\n network: 'mainnet' | 'testnet' = 'testnet'\n): string {\n const chainParam = network === 'testnet' ? '?chain=testnet' : '';\n return `https://explorer.hiro.so/address/${address}${chainParam}`;\n}\n","/**\n * Veridex Protocol SDK - Stacks Chain Client\n *\n * Production-grade implementation of ChainClient interface for Stacks.\n * Supports direct relay with native sponsored transactions.\n *\n * Security:\n * - Native secp256r1-verify for Passkey (P-256) validation\n * - Native secp256k1-verify for session key validation\n * - Protocol-level Post-Conditions for spending safety\n * - Nonce-based replay protection\n * - Block-height based session expiry\n *\n * Architecture:\n * - Phase 1: Direct relay (relayer sponsors Stacks transactions)\n * - Phase 2: Wormhole cross-chain messaging (VAA + CCQ)\n * - Vaults are map-based (Clarity doesn't support factory patterns)\n * - All identities stored in veridex-spoke.clar\n * - STX/sBTC custody in veridex-vault.clar (direct) + veridex-vault-vaa.clar (cross-chain)\n * - Guardian signature verification in veridex-wormhole-verifier.clar\n */\n\nimport type { SessionKey } from '../../sessions/types.js';\nimport type {\n ChainClient,\n ChainConfig,\n TransferParams,\n ExecuteParams,\n BridgeParams,\n DispatchResult,\n WebAuthnSignature,\n VaultCreationResult,\n RegisterSessionParams,\n RevokeSessionParams,\n SessionValidationResult,\n} from '../../core/types.js';\nimport { encodeTransferAction, encodeExecuteAction, encodeBridgeAction } from '../../payload.js';\nimport {\n compressPublicKey,\n rsToCompactSignature,\n computeKeyHashFromCoords,\n bytesToHex,\n hexToBytes,\n} from './StacksSigner.js';\nimport {\n parseContractPrincipal,\n isContractPrincipal,\n} from './StacksAddressUtils.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Stacks action types matching veridex-spoke.clar constants */\nexport const STACKS_ACTION_TYPES = {\n TRANSFER_STX: 1,\n TRANSFER_SBTC: 2,\n CONTRACT_CALL: 3,\n} as const;\n\n/** Default Hiro API endpoints */\nconst HIRO_API = {\n testnet: 'https://api.testnet.hiro.so',\n mainnet: 'https://api.hiro.so',\n} as const;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StacksClientConfig {\n /** Wormhole chain ID (60 for Stacks) */\n wormholeChainId: number;\n /** Stacks RPC URL (Hiro API) */\n rpcUrl: string;\n /** Spoke contract principal (e.g., \"ST1PQHQKV...veridex-spoke\") */\n spokeContractAddress?: string;\n /** Vault contract principal (e.g., \"ST1PQHQKV...veridex-vault\") */\n vaultContractAddress?: string;\n /** Wormhole verifier contract principal (Phase 2) */\n wormholeVerifierAddress?: string;\n /** VAA-authorized vault contract principal (Phase 2) */\n vaultVaaContractAddress?: string;\n /** Network type */\n network?: 'mainnet' | 'testnet';\n /** Hub RPC URL for cross-chain session management */\n hubRpcUrl?: string;\n /** Hub contract address for cross-chain session management */\n hubContractAddress?: string;\n}\n\n/** Parsed contract principal */\ninterface ContractId {\n address: string;\n name: string;\n}\n\n// ============================================================================\n// StacksClient\n// ============================================================================\n\nexport class StacksClient implements ChainClient {\n private config: ChainConfig;\n private rpcUrl: string;\n private spokeContract: ContractId | null;\n private vaultContract: ContractId | null;\n private wormholeVerifierContract: ContractId | null;\n private vaultVaaContract: ContractId | null;\n private networkType: 'mainnet' | 'testnet';\n\n constructor(clientConfig: StacksClientConfig) {\n this.networkType = clientConfig.network || 'testnet';\n this.rpcUrl = clientConfig.rpcUrl || HIRO_API[this.networkType];\n\n // Parse spoke contract principal\n if (clientConfig.spokeContractAddress && isContractPrincipal(clientConfig.spokeContractAddress)) {\n const parsed = parseContractPrincipal(clientConfig.spokeContractAddress);\n this.spokeContract = { address: parsed.address, name: parsed.contractName };\n } else {\n this.spokeContract = null;\n }\n\n // Parse vault contract principal\n if (clientConfig.vaultContractAddress && isContractPrincipal(clientConfig.vaultContractAddress)) {\n const parsed = parseContractPrincipal(clientConfig.vaultContractAddress);\n this.vaultContract = { address: parsed.address, name: parsed.contractName };\n } else {\n this.vaultContract = null;\n }\n\n // Parse wormhole verifier contract principal (Phase 2)\n if (clientConfig.wormholeVerifierAddress && isContractPrincipal(clientConfig.wormholeVerifierAddress)) {\n const parsed = parseContractPrincipal(clientConfig.wormholeVerifierAddress);\n this.wormholeVerifierContract = { address: parsed.address, name: parsed.contractName };\n } else {\n this.wormholeVerifierContract = null;\n }\n\n // Parse vault-vaa contract principal (Phase 2)\n if (clientConfig.vaultVaaContractAddress && isContractPrincipal(clientConfig.vaultVaaContractAddress)) {\n const parsed = parseContractPrincipal(clientConfig.vaultVaaContractAddress);\n this.vaultVaaContract = { address: parsed.address, name: parsed.contractName };\n } else {\n this.vaultVaaContract = null;\n }\n\n // If spoke is set but vault is not, derive vault from same deployer\n if (this.spokeContract && !this.vaultContract) {\n this.vaultContract = {\n address: this.spokeContract.address,\n name: 'veridex-vault',\n };\n }\n\n // Auto-derive Phase 2 contracts from spoke deployer if not explicitly set\n if (this.spokeContract && !this.wormholeVerifierContract) {\n this.wormholeVerifierContract = {\n address: this.spokeContract.address,\n name: 'veridex-wormhole-verifier',\n };\n }\n if (this.spokeContract && !this.vaultVaaContract) {\n this.vaultVaaContract = {\n address: this.spokeContract.address,\n name: 'veridex-vault-vaa',\n };\n }\n\n this.config = {\n name: `Stacks ${this.networkType}`,\n chainId: this.networkType === 'mainnet' ? 1 : 2147483648,\n wormholeChainId: clientConfig.wormholeChainId,\n rpcUrl: this.rpcUrl,\n explorerUrl: this.networkType === 'testnet'\n ? 'https://explorer.hiro.so/?chain=testnet'\n : 'https://explorer.hiro.so',\n isEvm: false,\n contracts: {\n hub: clientConfig.spokeContractAddress,\n wormholeCoreBridge: '',\n wormholeVerifier: this.wormholeVerifierContract\n ? `${this.wormholeVerifierContract.address}.${this.wormholeVerifierContract.name}`\n : undefined,\n vaultVaa: this.vaultVaaContract\n ? `${this.vaultVaaContract.address}.${this.vaultVaaContract.name}`\n : undefined,\n },\n };\n }\n\n // ========================================================================\n // ChainClient Interface - Configuration\n // ========================================================================\n\n getConfig(): ChainConfig {\n return this.config;\n }\n\n // ========================================================================\n // ChainClient Interface - Nonce & Fees\n // ========================================================================\n\n /**\n * Get the current nonce for a user identity from the spoke contract.\n * Calls the read-only function `get-nonce` on veridex-spoke.\n */\n async getNonce(userKeyHash: string): Promise<bigint> {\n if (!this.spokeContract) {\n return 0n;\n }\n\n try {\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'get-nonce',\n [`0x${userKeyHash.replace('0x', '')}`]\n );\n\n // Result is (ok uint) or (err uint)\n if (result && result.value !== undefined) {\n return BigInt(result.value);\n }\n return 0n;\n } catch {\n return 0n;\n }\n }\n\n /**\n * Get the Wormhole message fee.\n * Phase 1: No Wormhole integration, returns 0.\n */\n async getMessageFee(): Promise<bigint> {\n return 0n;\n }\n\n // ========================================================================\n // ChainClient Interface - Payload Building\n // ========================================================================\n\n async buildTransferPayload(params: TransferParams): Promise<string> {\n return encodeTransferAction(params.token, params.recipient, params.amount);\n }\n\n async buildExecutePayload(params: ExecuteParams): Promise<string> {\n return encodeExecuteAction(params.target, params.value, params.data);\n }\n\n async buildBridgePayload(params: BridgeParams): Promise<string> {\n return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);\n }\n\n // ========================================================================\n // ChainClient Interface - Dispatch\n // ========================================================================\n\n /**\n * Direct dispatch is not supported on Stacks in Phase 1.\n * Stacks actions are executed via sponsored transactions through the relayer.\n */\n async dispatch(\n _signature: WebAuthnSignature,\n _publicKeyX: bigint,\n _publicKeyY: bigint,\n _targetChain: number,\n _actionPayload: string,\n _nonce: bigint,\n _signer: unknown\n ): Promise<DispatchResult> {\n throw new Error(\n 'Direct dispatch not supported on Stacks in Phase 1. ' +\n 'Use dispatchGasless() to route through the relayer, which sponsors Stacks transactions. ' +\n 'Phase 2 will add Wormhole cross-chain dispatch support.'\n );\n }\n\n /**\n * Dispatch an action via the relayer (gasless/sponsored).\n *\n * Flow:\n * 1. User signs action with Passkey (on client)\n * 2. SDK submits to relayer with targetChain=60 (Stacks)\n * 3. Relayer builds Clarity contract-call transaction\n * 4. Relayer sponsors the transaction (pays STX gas)\n * 5. Relayer broadcasts to Stacks network\n * 6. Transaction confirmed on Stacks\n */\n async dispatchGasless(\n signature: WebAuthnSignature,\n publicKeyX: bigint,\n publicKeyY: bigint,\n targetChain: number,\n actionPayload: string,\n nonce: bigint,\n relayerUrl: string\n ): Promise<DispatchResult> {\n const keyHash = await computeKeyHashFromCoords(publicKeyX, publicKeyY);\n const compressedPubkey = compressPublicKey(publicKeyX, publicKeyY);\n const compactSig = rsToCompactSignature(signature.r, signature.s);\n\n const request = {\n signature: {\n r: '0x' + signature.r.toString(16).padStart(64, '0'),\n s: '0x' + signature.s.toString(16).padStart(64, '0'),\n authenticatorData: signature.authenticatorData,\n clientDataJSON: signature.clientDataJSON,\n challengeIndex: signature.challengeIndex,\n typeIndex: signature.typeIndex,\n },\n publicKeyX: '0x' + publicKeyX.toString(16).padStart(64, '0'),\n publicKeyY: '0x' + publicKeyY.toString(16).padStart(64, '0'),\n compressedPubkey: '0x' + bytesToHex(compressedPubkey),\n compactSignature: '0x' + bytesToHex(compactSig),\n targetChain,\n actionPayload,\n userNonce: Number(nonce),\n };\n\n const response = await fetch(`${relayerUrl}/api/v1/submit`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new Error(\n `Relayer submission failed: ${response.status} ${response.statusText}. ` +\n `Error: ${errorText}`\n );\n }\n\n const result = await response.json() as Record<string, unknown>;\n\n return {\n transactionHash: (result.transactionHash ?? result.txHash ?? result.hubTxHash ?? '') as string,\n sequence: BigInt((result.sequence as string | number) || 0),\n userKeyHash: keyHash,\n targetChain,\n };\n }\n\n // ========================================================================\n // ChainClient Interface - Vault Management\n // ========================================================================\n\n /**\n * Get vault address for a user.\n * On Stacks, vaults are map-based within the vault contract.\n * The \"vault address\" is the vault contract principal itself.\n */\n async getVaultAddress(userKeyHash: string): Promise<string | null> {\n if (!this.vaultContract) {\n return null;\n }\n\n // Check if identity exists in spoke contract\n const exists = await this.vaultExists(userKeyHash);\n if (!exists) {\n return null;\n }\n\n return `${this.vaultContract.address}.${this.vaultContract.name}`;\n }\n\n /**\n * Compute vault address deterministically.\n * On Stacks, all vaults live in the same contract (map-based).\n */\n computeVaultAddress(_userKeyHash: string): string {\n if (!this.vaultContract) {\n throw new Error('Vault contract not configured');\n }\n return `${this.vaultContract.address}.${this.vaultContract.name}`;\n }\n\n /**\n * Check if a vault (identity) exists for a user.\n * Queries the spoke contract's `identity-exists` read-only function.\n */\n async vaultExists(userKeyHash: string): Promise<boolean> {\n if (!this.spokeContract) {\n return false;\n }\n\n try {\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'identity-exists',\n [`0x${userKeyHash.replace('0x', '')}`]\n );\n\n return result === true || result?.value === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Create a vault (register identity) on Stacks.\n * Must be done via Hub dispatch or relayer in Phase 1.\n */\n async createVault(userKeyHash: string, _signer: unknown): Promise<VaultCreationResult> {\n throw new Error(\n 'Vault creation on Stacks requires Passkey signature verification. ' +\n 'Use createVaultViaRelayer() for sponsored identity registration, ' +\n 'or call register-identity directly with a signed Stacks transaction. ' +\n `KeyHash=${userKeyHash}`\n );\n }\n\n /**\n * Create a vault with a sponsor wallet.\n * On Stacks, this registers an identity via sponsored transaction.\n */\n async createVaultSponsored?(\n userKeyHash: string,\n _sponsorPrivateKey: string,\n _rpcUrl?: string\n ): Promise<VaultCreationResult> {\n throw new Error(\n 'Sponsored vault creation on Stacks requires the user to sign with their Passkey. ' +\n 'Use createVaultViaRelayer() which handles the sponsored transaction flow. ' +\n `KeyHash=${userKeyHash}`\n );\n }\n\n /**\n * Create a vault via the relayer (sponsored/gasless).\n * The relayer will sponsor the register-identity transaction.\n */\n async createVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<VaultCreationResult> {\n const response = await fetch(`${relayerUrl}/api/v1/stacks/vault`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n userKeyHash,\n chainId: this.config.wormholeChainId,\n }),\n });\n\n const result = await response.json() as Record<string, unknown>;\n\n if (!response.ok || !result.success) {\n throw new Error((result.error as string) || 'Failed to create vault via relayer');\n }\n\n return {\n address: (result.vaultAddress as string) || this.computeVaultAddress(userKeyHash),\n transactionHash: (result.transactionHash as string) || '',\n blockNumber: 0,\n gasUsed: 0n,\n alreadyExisted: (result.alreadyExists as boolean) || false,\n sponsoredBy: 'relayer',\n };\n }\n\n async estimateVaultCreationGas(_userKeyHash: string): Promise<bigint> {\n // Stacks fees are typically 0.001-0.01 STX for contract calls\n return 10000n; // 0.01 STX in microSTX\n }\n\n getFactoryAddress(): string | undefined {\n return undefined; // No factory pattern on Stacks\n }\n\n getImplementationAddress(): string | undefined {\n return undefined;\n }\n\n // ========================================================================\n // Stacks-Specific: Balance Queries\n // ========================================================================\n\n /**\n * Get native STX balance for an address.\n */\n async getNativeBalance(address: string): Promise<bigint> {\n try {\n const response = await fetch(\n `${this.rpcUrl}/v2/accounts/${address}?proof=0`\n );\n if (!response.ok) {\n return 0n;\n }\n const data = await response.json() as Record<string, string>;\n return BigInt(data.balance || '0');\n } catch {\n return 0n;\n }\n }\n\n /**\n * Get vault STX balance for an identity.\n * Queries the vault contract's `get-stx-balance` read-only function.\n */\n async getVaultStxBalance(keyHash: string): Promise<bigint> {\n if (!this.vaultContract) {\n return 0n;\n }\n\n try {\n const result = await this.callReadOnly(\n this.vaultContract.address,\n this.vaultContract.name,\n 'get-stx-balance',\n [`0x${keyHash.replace('0x', '')}`]\n );\n\n if (result && result.value !== undefined) {\n return BigInt(result.value);\n }\n return 0n;\n } catch {\n return 0n;\n }\n }\n\n /**\n * Get vault sBTC balance for an identity.\n */\n async getVaultSbtcBalance(keyHash: string): Promise<bigint> {\n if (!this.vaultContract) {\n return 0n;\n }\n\n try {\n const result = await this.callReadOnly(\n this.vaultContract.address,\n this.vaultContract.name,\n 'get-sbtc-balance',\n [`0x${keyHash.replace('0x', '')}`]\n );\n\n if (result && result.value !== undefined) {\n return BigInt(result.value);\n }\n return 0n;\n } catch {\n return 0n;\n }\n }\n\n // ========================================================================\n // Stacks-Specific: Identity & Session Queries\n // ========================================================================\n\n /**\n * Get identity info from the spoke contract.\n */\n async getIdentity(keyHash: string): Promise<{\n compressedPubkey: string;\n owner: string;\n nonce: bigint;\n createdAt: bigint;\n } | null> {\n if (!this.spokeContract) {\n return null;\n }\n\n try {\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'get-identity',\n [`0x${keyHash.replace('0x', '')}`]\n );\n\n if (!result || result.value === undefined) {\n return null;\n }\n\n const val = result.value;\n return {\n compressedPubkey: val['compressed-pubkey']?.value || '',\n owner: val.owner?.value || '',\n nonce: BigInt(val.nonce?.value || 0),\n createdAt: BigInt(val['created-at']?.value || 0),\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get session info from the spoke contract.\n */\n async getSession(keyHash: string, sessionHash: string): Promise<{\n sessionPubkey: string;\n expiry: bigint;\n maxValue: bigint;\n spent: bigint;\n revoked: boolean;\n createdAt: bigint;\n } | null> {\n if (!this.spokeContract) {\n return null;\n }\n\n try {\n const cleanKeyHash = `0x${keyHash.replace('0x', '')}`;\n const cleanSessionHash = `0x${sessionHash.replace('0x', '')}`;\n\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'get-session',\n [cleanKeyHash, cleanSessionHash]\n );\n\n if (!result || result.value === undefined) {\n return null;\n }\n\n const val = result.value;\n return {\n sessionPubkey: val['session-pubkey']?.value || '',\n expiry: BigInt(val.expiry?.value || 0),\n maxValue: BigInt(val['max-value']?.value || 0),\n spent: BigInt(val.spent?.value || 0),\n revoked: val.revoked?.value === true,\n createdAt: BigInt(val['created-at']?.value || 0),\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Check if a session is currently active.\n */\n async checkSessionActive(keyHash: string, sessionHash: string): Promise<boolean> {\n if (!this.spokeContract) {\n return false;\n }\n\n try {\n const cleanKeyHash = `0x${keyHash.replace('0x', '')}`;\n const cleanSessionHash = `0x${sessionHash.replace('0x', '')}`;\n\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'is-session-active',\n [cleanKeyHash, cleanSessionHash]\n );\n\n return result?.value === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get remaining spending budget for a session.\n */\n async getRemainingBudget(keyHash: string, sessionHash: string): Promise<bigint> {\n if (!this.spokeContract) {\n return 0n;\n }\n\n try {\n const cleanKeyHash = `0x${keyHash.replace('0x', '')}`;\n const cleanSessionHash = `0x${sessionHash.replace('0x', '')}`;\n\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'get-remaining-budget',\n [cleanKeyHash, cleanSessionHash]\n );\n\n if (result && result.value !== undefined) {\n return BigInt(result.value);\n }\n return 0n;\n } catch {\n return 0n;\n }\n }\n\n // ========================================================================\n // Stacks-Specific: Protocol Status\n // ========================================================================\n\n /**\n * Check if the spoke contract is paused.\n */\n async isProtocolPaused(): Promise<boolean> {\n if (!this.spokeContract) {\n return false;\n }\n\n try {\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'is-paused',\n []\n );\n\n return result === true || result?.value === true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get global identity count.\n */\n async getIdentityCount(): Promise<bigint> {\n if (!this.spokeContract) {\n return 0n;\n }\n\n try {\n const result = await this.callReadOnly(\n this.spokeContract.address,\n this.spokeContract.name,\n 'get-identity-count',\n []\n );\n\n return BigInt(result?.value || result || 0);\n } catch {\n return 0n;\n }\n }\n\n /**\n * Get total STX deposited across all vaults.\n */\n async getTotalStxDeposited(): Promise<bigint> {\n if (!this.vaultContract) {\n return 0n;\n }\n\n try {\n const result = await this.callReadOnly(\n this.vaultContract.address,\n this.vaultContract.name,\n 'get-total-stx-deposited',\n []\n );\n\n return BigInt(result?.value || result || 0);\n } catch {\n return 0n;\n }\n }\n\n // ========================================================================\n // Session Management (Issue #13)\n // ========================================================================\n\n /**\n * Register a session key on the Stacks spoke.\n * On Stacks, sessions are managed directly on the spoke contract\n * (unlike EVM spokes where sessions are on the Hub).\n */\n async registerSession(_params: RegisterSessionParams): Promise<void> {\n throw new Error(\n 'Session registration on Stacks requires a Passkey signature. ' +\n 'Build a register-session transaction with the Passkey signature, ' +\n 'then submit via the relayer for sponsored execution.'\n );\n }\n\n /**\n * Revoke a session key on the Stacks spoke.\n */\n async revokeSession(_params: RevokeSessionParams): Promise<void> {\n throw new Error(\n 'Session revocation on Stacks requires a Passkey signature. ' +\n 'Build a revoke-session transaction with the Passkey signature, ' +\n 'then submit via the relayer for sponsored execution.'\n );\n }\n\n /**\n * Check if a session is active.\n */\n async isSessionActive(\n userKeyHash: string,\n sessionKeyHash: string\n ): Promise<SessionValidationResult> {\n const active = await this.checkSessionActive(userKeyHash, sessionKeyHash);\n const session = await this.getSession(userKeyHash, sessionKeyHash);\n\n return {\n isActive: active,\n expiry: session ? Number(session.expiry) : 0,\n maxValue: session?.maxValue ?? 0n,\n chainScopes: [this.config.wormholeChainId],\n };\n }\n\n /**\n * Get all sessions for a user.\n * Note: Clarity maps don't support enumeration, so this requires\n * off-chain indexing or event log parsing.\n */\n async getUserSessions(_userKeyHash: string): Promise<SessionKey[]> {\n throw new Error(\n 'Enumerating all sessions is not supported on Stacks (Clarity maps are not iterable). ' +\n 'Use checkSessionActive() with a known session hash, or query the Stacks event log ' +\n 'for \"session-registered\" print events via the Hiro API.'\n );\n }\n\n // ========================================================================\n // Stacks-Specific: Transaction Status\n // ========================================================================\n\n /**\n * Get the status of a Stacks transaction.\n */\n async getTransactionStatus(txId: string): Promise<{\n status: 'pending' | 'success' | 'failed' | 'not_found';\n blockHeight?: number;\n error?: string;\n }> {\n try {\n const cleanTxId = txId.startsWith('0x') ? txId : `0x${txId}`;\n const response = await fetch(\n `${this.rpcUrl}/extended/v1/tx/${cleanTxId}`\n );\n\n if (!response.ok) {\n return { status: 'not_found' };\n }\n\n const data = await response.json() as Record<string, unknown>;\n const txStatus = data.tx_status as string;\n\n if (txStatus === 'success') {\n return {\n status: 'success',\n blockHeight: data.block_height as number,\n };\n }\n\n if (txStatus === 'pending') {\n return { status: 'pending' };\n }\n\n if (txStatus === 'abort_by_response' || txStatus === 'abort_by_post_condition') {\n return {\n status: 'failed',\n error: `Transaction aborted: ${txStatus}`,\n };\n }\n\n return { status: 'pending' };\n } catch {\n return { status: 'not_found' };\n }\n }\n\n /**\n * Wait for a transaction to be confirmed.\n *\n * @param txId - Transaction ID\n * @param maxAttempts - Maximum polling attempts (default: 60)\n * @param pollIntervalMs - Polling interval in milliseconds (default: 5000)\n */\n async waitForConfirmation(\n txId: string,\n maxAttempts: number = 60,\n pollIntervalMs: number = 5000\n ): Promise<{ confirmed: boolean; blockHeight?: number }> {\n for (let i = 0; i < maxAttempts; i++) {\n const status = await this.getTransactionStatus(txId);\n\n if (status.status === 'success') {\n return { confirmed: true, blockHeight: status.blockHeight };\n }\n\n if (status.status === 'failed') {\n throw new Error(`Transaction failed: ${status.error}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n }\n\n return { confirmed: false };\n }\n\n // ========================================================================\n // Stacks-Specific: Network Info\n // ========================================================================\n\n /**\n * Get Stacks network info (block height, network version, etc.).\n */\n async getNetworkInfo(): Promise<{\n networkId: number;\n stacksBlockHeight: number;\n burnBlockHeight: number;\n serverVersion: string;\n }> {\n const response = await fetch(`${this.rpcUrl}/v2/info`);\n if (!response.ok) {\n throw new Error(`Failed to get Stacks network info: ${response.statusText}`);\n }\n\n const data = await response.json() as Record<string, unknown>;\n return {\n networkId: data.network_id as number,\n stacksBlockHeight: data.stacks_tip_height as number,\n burnBlockHeight: data.burn_block_height as number,\n serverVersion: data.server_version as string,\n };\n }\n\n /**\n * Get the current Stacks block height.\n * Used for session expiry calculations.\n */\n async getCurrentBlockHeight(): Promise<number> {\n const info = await this.getNetworkInfo();\n return info.stacksBlockHeight;\n }\n\n // ========================================================================\n // Internal: Read-Only Contract Calls via Hiro API\n // ========================================================================\n\n /**\n * Call a read-only Clarity function via the Hiro API.\n * Uses the /v2/contracts/call-read endpoint.\n */\n private async callReadOnly(\n contractAddress: string,\n contractName: string,\n functionName: string,\n args: string[]\n ): Promise<any> {\n const url = `${this.rpcUrl}/v2/contracts/call-read/${contractAddress}/${contractName}/${functionName}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sender: contractAddress,\n arguments: args,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new Error(\n `Read-only call failed: ${contractAddress}.${contractName}::${functionName} - ` +\n `${response.status}: ${errorText}`\n );\n }\n\n const data = await response.json() as Record<string, unknown>;\n\n if (!data.okay) {\n throw new Error(\n `Read-only call returned error: ${contractAddress}.${contractName}::${functionName} - ` +\n `${data.cause || 'Unknown cause'}`\n );\n }\n\n // Parse the Clarity value from the hex result\n return this.parseClarityValue(data.result as string);\n }\n\n /**\n * Parse a hex-encoded Clarity value from the API response.\n * This is a simplified parser for common Clarity types.\n */\n private parseClarityValue(hex: string): any {\n if (!hex || hex === '0x') {\n return null;\n }\n\n const bytes = hexToBytes(hex);\n if (bytes.length === 0) {\n return null;\n }\n\n const typeId = bytes[0];\n\n switch (typeId) {\n // int (0x00)\n case 0x00: {\n let value = 0n;\n for (let i = 1; i < 17 && i < bytes.length; i++) {\n value = (value << 8n) | BigInt(bytes[i]!);\n }\n return { value };\n }\n // uint (0x01)\n case 0x01: {\n let value = 0n;\n for (let i = 1; i < 17 && i < bytes.length; i++) {\n value = (value << 8n) | BigInt(bytes[i]!);\n }\n return { value };\n }\n // buffer (0x02)\n case 0x02: {\n const len = (bytes[1]! << 24) | (bytes[2]! << 16) | (bytes[3]! << 8) | bytes[4]!;\n const bufValue = bytes.slice(5, 5 + len);\n return { value: '0x' + bytesToHex(bufValue) };\n }\n // bool true (0x03)\n case 0x03:\n return true;\n // bool false (0x04)\n case 0x04:\n return false;\n // optional none (0x09)\n case 0x09:\n return null;\n // optional some (0x0a)\n case 0x0a:\n return this.parseClarityValue('0x' + bytesToHex(bytes.slice(1)));\n // response ok (0x07)\n case 0x07:\n return this.parseClarityValue('0x' + bytesToHex(bytes.slice(1)));\n // response err (0x08)\n case 0x08: {\n const errVal = this.parseClarityValue('0x' + bytesToHex(bytes.slice(1)));\n throw new Error(`Clarity error: ${JSON.stringify(errVal)}`);\n }\n // tuple (0x0c)\n case 0x0c: {\n // Simplified tuple parsing - return raw for now\n return { value: hex };\n }\n default:\n return { value: hex };\n }\n }\n}\n","/**\n * Veridex Protocol SDK - Stacks Post-Condition Builder\n *\n * Stacks Post-Conditions are a unique protocol-level safety feature.\n * They enforce spending constraints at the blockchain level, independent\n * of smart contract logic. This provides a third layer of protection:\n *\n * 1. SDK-level spending limits (off-chain, first check)\n * 2. Contract-level spending limits (on-chain, enforced in veridex-spoke sessions)\n * 3. Protocol-level Post-Conditions (Stacks-native, attached by SDK at tx broadcast)\n *\n * Post-conditions are attached to transactions before broadcast and are\n * validated by the Stacks node itself — they cannot be bypassed by contracts.\n */\n\nimport { isContractPrincipal, parseContractPrincipal } from './StacksAddressUtils.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Post-condition comparison type */\nexport type PostConditionComparison =\n | 'eq' // Exactly equal\n | 'gte' // Greater than or equal\n | 'lte' // Less than or equal\n | 'gt' // Greater than\n | 'lt'; // Less than\n\n/** STX post-condition */\nexport interface StxPostCondition {\n type: 'stx';\n principal: string;\n comparison: PostConditionComparison;\n amount: bigint;\n}\n\n/** Fungible token post-condition */\nexport interface FtPostCondition {\n type: 'ft';\n principal: string;\n comparison: PostConditionComparison;\n amount: bigint;\n contractAddress: string;\n contractName: string;\n tokenName: string;\n}\n\n/** Non-fungible token post-condition */\nexport interface NftPostCondition {\n type: 'nft';\n principal: string;\n contractAddress: string;\n contractName: string;\n tokenName: string;\n assetId: string;\n owns: boolean;\n}\n\nexport type PostCondition = StxPostCondition | FtPostCondition | NftPostCondition;\n\n// ============================================================================\n// Builder\n// ============================================================================\n\n/**\n * Build STX transfer post-conditions for vault withdrawals.\n * Ensures the contract sends exactly the specified amount.\n *\n * @param contractPrincipal - The vault contract principal (e.g., \"ST1PQHQKV...veridex-vault\")\n * @param amount - Exact amount in microSTX\n * @returns Array of post-conditions to attach to the transaction\n */\nexport function buildStxWithdrawalPostConditions(\n contractPrincipal: string,\n amount: bigint\n): StxPostCondition[] {\n return [\n {\n type: 'stx',\n principal: contractPrincipal,\n comparison: 'eq',\n amount,\n },\n ];\n}\n\n/**\n * Build STX deposit post-conditions.\n * Ensures the sender sends exactly the specified amount to the vault.\n *\n * @param senderPrincipal - The depositor's principal address\n * @param amount - Exact amount in microSTX\n * @returns Array of post-conditions\n */\nexport function buildStxDepositPostConditions(\n senderPrincipal: string,\n amount: bigint\n): StxPostCondition[] {\n return [\n {\n type: 'stx',\n principal: senderPrincipal,\n comparison: 'eq',\n amount,\n },\n ];\n}\n\n/**\n * Build sBTC transfer post-conditions for vault withdrawals.\n *\n * @param contractPrincipal - The vault contract principal\n * @param amount - Exact amount in satoshis\n * @param sbtcContractAddress - sBTC token contract deployer address\n * @param sbtcContractName - sBTC token contract name (default: 'sbtc-token')\n * @returns Array of post-conditions\n */\nexport function buildSbtcWithdrawalPostConditions(\n contractPrincipal: string,\n amount: bigint,\n sbtcContractAddress: string = 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4',\n sbtcContractName: string = 'sbtc-token'\n): FtPostCondition[] {\n return [\n {\n type: 'ft',\n principal: contractPrincipal,\n comparison: 'eq',\n amount,\n contractAddress: sbtcContractAddress,\n contractName: sbtcContractName,\n tokenName: 'sbtc-token',\n },\n ];\n}\n\n/**\n * Build post-conditions for a session-authorized execute action.\n * Combines STX or sBTC post-conditions based on action type.\n *\n * @param actionType - 1 = STX transfer, 2 = sBTC transfer\n * @param contractPrincipal - The spoke/vault contract principal\n * @param amount - Amount in base units\n * @returns Array of post-conditions\n */\nexport function buildExecutePostConditions(\n actionType: number,\n contractPrincipal: string,\n amount: bigint\n): PostCondition[] {\n switch (actionType) {\n case 1: // ACTION-TRANSFER-STX\n return buildStxWithdrawalPostConditions(contractPrincipal, amount);\n case 2: // ACTION-TRANSFER-SBTC\n return buildSbtcWithdrawalPostConditions(contractPrincipal, amount);\n default:\n return [];\n }\n}\n\n/**\n * Validate that a set of post-conditions is present and reasonable.\n * Used to reject transactions that lack post-conditions for asset transfers.\n *\n * @param postConditions - Array of post-conditions to validate\n * @param expectedAmount - Expected transfer amount\n * @returns Validation result with error message if invalid\n */\nexport function validatePostConditions(\n postConditions: PostCondition[],\n expectedAmount: bigint\n): { valid: boolean; error?: string } {\n if (postConditions.length === 0) {\n return {\n valid: false,\n error: 'No post-conditions attached. Asset transfers require post-conditions for safety.',\n };\n }\n\n // Check that at least one post-condition matches the expected amount\n const hasMatchingAmount = postConditions.some((pc) => {\n if (pc.type === 'stx' || pc.type === 'ft') {\n return pc.amount === expectedAmount && pc.comparison === 'eq';\n }\n return false;\n });\n\n if (!hasMatchingAmount) {\n return {\n valid: false,\n error: `No post-condition matches expected amount ${expectedAmount}. Ensure exact-match post-conditions are attached.`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Get the contract address and name from a principal for post-condition construction.\n * Handles both standard and contract principals.\n *\n * @param principal - Stacks principal (standard or contract)\n * @returns Object with address and optional contractName\n */\nexport function principalForPostCondition(principal: string): {\n address: string;\n contractName?: string;\n} {\n if (isContractPrincipal(principal)) {\n const parsed = parseContractPrincipal(principal);\n return { address: parsed.address, contractName: parsed.contractName };\n }\n return { address: principal };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/PasskeyManager.ts","../src/core/relayerUrl.ts"],"sourcesContent":["/**\n * Veridex Protocol SDK - Passkey Manager\n * \n * Chain-agnostic WebAuthn/Passkey credential management\n */\n\nimport {\n startRegistration,\n startAuthentication,\n browserSupportsWebAuthn,\n} from '@simplewebauthn/browser';\nimport type {\n PublicKeyCredentialCreationOptionsJSON,\n PublicKeyCredentialRequestOptionsJSON,\n RegistrationResponseJSON,\n AuthenticationResponseJSON,\n} from '@simplewebauthn/types';\nimport { ethers } from 'ethers';\nimport { base64URLEncode, base64URLDecode, parseDERSignature, computeKeyHash } from '../utils.js';\nimport { buildRelayerApiUrl, normalizeRelayerOrigin } from './relayerUrl.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PasskeyCredential {\n credentialId: string;\n publicKeyX: bigint;\n publicKeyY: bigint;\n keyHash: string;\n}\n\nexport interface WebAuthnSignature {\n authenticatorData: string;\n clientDataJSON: string;\n challengeIndex: number;\n typeIndex: number;\n r: bigint;\n s: bigint;\n}\n\nexport interface PasskeyManagerConfig {\n rpName?: string;\n rpId?: string;\n timeout?: number;\n userVerification?: 'required' | 'preferred' | 'discouraged';\n authenticatorAttachment?: 'platform' | 'cross-platform';\n /** Relayer API URL for cross-device credential recovery */\n relayerUrl?: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * The canonical Veridex RP ID for cross-domain passkey sharing.\n * All Veridex SDK instances should use this RP ID to enable passkey\n * portability across different applications and domains.\n * \n * This works via W3C Related Origin Requests (ROR) - veridex.network\n * hosts a .well-known/webauthn file that lists allowed origins.\n */\nexport const VERIDEX_RP_ID = 'veridex.network';\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Detects the appropriate RP ID for passkey sharing.\n * \n * For production: Returns VERIDEX_RP_ID ('veridex.network') to enable\n * cross-domain passkey sharing via Related Origin Requests (ROR).\n * \n * For local development:\n * - localhost/127.0.0.1 → returns as-is\n * - IP addresses → returns as-is\n * \n * @param forceLocal - If true, uses local domain detection instead of canonical RP ID\n */\nfunction detectRpId(forceLocal?: boolean): string {\n if (typeof window === 'undefined') return 'localhost';\n\n const hostname = window.location.hostname;\n\n // For local development, always use the actual hostname\n if (hostname === 'localhost' || hostname === '127.0.0.1' || /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(hostname)) {\n return hostname;\n }\n\n // If forceLocal is true, detect from hostname (legacy behavior)\n if (forceLocal) {\n const parts = hostname.split('.');\n if (parts.length <= 2) {\n return hostname;\n }\n return parts.slice(-2).join('.');\n }\n\n // Default: Use canonical Veridex RP ID for cross-domain passkey sharing\n return VERIDEX_RP_ID;\n}\n\n/**\n * Check if the browser supports Related Origin Requests (ROR).\n * This is a WebAuthn Level 3 feature that allows using passkeys\n * across different domains listed in the RP's .well-known/webauthn file.\n * \n * @returns true if ROR is supported, false otherwise\n */\nexport async function supportsRelatedOrigins(): Promise<boolean> {\n if (typeof window === 'undefined' || !window.PublicKeyCredential) {\n return false;\n }\n\n // Check for getClientCapabilities (WebAuthn L3)\n if ('getClientCapabilities' in PublicKeyCredential) {\n try {\n const getCapabilities = (PublicKeyCredential as unknown as {\n getClientCapabilities: () => Promise<{ relatedOrigins?: boolean }>\n }).getClientCapabilities;\n const capabilities = await getCapabilities();\n return capabilities?.relatedOrigins === true;\n } catch {\n return false;\n }\n }\n\n return false;\n}\n\n/** \n * Export the detectRpId function for external use.\n * Apps can call this to see what RP ID will be used.\n */\nexport { detectRpId };\n\n// ============================================================================\n// PasskeyManager Class\n// ============================================================================\n\n/**\n * Manages WebAuthn passkey credentials for Veridex Protocol\n */\nexport class PasskeyManager {\n private config: Required<PasskeyManagerConfig>;\n private credential: PasskeyCredential | null = null;\n\n constructor(config: PasskeyManagerConfig = {}) {\n this.config = {\n rpName: config.rpName ?? 'Veridex Protocol',\n rpId: config.rpId ?? detectRpId(),\n timeout: config.timeout ?? 60000,\n userVerification: config.userVerification ?? 'required',\n authenticatorAttachment: config.authenticatorAttachment ?? 'platform',\n relayerUrl: normalizeRelayerOrigin(config.relayerUrl ?? ''),\n };\n }\n\n static isSupported(): boolean {\n return browserSupportsWebAuthn();\n }\n\n static async isPlatformAuthenticatorAvailable(): Promise<boolean> {\n if (typeof window === 'undefined' || !window.PublicKeyCredential) {\n return false;\n }\n return await window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();\n }\n\n async register(username: string, displayName: string): Promise<PasskeyCredential> {\n if (!PasskeyManager.isSupported()) {\n throw new Error('WebAuthn is not supported in this browser');\n }\n\n const challenge = ethers.randomBytes(32);\n const challengeBase64 = base64URLEncode(challenge);\n\n const options: PublicKeyCredentialCreationOptionsJSON = {\n challenge: challengeBase64,\n rp: {\n name: this.config.rpName,\n id: this.config.rpId,\n },\n user: {\n id: base64URLEncode(ethers.toUtf8Bytes(username)),\n name: username,\n displayName: displayName,\n },\n pubKeyCredParams: [\n { alg: -7, type: 'public-key' }, // ES256 (P-256) - WebAuthn default\n { alg: -257, type: 'public-key' }, // RS256 - Widely supported\n { alg: -8, type: 'public-key' }, // EdDSA - Modern, efficient\n { alg: -35, type: 'public-key' }, // ES384 (P-384)\n { alg: -36, type: 'public-key' }, // ES512 (P-521)\n { alg: -37, type: 'public-key' }, // PS256 - RSA PSS\n ],\n authenticatorSelection: {\n authenticatorAttachment: this.config.authenticatorAttachment,\n userVerification: this.config.userVerification,\n residentKey: 'required',\n requireResidentKey: true,\n },\n timeout: this.config.timeout,\n attestation: 'none',\n };\n\n const response = await startRegistration(options);\n const publicKey = this.extractPublicKeyFromAttestation(response);\n const keyHash = computeKeyHash(publicKey.x, publicKey.y);\n\n this.credential = {\n credentialId: response.id,\n publicKeyX: publicKey.x,\n publicKeyY: publicKey.y,\n keyHash,\n };\n\n return this.credential;\n }\n\n async sign(challenge: Uint8Array): Promise<WebAuthnSignature> {\n if (!this.credential) {\n throw new Error('No credential set. Call register() or setCredential() first.');\n }\n\n const challengeBase64 = base64URLEncode(challenge);\n\n const options: PublicKeyCredentialRequestOptionsJSON = {\n challenge: challengeBase64,\n rpId: this.config.rpId,\n allowCredentials: [\n {\n id: this.credential.credentialId,\n type: 'public-key',\n transports: ['internal'],\n },\n ],\n userVerification: this.config.userVerification,\n timeout: this.config.timeout,\n };\n\n const response = await startAuthentication(options);\n return this.parseAuthenticationResponse(response);\n }\n\n\n\n /**\n * Authenticate using a discoverable credential (passkey)\n * This allows sign-in without knowing the credential ID ahead of time.\n * The authenticator will show all available passkeys for this RP.\n * \n * @param challenge - Optional challenge bytes. If not provided, a random challenge is used.\n * @returns The credential that was used to authenticate, along with the signature\n */\n async authenticate(challenge?: Uint8Array): Promise<{\n credential: PasskeyCredential;\n signature: WebAuthnSignature;\n }> {\n if (!PasskeyManager.isSupported()) {\n throw new Error('WebAuthn is not supported in this browser');\n }\n\n const actualChallenge = challenge ?? ethers.randomBytes(32);\n const challengeBase64 = base64URLEncode(actualChallenge);\n\n // Allow any registered credential (discoverable or not)\n const options: PublicKeyCredentialRequestOptionsJSON = {\n challenge: challengeBase64,\n rpId: this.config.rpId,\n userVerification: this.config.userVerification,\n timeout: this.config.timeout,\n };\n\n const response = await startAuthentication(options);\n const credentialId = response.id;\n const signature = this.parseAuthenticationResponse(response);\n\n // 1. Try to find the credential in our local cache (could be one of many)\n let storedCredential = this.findCredentialById(credentialId);\n\n if (storedCredential) {\n this.credential = storedCredential;\n return { credential: storedCredential, signature };\n }\n\n // 2. Try to fetch from the Relayer/API if configured\n if (this.config.relayerUrl) {\n storedCredential = await this.loadCredentialFromRelayer(credentialId);\n if (storedCredential) {\n this.credential = storedCredential;\n this.addCredentialToStorage(storedCredential);\n return { credential: storedCredential, signature };\n }\n }\n\n // 3. If we still don't have it, we can't verify signatures or derive the keyHash\n const hasRelayer = !!this.config.relayerUrl;\n throw new Error(\n 'Credential not found. ' +\n 'This passkey was registered on a different device or the data was cleared. ' +\n (hasRelayer\n ? 'The credential was not found. Please register a new passkey.'\n : 'Please register a new passkey or ensure the relayer URL is configured.')\n );\n }\n\n /**\n * Find a credential by ID in the list of stored credentials\n */\n private findCredentialById(credentialId: string): PasskeyCredential | null {\n if (typeof window === 'undefined') return null;\n\n const stored = this.getAllStoredCredentials();\n return stored.find(c => c.credentialId === credentialId) || null;\n }\n\n /**\n * Get all credentials stored in localStorage\n */\n getAllStoredCredentials(key = 'veridex_credentials'): PasskeyCredential[] {\n if (typeof window === 'undefined') return [];\n\n const stored = localStorage.getItem(key);\n if (!stored) {\n // Fallback to legacy single key for backward compatibility\n const legacy = localStorage.getItem('veridex_credential');\n if (legacy) {\n try {\n const data = JSON.parse(legacy);\n // Migrate to new format\n const cred = this.parseStoredCredential(data);\n if (cred) {\n this.saveCredentials([cred], key);\n localStorage.removeItem('veridex_credential');\n return [cred];\n }\n } catch (e) { /* ignore */ }\n }\n return [];\n }\n\n try {\n const data = JSON.parse(stored);\n if (Array.isArray(data)) {\n return data.map(item => this.parseStoredCredential(item)).filter((c): c is PasskeyCredential => c !== null);\n }\n return [];\n } catch (error) {\n console.error('Failed to load credentials:', error);\n return [];\n }\n }\n\n private parseStoredCredential(data: any): PasskeyCredential | null {\n try {\n return {\n credentialId: data.credentialId,\n publicKeyX: BigInt(data.publicKeyX),\n publicKeyY: BigInt(data.publicKeyY),\n keyHash: data.keyHash,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Save a list of credentials to localStorage\n */\n saveCredentials(credentials: PasskeyCredential[], key = 'veridex_credentials'): void {\n if (typeof window === 'undefined') return;\n\n const data = credentials.map(c => ({\n credentialId: c.credentialId,\n publicKeyX: c.publicKeyX.toString(),\n publicKeyY: c.publicKeyY.toString(),\n keyHash: c.keyHash,\n }));\n\n localStorage.setItem(key, JSON.stringify(data));\n }\n\n /**\n * Add a single credential to storage (append or update)\n */\n addCredentialToStorage(credential: PasskeyCredential, key = 'veridex_credentials'): void {\n const stored = this.getAllStoredCredentials(key);\n const existingIndex = stored.findIndex(c => c.credentialId === credential.credentialId);\n\n if (existingIndex >= 0) {\n stored[existingIndex] = credential;\n } else {\n stored.push(credential);\n }\n\n this.saveCredentials(stored, key);\n }\n\n /**\n * Check if there's ANY stored credential for this RP\n */\n hasStoredCredential(): boolean {\n return this.getAllStoredCredentials().length > 0;\n }\n\n getCredential(): PasskeyCredential | null {\n return this.credential;\n }\n\n setCredential(credential: PasskeyCredential): void {\n this.credential = credential;\n }\n\n createCredentialFromPublicKey(\n credentialId: string,\n publicKeyX: bigint,\n publicKeyY: bigint\n ): PasskeyCredential {\n const keyHash = computeKeyHash(publicKeyX, publicKeyY);\n this.credential = {\n credentialId,\n publicKeyX,\n publicKeyY,\n keyHash,\n };\n return this.credential;\n }\n\n clearCredential(): void {\n this.credential = null;\n }\n\n /**\n * Save the current credential to localStorage (appends to list)\n */\n saveToLocalStorage(key = 'veridex_credentials'): void {\n if (!this.credential) {\n throw new Error('No credential to save');\n }\n this.addCredentialToStorage(this.credential, key);\n }\n\n loadFromLocalStorage(key = 'veridex_credentials'): PasskeyCredential | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n // Return the most recently used credential, or the last one added\n const stored = this.getAllStoredCredentials(key);\n if (stored.length > 0) {\n // Use the last one as default\n this.credential = stored[stored.length - 1];\n return this.credential;\n }\n\n return null;\n }\n\n removeFromLocalStorage(key = 'veridex_credentials'): void {\n if (typeof window !== 'undefined') {\n localStorage.removeItem(key);\n // Also remove legacy key\n localStorage.removeItem('veridex_credential');\n }\n }\n\n // =========================================================================\n // Relayer-based Credential Storage (Cross-Device Recovery)\n // =========================================================================\n\n /**\n * Save the current credential to the relayer for cross-device recovery.\n * This should be called after registration.\n */\n async saveCredentialToRelayer(): Promise<boolean> {\n if (!this.credential) {\n throw new Error('No credential to save');\n }\n if (!this.config.relayerUrl) {\n console.warn('Relayer URL not configured; skipping remote credential storage');\n return false;\n }\n\n try {\n const response = await fetch(buildRelayerApiUrl(this.config.relayerUrl, '/credential'), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n keyHash: this.credential.keyHash,\n credentialId: this.credential.credentialId,\n publicKeyX: this.credential.publicKeyX.toString(),\n publicKeyY: this.credential.publicKeyY.toString(),\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n console.error('Failed to save credential to relayer:', errorData);\n return false;\n }\n\n console.log('Credential saved to relayer for cross-device recovery');\n return true;\n } catch (error) {\n console.error('Failed to save credential to relayer:', error);\n return false;\n }\n }\n\n /**\n * Load a credential from the relayer by credential ID.\n * Used during discoverable credential authentication when localStorage is empty.\n */\n async loadCredentialFromRelayer(credentialId: string): Promise<PasskeyCredential | null> {\n if (!this.config.relayerUrl) {\n return null;\n }\n\n try {\n const response = await fetch(\n buildRelayerApiUrl(this.config.relayerUrl, `/credential/by-id/${encodeURIComponent(credentialId)}`)\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n if (!data.exists) {\n return null;\n }\n\n // Validate required fields before attempting BigInt conversion\n if (!data.credentialId || !data.publicKeyX || !data.publicKeyY || !data.keyHash) {\n console.warn('Relayer returned incomplete credential data:', {\n hasCredentialId: !!data.credentialId,\n hasPublicKeyX: !!data.publicKeyX,\n hasPublicKeyY: !!data.publicKeyY,\n hasKeyHash: !!data.keyHash,\n });\n return null;\n }\n\n return {\n credentialId: data.credentialId,\n publicKeyX: BigInt(data.publicKeyX),\n publicKeyY: BigInt(data.publicKeyY),\n keyHash: data.keyHash,\n };\n } catch (error) {\n console.error('Failed to load credential from relayer:', error);\n return null;\n }\n }\n\n /**\n * Load a credential from the relayer by keyHash.\n * Useful when you know the user's keyHash but not their credential ID.\n */\n async loadCredentialFromRelayerByKeyHash(keyHash: string): Promise<PasskeyCredential | null> {\n if (!this.config.relayerUrl) {\n return null;\n }\n\n try {\n const response = await fetch(\n buildRelayerApiUrl(this.config.relayerUrl, `/credential/${encodeURIComponent(keyHash)}`)\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n if (!data.exists) {\n return null;\n }\n\n // Validate required fields before attempting BigInt conversion\n if (!data.credentialId || !data.publicKeyX || !data.publicKeyY || !data.keyHash) {\n console.warn('Relayer returned incomplete credential data for keyHash:', {\n hasCredentialId: !!data.credentialId,\n hasPublicKeyX: !!data.publicKeyX,\n hasPublicKeyY: !!data.publicKeyY,\n hasKeyHash: !!data.keyHash,\n });\n return null;\n }\n\n return {\n credentialId: data.credentialId,\n publicKeyX: BigInt(data.publicKeyX),\n publicKeyY: BigInt(data.publicKeyY),\n keyHash: data.keyHash,\n };\n } catch (error) {\n console.error('Failed to load credential from relayer:', error);\n return null;\n }\n }\n\n // =========================================================================\n // Backup Passkey & Device Migration (Phase 3)\n // =========================================================================\n\n /**\n * Register a backup passkey for the current identity.\n *\n * This creates a new WebAuthn credential on this device/platform that becomes\n * an additional authorized key for the same Veridex identity. The caller\n * must submit the returned credential to VeridexHub.addKey() for on-chain registration.\n *\n * Use cases:\n * - \"Add this device\" flow when signing in on a new machine\n * - Proactive backup creation on a separate authenticator\n * - Cross-ecosystem redundancy (iCloud + Google Password Manager)\n *\n * @param username - Username for the new credential (typically same as primary)\n * @param displayName - Display name for the backup (e.g., \"MacBook Pro Backup\")\n * @param excludeCredentialIds - Credential IDs to exclude (prevents re-registering same authenticator)\n * @returns The newly registered backup credential\n */\n async registerBackupPasskey(\n username: string,\n displayName: string,\n excludeCredentialIds?: string[]\n ): Promise<PasskeyCredential> {\n if (!PasskeyManager.isSupported()) {\n throw new Error('WebAuthn is not supported in this browser');\n }\n\n const challenge = ethers.randomBytes(32);\n const challengeBase64 = base64URLEncode(challenge);\n\n // Build exclude list: prevent re-registering on the same authenticator\n const excludeList = excludeCredentialIds ?? this.getAllStoredCredentials().map(c => c.credentialId);\n\n const options: PublicKeyCredentialCreationOptionsJSON = {\n challenge: challengeBase64,\n rp: {\n name: this.config.rpName,\n id: this.config.rpId,\n },\n user: {\n id: base64URLEncode(ethers.toUtf8Bytes(username)),\n name: username,\n displayName: displayName,\n },\n pubKeyCredParams: [\n { alg: -7, type: 'public-key' }, // ES256 (P-256)\n { alg: -257, type: 'public-key' }, // RS256\n { alg: -8, type: 'public-key' }, // EdDSA\n ],\n excludeCredentials: excludeList.map(id => ({\n id,\n type: 'public-key' as const,\n transports: ['internal' as const, 'hybrid' as const],\n })),\n authenticatorSelection: {\n // Allow cross-platform for backup on security keys\n userVerification: this.config.userVerification,\n residentKey: 'required',\n requireResidentKey: true,\n },\n timeout: this.config.timeout,\n attestation: 'none',\n };\n\n const response = await startRegistration(options);\n const publicKey = this.extractPublicKeyFromAttestation(response);\n const keyHash = computeKeyHash(publicKey.x, publicKey.y);\n\n const backupCredential: PasskeyCredential = {\n credentialId: response.id,\n publicKeyX: publicKey.x,\n publicKeyY: publicKey.y,\n keyHash,\n };\n\n // Store locally alongside existing credentials\n this.addCredentialToStorage(backupCredential);\n\n return backupCredential;\n }\n\n /**\n * Get registration info for backup state from a registration response.\n *\n * This extracts the backup eligibility (BE) and backup state (BS) flags\n * from the authenticator data, which indicate whether the credential\n * is eligible for cloud sync and whether it is currently synced.\n *\n * @param authenticatorData - Hex-encoded authenticator data from registration\n * @returns Backup flags, or null if not determinable\n */\n static parseBackupFlags(authenticatorData: string): {\n backupEligible: boolean;\n backupState: boolean;\n } | null {\n try {\n const data = ethers.getBytes(authenticatorData);\n if (data.length < 37) return null;\n\n // Flags byte is at offset 32 (after the 32-byte rpIdHash)\n const flags = data[32];\n\n // Bit 3 (0x08) = Backup Eligible (BE)\n // Bit 4 (0x10) = Backup State (BS)\n return {\n backupEligible: (flags & 0x08) !== 0,\n backupState: (flags & 0x10) !== 0,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Get the number of credentials stored locally.\n */\n getStoredCredentialCount(): number {\n return this.getAllStoredCredentials().length;\n }\n\n /**\n * Get all credential IDs stored locally (for exclude lists).\n */\n getStoredCredentialIds(): string[] {\n return this.getAllStoredCredentials().map(c => c.credentialId);\n }\n\n private extractPublicKeyFromAttestation(\n response: RegistrationResponseJSON\n ): { x: bigint; y: bigint } {\n const attestationObject = base64URLDecode(response.response.attestationObject);\n\n // Parse CBOR attestation object\n // The attestation object is a CBOR map with keys: fmt, authData, attStmt\n // We need to extract the authData which contains the credential public key\n\n let offset = 0;\n\n // Skip the CBOR map header (usually 0xa3 for 3-item map or 0xa2 for 2-item map)\n if (attestationObject[offset] >= 0xa0 && attestationObject[offset] <= 0xbf) {\n offset++;\n }\n\n // Find the authData field in the CBOR map\n // Look for the text string \"authData\" (0x68 followed by \"authData\")\n while (offset < attestationObject.length - 37) {\n if (attestationObject[offset] === 0x68 && // text string, 8 bytes\n attestationObject[offset + 1] === 0x61 && // 'a'\n attestationObject[offset + 2] === 0x75 && // 'u'\n attestationObject[offset + 3] === 0x74 && // 't'\n attestationObject[offset + 4] === 0x68 && // 'h'\n attestationObject[offset + 5] === 0x44 && // 'D'\n attestationObject[offset + 6] === 0x61 && // 'a'\n attestationObject[offset + 7] === 0x74 && // 't'\n attestationObject[offset + 8] === 0x61) { // 'a'\n offset += 9;\n break;\n }\n offset++;\n }\n\n // Skip the byte string header for authData\n if (attestationObject[offset] === 0x58 || attestationObject[offset] === 0x59) {\n // 0x58 = 1-byte length, 0x59 = 2-byte length\n const lengthBytes = attestationObject[offset] === 0x58 ? 1 : 2;\n offset += 1 + lengthBytes;\n }\n\n // Now we're at the start of authData\n // authData structure:\n // - rpIdHash: 32 bytes\n // - flags: 1 byte\n // - signCount: 4 bytes\n // - attestedCredentialData (if AT flag is set):\n // - aaguid: 16 bytes\n // - credentialIdLength: 2 bytes\n // - credentialId: credentialIdLength bytes\n // - credentialPublicKey: CBOR-encoded COSE_Key\n\n offset += 32; // Skip rpIdHash\n offset += 1; // Skip flags\n offset += 4; // Skip signCount\n offset += 16; // Skip aaguid\n\n // Read credential ID length\n const credIdLen = (attestationObject[offset] << 8) | attestationObject[offset + 1];\n offset += 2;\n offset += credIdLen; // Skip credential ID\n\n // Now we're at the COSE public key\n const coseKey = attestationObject.slice(offset);\n\n console.log('COSE key length:', coseKey.length);\n console.log('COSE key hex:', this.bytesToHex(coseKey.slice(0, Math.min(100, coseKey.length))));\n\n const { x, y } = this.parseCOSEKey(coseKey);\n return { x, y };\n }\n\n private parseCOSEKey(coseKey: Uint8Array): { x: bigint; y: bigint } {\n console.log('COSE key length:', coseKey.length);\n console.log('COSE key hex:', this.bytesToHex(coseKey));\n\n // Try multiple parsing strategies\n const parsed = this.tryParseCOSEKeyStrategies(coseKey);\n if (parsed) {\n return parsed;\n }\n\n // If all strategies fail, try using a CBOR parser approach\n return this.parseCOSEKeyWithCBORStructure(coseKey);\n }\n\n private tryParseCOSEKeyStrategies(coseKey: Uint8Array): { x: bigint; y: bigint } | null {\n // Strategy 1: Look for the specific pattern of EC2 keys\n const keyBytes = new Uint8Array(coseKey);\n\n // Common pattern for EC2 keys with P-256 curve\n for (let i = 0; i < keyBytes.length - 40; i++) {\n // Check for potential x coordinate (32 bytes preceded by key marker)\n if (keyBytes[i] === 0x58 && keyBytes[i + 1] === 0x20) {\n const potentialX = keyBytes.slice(i + 2, i + 34);\n\n // Look for y coordinate after x\n for (let j = i + 34; j < keyBytes.length - 34; j++) {\n if (keyBytes[j] === 0x58 && keyBytes[j + 1] === 0x20) {\n const potentialY = keyBytes.slice(j + 2, j + 34);\n\n // Verify these look like valid coordinates\n if (this.isValidCoordinate(potentialX) && this.isValidCoordinate(potentialY)) {\n console.log('Found coordinates via pattern matching');\n return {\n x: this.bytesToBigInt(potentialX),\n y: this.bytesToBigInt(potentialY)\n };\n }\n }\n }\n }\n }\n\n // Strategy 2: Look for ASN.1 structure\n return this.tryParseASN1Structure(keyBytes);\n }\n\n private parseCOSEKeyWithCBORStructure(coseKey: Uint8Array): { x: bigint; y: bigint } {\n // More flexible parsing that handles different CBOR structures\n const bytes = new Uint8Array(coseKey);\n let xBytes: Uint8Array | null = null;\n let yBytes: Uint8Array | null = null;\n\n // Look for x and y coordinates by scanning for byte strings\n let i = 0;\n while (i < bytes.length) {\n // Check for byte string markers\n if (bytes[i] === 0x58) { // Byte string with length byte\n const length = bytes[i + 1];\n if (length === 0x20) { // 32 bytes - likely a coordinate\n const start = i + 2;\n const end = start + 32;\n\n if (end <= bytes.length) {\n const coordinate = bytes.slice(start, end);\n\n // Assign to x or y based on position or previous assignments\n if (!xBytes) {\n xBytes = coordinate;\n console.log('Found x at offset', i);\n } else if (!yBytes) {\n yBytes = coordinate;\n console.log('Found y at offset', i);\n break; // Found both, exit loop\n }\n }\n i = end;\n } else {\n i += length + 2;\n }\n } else if (bytes[i] === 0x42) { // Byte string with 2-byte length\n if (i + 3 < bytes.length) {\n const length = (bytes[i + 1] << 8) | bytes[i + 2];\n if (length === 32) {\n const start = i + 3;\n const end = start + 32;\n\n if (end <= bytes.length) {\n const coordinate = bytes.slice(start, end);\n\n if (!xBytes) {\n xBytes = coordinate;\n console.log('Found x at offset', i);\n } else if (!yBytes) {\n yBytes = coordinate;\n console.log('Found y at offset', i);\n break;\n }\n }\n i = end;\n } else {\n i += length + 3;\n }\n } else {\n i++;\n }\n } else if (bytes[i] === 0x40) { // Byte string with indefinite length\n // Skip indefinite length marker\n i++;\n // Look for 0x04 marker (uncompressed point) or direct coordinates\n while (i < bytes.length && bytes[i] !== 0xFF) { // 0xFF is break marker\n if (bytes[i] === 0x04 && i + 65 <= bytes.length) {\n // Uncompressed EC point format\n const x = bytes.slice(i + 1, i + 33);\n const y = bytes.slice(i + 33, i + 65);\n\n if (this.isValidCoordinate(x) && this.isValidCoordinate(y)) {\n xBytes = x;\n yBytes = y;\n console.log('Found coordinates in uncompressed point format');\n break;\n }\n }\n i++;\n }\n if (xBytes && yBytes) break;\n } else {\n i++;\n }\n }\n\n if (!xBytes || !yBytes) {\n // Fallback: Try to find any 32-byte sequences\n const potentialCoords = this.find32ByteSequences(bytes);\n if (potentialCoords.length >= 2) {\n xBytes = potentialCoords[0];\n yBytes = potentialCoords[1];\n console.log('Fallback: Using first two 32-byte sequences as coordinates');\n }\n }\n\n if (!xBytes || !yBytes) {\n console.error('Failed to find coordinates in COSE key. Full dump:');\n console.error('Hex:', this.bytesToHex(bytes));\n console.error('Structure analysis:');\n this.analyzeCOSEStructure(bytes);\n throw new Error('Failed to extract public key coordinates from COSE key. Check console for details.');\n }\n\n return {\n x: this.bytesToBigInt(xBytes),\n y: this.bytesToBigInt(yBytes)\n };\n }\n\n private tryParseASN1Structure(bytes: Uint8Array): { x: bigint; y: bigint } | null {\n // ASN.1 SEQUENCE for EC public key\n if (bytes[0] === 0x30) { // SEQUENCE tag\n let offset = 2; // Skip tag and length\n\n // Look for BIT STRING (0x03)\n if (bytes[offset] === 0x03) {\n offset += 2; // Skip BIT STRING tag and unused bits\n\n // Look for another SEQUENCE\n if (bytes[offset] === 0x30) {\n offset += 2;\n\n // Should now have OID for P-256: 1.2.840.10045.3.1.7\n // Skip OID (usually 10 bytes: 06 08 2A 86 48 CE 3D 03 01 07)\n offset += 12;\n\n // BIT STRING containing the raw public key\n if (bytes[offset] === 0x03 && bytes[offset + 2] === 0x04) {\n offset += 3; // Skip to uncompressed point (0x04)\n\n const x = bytes.slice(offset, offset + 32);\n const y = bytes.slice(offset + 32, offset + 64);\n\n if (x.length === 32 && y.length === 32) {\n console.log('Found coordinates via ASN.1 parsing');\n return {\n x: this.bytesToBigInt(x),\n y: this.bytesToBigInt(y)\n };\n }\n }\n }\n }\n }\n return null;\n }\n\n private find32ByteSequences(bytes: Uint8Array): Uint8Array[] {\n const sequences: Uint8Array[] = [];\n\n for (let i = 0; i <= bytes.length - 32; i++) {\n // Check if this 32-byte sequence looks like a valid coordinate\n const sequence = bytes.slice(i, i + 32);\n if (this.isValidCoordinate(sequence)) {\n sequences.push(sequence);\n }\n }\n\n return sequences;\n }\n\n private isValidCoordinate(bytes: Uint8Array): boolean {\n if (bytes.length !== 32) return false;\n\n // Basic validation: not all zeros, not all FF\n let allZeros = true;\n let allOnes = true;\n\n for (const byte of bytes) {\n if (byte !== 0) allZeros = false;\n if (byte !== 0xFF) allOnes = false;\n }\n\n return !allZeros && !allOnes;\n }\n\n private bytesToBigInt(bytes: Uint8Array): bigint {\n return BigInt('0x' + this.bytesToHex(bytes));\n }\n\n private bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n }\n\n private analyzeCOSEStructure(bytes: Uint8Array): void {\n console.log('COSE Structure Analysis:');\n console.log('First 20 bytes:', this.bytesToHex(bytes.slice(0, 20)));\n\n // Check for known COSE key structure markers\n const firstByte = bytes[0];\n console.log('First byte (0x' + firstByte.toString(16) + '):');\n\n if (firstByte >= 0xa0 && firstByte <= 0xbf) {\n console.log('- Definite length map with', (firstByte & 0x1f), 'pairs');\n } else if (firstByte === 0xbf) {\n console.log('- Indefinite length map');\n } else if (firstByte === 0x04) {\n console.log('- Byte string');\n } else if (firstByte === 0x02) {\n console.log('- Negative integer');\n }\n\n // Count occurrences of 32-byte sequences\n let count32 = 0;\n for (let i = 0; i <= bytes.length - 32; i++) {\n const chunk = bytes.slice(i, i + 32);\n if (this.isValidCoordinate(chunk)) {\n console.log(`Found valid 32-byte sequence at offset ${i}:`,\n this.bytesToHex(chunk.slice(0, 8)) + '...');\n count32++;\n }\n }\n console.log(`Total valid 32-byte sequences: ${count32}`);\n\n // Look for specific markers\n console.log('Looking for known markers:');\n const markers = [\n { byte: 0x04, name: 'Uncompressed point marker' },\n { byte: 0x03, name: 'BIT STRING' },\n { byte: 0x30, name: 'SEQUENCE' },\n { byte: 0x02, name: 'INTEGER' },\n { byte: 0x06, name: 'OBJECT IDENTIFIER' },\n { byte: 0x58, name: 'Byte string with length byte' },\n { byte: 0x42, name: 'Byte string with 2-byte length' },\n { byte: 0x40, name: 'Byte string indefinite length' },\n { byte: 0xA0, name: 'Map start' },\n { byte: 0xBF, name: 'Indefinite map start' },\n ];\n\n for (const marker of markers) {\n const indices = [];\n for (let i = 0; i < bytes.length; i++) {\n if (bytes[i] === marker.byte) {\n indices.push(i);\n }\n }\n if (indices.length > 0) {\n console.log(` ${marker.name} (0x${marker.byte.toString(16)}) at positions:`, indices.slice(0, 5).join(', '));\n }\n }\n }\n\n private parseAuthenticationResponse(response: AuthenticationResponseJSON): WebAuthnSignature {\n const authenticatorData = base64URLDecode(response.response.authenticatorData);\n const clientDataJSON = response.response.clientDataJSON;\n const signature = base64URLDecode(response.response.signature);\n\n const { r, s } = parseDERSignature(signature);\n\n // Normalize signature to low-S form.\n // The on-chain WebAuthn verifier rejects signatures with s > n/2.\n // WebAuthn authenticators are not guaranteed to produce low-S signatures,\n // so without this normalization, valid signatures can intermittently fail.\n const P256_N = BigInt('0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551');\n const P256_N_DIV_2 = BigInt(\n '0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8'\n );\n\n const clientDataStr = new TextDecoder().decode(base64URLDecode(clientDataJSON));\n const challengeIndex = clientDataStr.indexOf('\"challenge\"');\n const typeIndex = clientDataStr.indexOf('\"type\"');\n\n if (challengeIndex === -1 || typeIndex === -1) {\n throw new Error('Invalid clientDataJSON format');\n }\n\n return {\n authenticatorData: ethers.hexlify(authenticatorData),\n clientDataJSON: clientDataStr,\n challengeIndex,\n typeIndex,\n r: this.bytesToBigInt(r),\n s: (() => {\n const sBig = this.bytesToBigInt(s);\n return sBig > P256_N_DIV_2 ? P256_N - sBig : sBig;\n })(),\n };\n }\n}\n","const API_ROOT = '/api/v1';\n\nfunction trimTrailingSlashes(value: string): string {\n return value.trim().replace(/\\/+$/, '');\n}\n\nexport function normalizeRelayerOrigin(value: string): string {\n const trimmed = trimTrailingSlashes(value);\n if (!trimmed) {\n return '';\n }\n\n if (trimmed.startsWith('/')) {\n return trimmed.replace(/\\/api\\/v1$/i, '');\n }\n\n try {\n const url = new URL(trimmed);\n url.pathname = url.pathname.replace(/\\/api\\/v1$/i, '').replace(/\\/+$/, '');\n return url.toString().replace(/\\/+$/, '');\n } catch {\n return trimmed.replace(/\\/api\\/v1$/i, '');\n }\n}\n\nexport function buildRelayerApiUrl(baseUrl: string, path: string): string {\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const trimmed = trimTrailingSlashes(baseUrl);\n\n if (!trimmed) {\n return `${API_ROOT}${normalizedPath}`;\n }\n\n if (trimmed.startsWith('/')) {\n if (/\\/api\\/v1$/i.test(trimmed) || /\\/api\\/auth\\/relay$/i.test(trimmed)) {\n return `${trimmed}${normalizedPath}`;\n }\n\n return `${trimmed}${normalizedPath}`;\n }\n\n try {\n const url = new URL(trimmed);\n\n if (/\\/api\\/v1$/i.test(url.pathname) || /\\/api\\/auth\\/relay$/i.test(url.pathname)) {\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}${normalizedPath}`;\n return url.toString();\n }\n\n if (!url.pathname || url.pathname === '/') {\n url.pathname = `${API_ROOT}${normalizedPath}`;\n return url.toString();\n }\n\n url.pathname = `${url.pathname.replace(/\\/+$/, '')}${normalizedPath}`;\n return url.toString();\n } catch {\n if (/\\/api\\/v1$/i.test(trimmed) || /\\/api\\/auth\\/relay$/i.test(trimmed)) {\n return `${trimmed}${normalizedPath}`;\n }\n\n return `${trimmed}${API_ROOT}${normalizedPath}`;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chains/sui/SuiClient.ts"],"sourcesContent":["/**\n * Veridex Protocol SDK - Sui Chain Client\n *\n * Production-grade implementation of ChainClient interface for Sui.\n * Supports session management, query-based execution, and vault operations.\n *\n * Security:\n * - Native sui::ecdsa_k1::secp256k1_verify for signature validation\n * - CCQ-based session validation with 60s staleness window\n * - Replay protection via nonce verification\n *\n * Note: Sui is a spoke chain. Session registration/revocation happens on Hub.\n */\n\nimport { SuiClient as MystenSuiClient } from '@mysten/sui/client';\nimport { createHash } from 'crypto';\nimport type { SessionKey } from '../../sessions/types.js';\nimport type {\n ChainClient,\n ChainConfig,\n TransferParams,\n ExecuteParams,\n BridgeParams,\n DispatchResult,\n WebAuthnSignature,\n VaultCreationResult,\n RegisterSessionParams,\n RevokeSessionParams,\n SessionValidationResult,\n} from '../../core/types.js';\nimport { encodeTransferAction, encodeExecuteAction, encodeBridgeAction } from '../../payload.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SuiClientConfig {\n wormholeChainId: number;\n rpcUrl: string;\n packageId: string; // Veridex Spoke package ID\n wormholeCoreBridge: string;\n tokenBridge?: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n hubRpcUrl?: string; // Hub chain RPC for session management\n hubContractAddress?: string; // Hub contract for session management\n}\n\n// ============================================================================\n// SuiClient\n// ============================================================================\n\nexport class SuiClient implements ChainClient {\n private config: ChainConfig;\n private client: MystenSuiClient;\n private packageId: string;\n private hubRpcUrl?: string;\n private hubContractAddress?: string;\n\n constructor(config: SuiClientConfig) {\n this.config = {\n name: `Sui ${config.network || 'mainnet'}`,\n chainId: 0,\n wormholeChainId: config.wormholeChainId,\n rpcUrl: config.rpcUrl,\n explorerUrl: config.network === 'testnet'\n ? 'https://suiscan.xyz/testnet'\n : config.network === 'devnet'\n ? 'https://suiscan.xyz/devnet'\n : 'https://suiscan.xyz/mainnet',\n isEvm: false,\n contracts: {\n hub: config.packageId,\n wormholeCoreBridge: config.wormholeCoreBridge,\n tokenBridge: config.tokenBridge,\n },\n };\n\n this.client = new MystenSuiClient({ url: config.rpcUrl });\n this.packageId = config.packageId;\n this.hubRpcUrl = config.hubRpcUrl;\n this.hubContractAddress = config.hubContractAddress;\n }\n\n getConfig(): ChainConfig {\n return this.config;\n }\n\n async getNonce(_userKeyHash: string): Promise<bigint> {\n // Nonce is managed on the Hub for cross-chain actions.\n return 0n;\n }\n\n async getMessageFee(): Promise<bigint> {\n // Wormhole fees for Sui are generally handled by relayer submission.\n return 0n;\n }\n\n async buildTransferPayload(params: TransferParams): Promise<string> {\n return encodeTransferAction(params.token, params.recipient, params.amount);\n }\n\n async buildExecutePayload(params: ExecuteParams): Promise<string> {\n return encodeExecuteAction(params.target, params.value, params.data);\n }\n\n async buildBridgePayload(params: BridgeParams): Promise<string> {\n return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);\n }\n\n async dispatch(\n signature: WebAuthnSignature,\n publicKeyX: bigint,\n publicKeyY: bigint,\n targetChain: number,\n actionPayload: string,\n nonce: bigint,\n signer: any\n ): Promise<DispatchResult> {\n void signature;\n void publicKeyX;\n void publicKeyY;\n void targetChain;\n void actionPayload;\n void nonce;\n void signer;\n throw new Error(\n 'Direct dispatch not supported on Sui spoke chains. ' +\n 'Actions must be dispatched from the Hub (EVM) chain. '\n );\n }\n\n async dispatchGasless(\n signature: WebAuthnSignature,\n publicKeyX: bigint,\n publicKeyY: bigint,\n targetChain: number,\n actionPayload: string,\n nonce: bigint,\n relayerUrl: string\n ): Promise<DispatchResult> {\n const keyHash = this.computeKeyHash(publicKeyX, publicKeyY);\n const messageHash = this.buildMessageHash(keyHash, targetChain, actionPayload, nonce);\n\n const request = {\n messageHash,\n r: '0x' + signature.r.toString(16).padStart(64, '0'),\n s: '0x' + signature.s.toString(16).padStart(64, '0'),\n publicKeyX: '0x' + publicKeyX.toString(16).padStart(64, '0'),\n publicKeyY: '0x' + publicKeyY.toString(16).padStart(64, '0'),\n targetChain,\n actionPayload,\n nonce: Number(nonce),\n };\n\n const response = await fetch(`${relayerUrl}/api/v1/submit`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Relayer submission failed: ${response.status} ${response.statusText}`);\n }\n\n const result = await response.json();\n\n return {\n transactionHash: result.transactionHash ?? result.txHash,\n sequence: BigInt(result.sequence || 0),\n userKeyHash: keyHash,\n targetChain,\n };\n }\n\n async getVaultAddress(userKeyHash: string): Promise<string | null> {\n // Sui vaults may be represented as shared objects on-chain; without registry object IDs,\n // we return the deterministic address used by the SDK for receiving and balance display.\n return this.computeVaultAddress(userKeyHash);\n }\n\n computeVaultAddress(userKeyHash: string): string {\n // SDK convention: Sui addresses are 32-byte hex with 0x prefix.\n const clean = userKeyHash.replace(/^0x/, '').padStart(64, '0');\n return '0x' + clean;\n }\n\n async vaultExists(_userKeyHash: string): Promise<boolean> {\n // Account addresses on Sui are implicit; treat as existing.\n return true;\n }\n\n async createVault(userKeyHash: string, signer: any): Promise<VaultCreationResult> {\n void signer;\n throw new Error(\n 'Vault creation on Sui must be done via cross-chain message from Hub. ' +\n `Use the Hub client to dispatch a vault creation action targeting Sui (chain ${this.config.wormholeChainId}). KeyHash=${userKeyHash}`\n );\n }\n\n async createVaultSponsored?(userKeyHash: string, sponsorPrivateKey: string, rpcUrl?: string): Promise<VaultCreationResult> {\n void userKeyHash;\n void sponsorPrivateKey;\n void rpcUrl;\n throw new Error(\n 'Vault creation on Sui must be done via cross-chain message from Hub. ' +\n 'Use relayer gasless submission to create vault.'\n );\n }\n\n /**\n * Create a vault via the relayer (sponsored/gasless)\n * This is the recommended way to create Sui vaults\n * \n * The relayer will dispatch a vault creation action from Hub to Sui spoke\n */\n async createVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<VaultCreationResult> {\n const response = await fetch(`${relayerUrl}/api/v1/sui/vault`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n userKeyHash,\n chainId: this.config.wormholeChainId,\n }),\n });\n\n const result = await response.json();\n\n if (!response.ok || !result.success) {\n throw new Error(result.error || 'Failed to create vault via relayer');\n }\n\n return {\n address: result.vaultAddress,\n transactionHash: result.transactionHash || '',\n blockNumber: 0,\n gasUsed: 0n,\n alreadyExisted: result.alreadyExists || false,\n sponsoredBy: 'relayer',\n };\n }\n\n /**\n * Get vault info via relayer (includes existence check)\n */\n async getVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<{ vaultAddress: string; exists: boolean }> {\n const response = await fetch(\n `${relayerUrl}/api/v1/sui/vault/${userKeyHash}?chainId=${this.config.wormholeChainId}`\n );\n\n if (!response.ok) {\n throw new Error('Failed to get vault info from relayer');\n }\n\n const result = await response.json();\n return {\n vaultAddress: result.vaultAddress,\n exists: result.exists,\n };\n }\n\n async estimateVaultCreationGas(_userKeyHash: string): Promise<bigint> {\n // Best-effort placeholder.\n return 5_000n;\n }\n\n getFactoryAddress(): string | undefined {\n return undefined;\n }\n\n getImplementationAddress(): string | undefined {\n return undefined;\n }\n\n // ========================================================================\n // Balance utilities (used by VeridexSDK multichain)\n // ========================================================================\n\n async getNativeBalance(address: string): Promise<bigint> {\n const balance = await this.client.getBalance({ owner: address });\n return BigInt(balance.totalBalance);\n }\n\n async getTokenBalance(coinType: string, ownerAddress: string): Promise<bigint> {\n const balance = await this.client.getBalance({ owner: ownerAddress, coinType });\n return BigInt(balance.totalBalance);\n }\n\n getClient(): MystenSuiClient {\n return this.client;\n }\n\n getPackageId(): string {\n return this.packageId;\n }\n\n // ========================================================================\n // Session Management (Issue #13)\n // ========================================================================\n\n /**\n * Register a session key on the Hub (must be called via Hub client)\n * Sui spokes validate sessions via CCQ, but registration happens on Hub\n * \n * @throws Error - Session management must be done via Hub chain\n */\n async registerSession(_params: RegisterSessionParams): Promise<void> {\n throw new Error(\n 'Session registration must be performed on the Hub chain (Base). ' +\n 'Use EVMClient connected to the Hub to call registerSession().'\n );\n }\n\n /**\n * Revoke a session key on the Hub (must be called via Hub client)\n * \n * @throws Error - Session management must be done via Hub chain\n */\n async revokeSession(_params: RevokeSessionParams): Promise<void> {\n throw new Error(\n 'Session revocation must be performed on the Hub chain (Base). ' +\n 'Use EVMClient connected to the Hub to call revokeSession().'\n );\n }\n\n /**\n * Check if a session is active by querying the Hub\n * This method queries the Hub contract directly for session validation\n * \n * @param userKeyHash - Hash of user's Passkey public key\n * @param sessionKeyHash - Hash of session key to validate\n * @returns Session validation result with expiry and limits\n */\n async isSessionActive(\n _userKeyHash: string,\n _sessionKeyHash: string\n ): Promise<SessionValidationResult> {\n if (!this.hubRpcUrl || !this.hubContractAddress) {\n throw new Error(\n 'Hub configuration required for session validation. ' +\n 'Provide hubRpcUrl and hubContractAddress in SuiClientConfig.'\n );\n }\n\n // Query Hub contract for session status\n // This would normally use ethers.js to query the Hub contract\n // For production, import ethers dynamically or pass Hub client\n throw new Error(\n 'isSessionActive requires Hub client integration. ' +\n 'Use EVMClient.isSessionActive() on the Hub chain, ' +\n 'then pass the result to session execution on Sui.'\n );\n }\n\n /**\n * Get all sessions for a user from the Hub\n * \n * @param userKeyHash - Hash of user's Passkey public key\n * @returns Array of all sessions (active and expired/revoked)\n */\n async getUserSessions(userKeyHash: string): Promise<SessionKey[]> {\n if (!this.hubRpcUrl || !this.hubContractAddress) {\n throw new Error(\n 'Hub configuration required for session queries. ' +\n 'Provide hubRpcUrl and hubContractAddress in SuiClientConfig.'\n );\n }\n\n // Query Hub contract for user sessions\n throw new Error(\n 'getUserSessions requires Hub client integration. ' +\n 'Use EVMClient.getUserSessions() on the Hub chain. ' +\n `User: ${userKeyHash}`\n );\n }\n\n // ========================================================================\n // Query-Based Execution (Issue #9/#10)\n // ========================================================================\n\n /**\n * Get user state from Hub (comprehensive state query)\n * Returns key hash, nonce, and last action hash for CCQ validation\n * \n * @param userKeyHash - Hash of user's Passkey public key\n * @returns User state with nonce and last action hash\n */\n async getUserState(userKeyHash: string): Promise<{\n keyHash: string;\n nonce: bigint;\n lastActionHash: string;\n }> {\n if (!this.hubRpcUrl || !this.hubContractAddress) {\n throw new Error(\n 'Hub configuration required for state queries. ' +\n 'Provide hubRpcUrl and hubContractAddress in SuiClientConfig.'\n );\n }\n\n // Query Hub contract for user state\n // This enables query-based execution with CCQ validation\n throw new Error(\n 'getUserState requires Hub client integration. ' +\n 'Use EVMClient.getUserState() on the Hub chain. ' +\n `User: ${userKeyHash}`\n );\n }\n\n /**\n * Get user's last action hash from Hub\n * Used for optimistic execution and nonce validation\n * \n * @param userKeyHash - Hash of user's Passkey public key\n * @returns Last action hash (zero hash if no actions)\n */\n async getUserLastActionHash(userKeyHash: string): Promise<string> {\n if (!this.hubRpcUrl || !this.hubContractAddress) {\n throw new Error(\n 'Hub configuration required for action hash queries. ' +\n 'Provide hubRpcUrl and hubContractAddress in SuiClientConfig.'\n );\n }\n\n // Query Hub contract for last action hash\n throw new Error(\n 'getUserLastActionHash requires Hub client integration. ' +\n 'Use EVMClient.getUserLastActionHash() on the Hub chain. ' +\n `User: ${userKeyHash}`\n );\n }\n\n /**\n * Execute with query-based validation (faster than VAA, ~23s vs 60-90s)\n * Uses Wormhole CCQ to validate Hub state, then executes on Sui\n * \n * @param params Query execution parameters with CCQ response\n * @returns Dispatch result with transaction hash\n * \n * @remarks\n * Query-based execution flow:\n * 1. Query Hub state via Wormhole CCQ\n * 2. Validate Guardian signatures on query response\n * 3. Execute on Sui with validated state\n * 4. Hub state must be < 60s stale (enforced by QueryVerifier)\n */\n async executeWithQuery(\n _params: {\n userKeyHash: string;\n queryResponse: Uint8Array; // CCQ Guardian response\n actionType: number;\n actionPayload: Uint8Array;\n relayerUrl?: string;\n }\n ): Promise<DispatchResult> {\n throw new Error(\n 'Query-based execution on Sui requires relayer integration. ' +\n 'Use relayer API to submit query-validated transactions. ' +\n 'Relayer will call veridex_spoke::execute_with_query on Sui.'\n );\n }\n\n // ========================================================================\n // Internal helpers\n // ========================================================================\n\n private computeKeyHash(publicKeyX: bigint, publicKeyY: bigint): string {\n const xHex = publicKeyX.toString(16).padStart(64, '0');\n const yHex = publicKeyY.toString(16).padStart(64, '0');\n const combined = Buffer.from(xHex + yHex, 'hex');\n const hash = createHash('sha256').update(combined).digest('hex');\n return '0x' + hash;\n }\n\n private buildMessageHash(keyHash: string, targetChain: number, actionPayload: string, nonce: bigint): string {\n const keyHashBuffer = Buffer.from(keyHash.replace(/^0x/, ''), 'hex');\n const targetChainBuffer = Buffer.alloc(2);\n targetChainBuffer.writeUInt16BE(targetChain);\n const payloadBuffer = Buffer.from(actionPayload.replace(/^0x/, ''), 'hex');\n const nonceHex = nonce.toString(16).padStart(64, '0');\n const nonceBuffer = Buffer.from(nonceHex, 'hex');\n\n const combined = Buffer.concat([keyHashBuffer, targetChainBuffer, payloadBuffer, nonceBuffer]);\n const hash = createHash('sha256').update(combined).digest('hex');\n return '0x' + hash;\n }\n\n // ============================================================================\n // Social Recovery Methods (Issue #23)\n // ============================================================================\n // \n // Note: Social recovery is managed on the Hub chain (EVM).\n // Sui spokes receive and execute recovery VAAs broadcast from the Hub.\n // The relayer service handles submitting recovery transactions to Sui.\n //\n // SDK users should use EVMClient methods for guardian management and\n // recovery initiation on the Hub chain.\n // ============================================================================\n\n /**\n * Get vault object ID by owner key hash\n * \n * @param ownerKeyHash - Owner's passkey hash (32 bytes as hex)\n * @param configObjectId - Shared Config object ID\n * @param registryObjectId - Shared VaultRegistry object ID\n * @returns Vault object ID or null if not found\n */\n async getVaultId(\n _ownerKeyHash: string,\n registryObjectId: string\n ): Promise<string | null> {\n try {\n // Query the VaultRegistry table for the owner_key_hash\n // The registry maps owner_key_hash -> vault ID\n const registryObject = await this.client.getObject({\n id: registryObjectId,\n options: { showContent: true },\n });\n\n if (!registryObject.data?.content) {\n return null;\n }\n\n // For a proper implementation, we'd need to query dynamic fields\n // or use a Move view function. This is a placeholder showing the pattern.\n console.warn('getVaultId requires dynamic field query - use Move view function');\n return null;\n } catch (error) {\n console.error('Error getting vault ID:', error);\n return null;\n }\n }\n\n /**\n * Get vault owner key hash from vault object\n * \n * @param vaultObjectId - Vault object ID\n * @returns Owner key hash as hex string\n */\n async getVaultOwner(vaultObjectId: string): Promise<string | null> {\n try {\n const vaultObject = await this.client.getObject({\n id: vaultObjectId,\n options: { showContent: true },\n });\n\n if (!vaultObject.data?.content || vaultObject.data.content.dataType !== 'moveObject') {\n return null;\n }\n\n const fields = vaultObject.data.content.fields as Record<string, unknown>;\n const ownerKeyHash = fields['owner_key_hash'] as number[] | undefined;\n\n if (!ownerKeyHash) {\n return null;\n }\n\n // Convert byte array to hex string\n return '0x' + Buffer.from(ownerKeyHash).toString('hex');\n } catch (error) {\n console.error('Error getting vault owner:', error);\n return null;\n }\n }\n\n /**\n * Get authorized signers for a vault\n * \n * @param vaultObjectId - Vault object ID\n * @returns Array of authorized signer key hashes\n */\n async getAuthorizedSigners(vaultObjectId: string): Promise<string[]> {\n try {\n const vaultObject = await this.client.getObject({\n id: vaultObjectId,\n options: { showContent: true },\n });\n\n if (!vaultObject.data?.content || vaultObject.data.content.dataType !== 'moveObject') {\n return [];\n }\n\n const fields = vaultObject.data.content.fields as Record<string, unknown>;\n const authorizedSigners = fields['authorized_signers'] as number[][] | undefined;\n\n if (!authorizedSigners) {\n return [];\n }\n\n // Convert each byte array to hex string\n return authorizedSigners.map(signer => \n '0x' + Buffer.from(signer).toString('hex')\n );\n } catch (error) {\n console.error('Error getting authorized signers:', error);\n return [];\n }\n }\n\n /**\n * Check if a VAA has been processed (for replay protection)\n * \n * @param vaaHash - VAA hash as hex string\n * @param processedVaasObjectId - ProcessedVAAs shared object ID\n * @returns Whether the VAA has been processed\n */\n async isVaaProcessed(\n _vaaHash: string,\n processedVaasObjectId: string\n ): Promise<boolean> {\n try {\n const processedObject = await this.client.getObject({\n id: processedVaasObjectId,\n options: { showContent: true },\n });\n\n if (!processedObject.data?.content) {\n return false;\n }\n\n // Would need to query dynamic field for vaaHash key\n console.warn('isVaaProcessed requires dynamic field query');\n return false;\n } catch (error) {\n console.error('Error checking VAA status:', error);\n return false;\n }\n }\n\n /**\n * Check if protocol is paused\n * \n * @param configObjectId - Config shared object ID\n * @returns Whether the protocol is paused\n */\n async isProtocolPaused(configObjectId: string): Promise<boolean> {\n try {\n const configObject = await this.client.getObject({\n id: configObjectId,\n options: { showContent: true },\n });\n\n if (!configObject.data?.content || configObject.data.content.dataType !== 'moveObject') {\n return false;\n }\n\n const fields = configObject.data.content.fields as Record<string, unknown>;\n return fields['paused'] === true;\n } catch (error) {\n console.error('Error checking pause status:', error);\n return false;\n }\n }\n}\n"],"mappings":";;;;;;;AAcA,SAAS,aAAa,uBAAuB;AAC7C,SAAS,kBAAkB;AAoCpB,IAAM,YAAN,MAAuC;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAAyB;AACjC,SAAK,SAAS;AAAA,MACV,MAAM,OAAO,OAAO,WAAW,SAAS;AAAA,MACxC,SAAS;AAAA,MACT,iBAAiB,OAAO;AAAA,MACxB,QAAQ,OAAO;AAAA,MACf,aAAa,OAAO,YAAY,YAC1B,gCACA,OAAO,YAAY,WACf,+BACA;AAAA,MACV,OAAO;AAAA,MACP,WAAW;AAAA,QACP,KAAK,OAAO;AAAA,QACZ,oBAAoB,OAAO;AAAA,QAC3B,aAAa,OAAO;AAAA,MACxB;AAAA,IACJ;AAEA,SAAK,SAAS,IAAI,gBAAgB,EAAE,KAAK,OAAO,OAAO,CAAC;AACxD,SAAK,YAAY,OAAO;AACxB,SAAK,YAAY,OAAO;AACxB,SAAK,qBAAqB,OAAO;AAAA,EACrC;AAAA,EAEA,YAAyB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,SAAS,cAAuC;AAElD,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,gBAAiC;AAEnC,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,qBAAqB,QAAyC;AAChE,WAAO,qBAAqB,OAAO,OAAO,OAAO,WAAW,OAAO,MAAM;AAAA,EAC7E;AAAA,EAEA,MAAM,oBAAoB,QAAwC;AAC9D,WAAO,oBAAoB,OAAO,QAAQ,OAAO,OAAO,OAAO,IAAI;AAAA,EACvE;AAAA,EAEA,MAAM,mBAAmB,QAAuC;AAC5D,WAAO,mBAAmB,OAAO,OAAO,OAAO,QAAQ,OAAO,kBAAkB,OAAO,SAAS;AAAA,EACpG;AAAA,EAEA,MAAM,SACF,WACA,YACA,YACA,aACA,eACA,OACA,QACuB;AACvB,SAAK;AACL,SAAK;AACL,SAAK;AACL,SAAK;AACL,SAAK;AACL,SAAK;AACL,SAAK;AACL,UAAM,IAAI;AAAA,MACN;AAAA,IAEJ;AAAA,EACJ;AAAA,EAEA,MAAM,gBACF,WACA,YACA,YACA,aACA,eACA,OACA,YACuB;AACvB,UAAM,UAAU,KAAK,eAAe,YAAY,UAAU;AAC1D,UAAM,cAAc,KAAK,iBAAiB,SAAS,aAAa,eAAe,KAAK;AAEpF,UAAM,UAAU;AAAA,MACZ;AAAA,MACA,GAAG,OAAO,UAAU,EAAE,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AAAA,MACnD,GAAG,OAAO,UAAU,EAAE,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AAAA,MACnD,YAAY,OAAO,WAAW,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AAAA,MAC3D,YAAY,OAAO,WAAW,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AAAA,MAC3D;AAAA,MACA;AAAA,MACA,OAAO,OAAO,KAAK;AAAA,IACvB;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,UAAU,kBAAkB;AAAA,MACxD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,IAChC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,IAAI,MAAM,8BAA8B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,IAC1F;AAEA,UAAM,SAAS,MAAM,SAAS,KAAK;AAEnC,WAAO;AAAA,MACH,iBAAiB,OAAO,mBAAmB,OAAO;AAAA,MAClD,UAAU,OAAO,OAAO,YAAY,CAAC;AAAA,MACrC,aAAa;AAAA,MACb;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,gBAAgB,aAA6C;AAG/D,WAAO,KAAK,oBAAoB,WAAW;AAAA,EAC/C;AAAA,EAEA,oBAAoB,aAA6B;AAE7C,UAAM,QAAQ,YAAY,QAAQ,OAAO,EAAE,EAAE,SAAS,IAAI,GAAG;AAC7D,WAAO,OAAO;AAAA,EAClB;AAAA,EAEA,MAAM,YAAY,cAAwC;AAEtD,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,YAAY,aAAqB,QAA2C;AAC9E,SAAK;AACL,UAAM,IAAI;AAAA,MACN,oJAC+E,KAAK,OAAO,eAAe,cAAc,WAAW;AAAA,IACvI;AAAA,EACJ;AAAA,EAEA,MAAM,qBAAsB,aAAqB,mBAA2B,QAA+C;AACvH,SAAK;AACL,SAAK;AACL,SAAK;AACL,UAAM,IAAI;AAAA,MACN;AAAA,IAEJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,sBACF,aACA,YAC4B;AAC5B,UAAM,WAAW,MAAM,MAAM,GAAG,UAAU,qBAAqB;AAAA,MAC3D,QAAQ;AAAA,MACR,SAAS;AAAA,QACL,gBAAgB;AAAA,MACpB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACjB;AAAA,QACA,SAAS,KAAK,OAAO;AAAA,MACzB,CAAC;AAAA,IACL,CAAC;AAED,UAAM,SAAS,MAAM,SAAS,KAAK;AAEnC,QAAI,CAAC,SAAS,MAAM,CAAC,OAAO,SAAS;AACjC,YAAM,IAAI,MAAM,OAAO,SAAS,oCAAoC;AAAA,IACxE;AAEA,WAAO;AAAA,MACH,SAAS,OAAO;AAAA,MAChB,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,aAAa;AAAA,MACb,SAAS;AAAA,MACT,gBAAgB,OAAO,iBAAiB;AAAA,MACxC,aAAa;AAAA,IACjB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACF,aACA,YACkD;AAClD,UAAM,WAAW,MAAM;AAAA,MACnB,GAAG,UAAU,qBAAqB,WAAW,YAAY,KAAK,OAAO,eAAe;AAAA,IACxF;AAEA,QAAI,CAAC,SAAS,IAAI;AACd,YAAM,IAAI,MAAM,uCAAuC;AAAA,IAC3D;AAEA,UAAM,SAAS,MAAM,SAAS,KAAK;AACnC,WAAO;AAAA,MACH,cAAc,OAAO;AAAA,MACrB,QAAQ,OAAO;AAAA,IACnB;AAAA,EACJ;AAAA,EAEA,MAAM,yBAAyB,cAAuC;AAElE,WAAO;AAAA,EACX;AAAA,EAEA,oBAAwC;AACpC,WAAO;AAAA,EACX;AAAA,EAEA,2BAA+C;AAC3C,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBAAiB,SAAkC;AACrD,UAAM,UAAU,MAAM,KAAK,OAAO,WAAW,EAAE,OAAO,QAAQ,CAAC;AAC/D,WAAO,OAAO,QAAQ,YAAY;AAAA,EACtC;AAAA,EAEA,MAAM,gBAAgB,UAAkB,cAAuC;AAC3E,UAAM,UAAU,MAAM,KAAK,OAAO,WAAW,EAAE,OAAO,cAAc,SAAS,CAAC;AAC9E,WAAO,OAAO,QAAQ,YAAY;AAAA,EACtC;AAAA,EAEA,YAA6B;AACzB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,eAAuB;AACnB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,gBAAgB,SAA+C;AACjE,UAAM,IAAI;AAAA,MACN;AAAA,IAEJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,SAA6C;AAC7D,UAAM,IAAI;AAAA,MACN;AAAA,IAEJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,gBACF,cACA,iBACgC;AAChC,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,oBAAoB;AAC7C,YAAM,IAAI;AAAA,QACN;AAAA,MAEJ;AAAA,IACJ;AAKA,UAAM,IAAI;AAAA,MACN;AAAA,IAGJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBAAgB,aAA4C;AAC9D,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,oBAAoB;AAC7C,YAAM,IAAI;AAAA,QACN;AAAA,MAEJ;AAAA,IACJ;AAGA,UAAM,IAAI;AAAA,MACN,4GAES,WAAW;AAAA,IACxB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,aAAa,aAIhB;AACC,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,oBAAoB;AAC7C,YAAM,IAAI;AAAA,QACN;AAAA,MAEJ;AAAA,IACJ;AAIA,UAAM,IAAI;AAAA,MACN,sGAES,WAAW;AAAA,IACxB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,sBAAsB,aAAsC;AAC9D,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,oBAAoB;AAC7C,YAAM,IAAI;AAAA,QACN;AAAA,MAEJ;AAAA,IACJ;AAGA,UAAM,IAAI;AAAA,MACN,wHAES,WAAW;AAAA,IACxB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,iBACF,SAOuB;AACvB,UAAM,IAAI;AAAA,MACN;AAAA,IAGJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,YAAoB,YAA4B;AACnE,UAAM,OAAO,WAAW,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AACrD,UAAM,OAAO,WAAW,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AACrD,UAAM,WAAW,OAAO,KAAK,OAAO,MAAM,KAAK;AAC/C,UAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK;AAC/D,WAAO,OAAO;AAAA,EAClB;AAAA,EAEQ,iBAAiB,SAAiB,aAAqB,eAAuB,OAAuB;AACzG,UAAM,gBAAgB,OAAO,KAAK,QAAQ,QAAQ,OAAO,EAAE,GAAG,KAAK;AACnE,UAAM,oBAAoB,OAAO,MAAM,CAAC;AACxC,sBAAkB,cAAc,WAAW;AAC3C,UAAM,gBAAgB,OAAO,KAAK,cAAc,QAAQ,OAAO,EAAE,GAAG,KAAK;AACzE,UAAM,WAAW,MAAM,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG;AACpD,UAAM,cAAc,OAAO,KAAK,UAAU,KAAK;AAE/C,UAAM,WAAW,OAAO,OAAO,CAAC,eAAe,mBAAmB,eAAe,WAAW,CAAC;AAC7F,UAAM,OAAO,WAAW,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,KAAK;AAC/D,WAAO,OAAO;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,WACF,eACA,kBACsB;AACtB,QAAI;AAGA,YAAM,iBAAiB,MAAM,KAAK,OAAO,UAAU;AAAA,QAC/C,IAAI;AAAA,QACJ,SAAS,EAAE,aAAa,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,eAAe,MAAM,SAAS;AAC/B,eAAO;AAAA,MACX;AAIA,cAAQ,KAAK,kEAAkE;AAC/E,aAAO;AAAA,IACX,SAAS,OAAO;AACZ,cAAQ,MAAM,2BAA2B,KAAK;AAC9C,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,eAA+C;AAC/D,QAAI;AACA,YAAM,cAAc,MAAM,KAAK,OAAO,UAAU;AAAA,QAC5C,IAAI;AAAA,QACJ,SAAS,EAAE,aAAa,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,YAAY,MAAM,WAAW,YAAY,KAAK,QAAQ,aAAa,cAAc;AAClF,eAAO;AAAA,MACX;AAEA,YAAM,SAAS,YAAY,KAAK,QAAQ;AACxC,YAAM,eAAe,OAAO,gBAAgB;AAE5C,UAAI,CAAC,cAAc;AACf,eAAO;AAAA,MACX;AAGA,aAAO,OAAO,OAAO,KAAK,YAAY,EAAE,SAAS,KAAK;AAAA,IAC1D,SAAS,OAAO;AACZ,cAAQ,MAAM,8BAA8B,KAAK;AACjD,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,qBAAqB,eAA0C;AACjE,QAAI;AACA,YAAM,cAAc,MAAM,KAAK,OAAO,UAAU;AAAA,QAC5C,IAAI;AAAA,QACJ,SAAS,EAAE,aAAa,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,YAAY,MAAM,WAAW,YAAY,KAAK,QAAQ,aAAa,cAAc;AAClF,eAAO,CAAC;AAAA,MACZ;AAEA,YAAM,SAAS,YAAY,KAAK,QAAQ;AACxC,YAAM,oBAAoB,OAAO,oBAAoB;AAErD,UAAI,CAAC,mBAAmB;AACpB,eAAO,CAAC;AAAA,MACZ;AAGA,aAAO,kBAAkB;AAAA,QAAI,YACzB,OAAO,OAAO,KAAK,MAAM,EAAE,SAAS,KAAK;AAAA,MAC7C;AAAA,IACJ,SAAS,OAAO;AACZ,cAAQ,MAAM,qCAAqC,KAAK;AACxD,aAAO,CAAC;AAAA,IACZ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eACF,UACA,uBACgB;AAChB,QAAI;AACA,YAAM,kBAAkB,MAAM,KAAK,OAAO,UAAU;AAAA,QAChD,IAAI;AAAA,QACJ,SAAS,EAAE,aAAa,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,gBAAgB,MAAM,SAAS;AAChC,eAAO;AAAA,MACX;AAGA,cAAQ,KAAK,6CAA6C;AAC1D,aAAO;AAAA,IACX,SAAS,OAAO;AACZ,cAAQ,MAAM,8BAA8B,KAAK;AACjD,aAAO;AAAA,IACX;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,iBAAiB,gBAA0C;AAC7D,QAAI;AACA,YAAM,eAAe,MAAM,KAAK,OAAO,UAAU;AAAA,QAC7C,IAAI;AAAA,QACJ,SAAS,EAAE,aAAa,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,aAAa,MAAM,WAAW,aAAa,KAAK,QAAQ,aAAa,cAAc;AACpF,eAAO;AAAA,MACX;AAEA,YAAM,SAAS,aAAa,KAAK,QAAQ;AACzC,aAAO,OAAO,QAAQ,MAAM;AAAA,IAChC,SAAS,OAAO;AACZ,cAAQ,MAAM,gCAAgC,KAAK;AACnD,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chains/aptos/AptosClient.ts"],"sourcesContent":["/**\n * Veridex Protocol SDK - Aptos Chain Client\n * \n * Implementation of ChainClient interface for Aptos blockchain\n * Updated to use @aptos-labs/ts-sdk v5.x\n */\n\nimport { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';\nimport { sha3_256 } from 'js-sha3';\nimport type {\n ChainClient,\n ChainConfig,\n TransferParams,\n ExecuteParams,\n BridgeParams,\n DispatchResult,\n WebAuthnSignature,\n VaultCreationResult,\n} from '../../core/types.js';\nimport { encodeTransferAction, encodeExecuteAction, encodeBridgeAction } from '../../payload.js';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Get Aptos network from string configuration\n * The new SDK works best with built-in network configuration\n */\nfunction getAptosNetworkEnum(network?: string): Network {\n if (network === 'mainnet') return Network.MAINNET;\n if (network === 'devnet') return Network.DEVNET;\n return Network.TESTNET; // Default to testnet\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AptosClientConfig {\n wormholeChainId: number;\n rpcUrl: string;\n moduleAddress: string; // Veridex Spoke module address\n wormholeCoreBridge: string;\n tokenBridge: string;\n network?: 'mainnet' | 'testnet' | 'devnet';\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n// ============================================================================\n// AptosClient Class\n// ============================================================================\n\n/**\n * Aptos implementation of the ChainClient interface\n */\nexport class AptosClient implements ChainClient {\n private config: ChainConfig;\n private client: Aptos;\n private moduleAddress: string;\n\n constructor(config: AptosClientConfig) {\n // Use SDK's built-in network configuration (more reliable than custom URLs)\n const network = getAptosNetworkEnum(config.network);\n \n this.config = {\n name: `Aptos ${config.network || 'mainnet'}`,\n chainId: config.wormholeChainId,\n wormholeChainId: config.wormholeChainId,\n rpcUrl: config.rpcUrl, // Keep for reference, but not used for SDK\n explorerUrl: config.network === 'testnet'\n ? 'https://explorer.aptoslabs.com?network=testnet'\n : 'https://explorer.aptoslabs.com',\n isEvm: false,\n contracts: {\n hub: undefined, // Aptos is a spoke only\n wormholeCoreBridge: config.wormholeCoreBridge,\n tokenBridge: config.tokenBridge,\n },\n };\n\n // Create Aptos client with built-in network configuration\n // This is more reliable than custom fullnode URLs\n const aptosConfig = new AptosConfig({ network });\n this.client = new Aptos(aptosConfig);\n this.moduleAddress = config.moduleAddress;\n }\n\n getConfig(): ChainConfig {\n return this.config;\n }\n\n async getNonce(userKeyHash: string): Promise<bigint> {\n try {\n const vaultAddress = this.computeVaultAddressFromHash(userKeyHash);\n\n // Query vault resource using new SDK\n const resources = await this.client.getAccountResources({ \n accountAddress: vaultAddress \n });\n const vaultResource = resources.find(\n (r: { type: string }) => r.type === `${this.moduleAddress}::vault::Vault`\n );\n \n if (!vaultResource) {\n return 0n;\n }\n\n if (vaultResource && vaultResource.data) {\n const data = vaultResource.data as { nonce?: string | number };\n return BigInt(data.nonce || 0);\n }\n\n return 0n;\n } catch (error) {\n console.error('Error getting nonce:', error);\n return 0n;\n }\n }\n\n async getMessageFee(): Promise<bigint> {\n try {\n // Query Wormhole bridge for message fee\n // For now, return a default estimate\n // TODO: Query on-chain Wormhole config\n return 0n; // Aptos doesn't charge a Wormhole fee in the same way\n } catch (error) {\n console.error('Error getting message fee:', error);\n return 0n;\n }\n }\n\n async buildTransferPayload(params: TransferParams): Promise<string> {\n return encodeTransferAction(\n params.token,\n params.recipient,\n params.amount\n );\n }\n\n async buildExecutePayload(params: ExecuteParams): Promise<string> {\n return encodeExecuteAction(\n params.target,\n params.value,\n params.data\n );\n }\n\n async buildBridgePayload(params: BridgeParams): Promise<string> {\n return encodeBridgeAction(\n params.token,\n params.amount,\n params.destinationChain,\n params.recipient\n );\n }\n\n async dispatch(\n signature: WebAuthnSignature,\n publicKeyX: bigint,\n publicKeyY: bigint,\n targetChain: number,\n actionPayload: string,\n nonce: bigint,\n signer: any // Aptos AptosAccount\n ): Promise<DispatchResult> {\n void signature;\n void publicKeyX;\n void publicKeyY;\n void targetChain;\n void actionPayload;\n void nonce;\n void signer;\n throw new Error(\n 'Direct dispatch not supported on Aptos spoke chains. ' +\n 'Actions must be dispatched from the Hub (EVM) chain. ' +\n 'This client is for receiving cross-chain messages only.'\n );\n }\n\n /**\n * Dispatch an action via relayer (gasless)\n * Note: On Aptos, this still goes through the Hub chain\n * Aptos is a spoke-only chain in Veridex architecture\n */\n async dispatchGasless(\n signature: WebAuthnSignature,\n publicKeyX: bigint,\n publicKeyY: bigint,\n targetChain: number,\n actionPayload: string,\n nonce: bigint,\n relayerUrl: string\n ): Promise<DispatchResult> {\n // Compute key hash\n const keyHash = this.computeKeyHash(publicKeyX, publicKeyY);\n\n // Build the message that was signed (matches Hub chain format)\n const message = this.buildMessage(keyHash, targetChain, actionPayload, nonce);\n\n // Prepare request for relayer\n const request = {\n messageHash: message,\n r: '0x' + signature.r.toString(16).padStart(64, '0'),\n s: '0x' + signature.s.toString(16).padStart(64, '0'),\n publicKeyX: '0x' + publicKeyX.toString(16).padStart(64, '0'),\n publicKeyY: '0x' + publicKeyY.toString(16).padStart(64, '0'),\n targetChain,\n actionPayload,\n nonce: Number(nonce),\n };\n\n // Submit to relayer\n const response = await fetch(`${relayerUrl}/api/v1/submit`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: response.statusText }));\n throw new Error(`Relayer submission failed: ${error.error || response.statusText}`);\n }\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(`Relayer submission failed: ${result.error}`);\n }\n\n return {\n transactionHash: result.txHash,\n sequence: BigInt(result.sequence || '0'),\n userKeyHash: keyHash,\n targetChain,\n };\n }\n\n /**\n * Get vault address from on-chain VaultRegistry.\n * Queries the get_vault_address view function which looks up the vault in the registry.\n */\n async getVaultAddress(userKeyHash: string): Promise<string | null> {\n try {\n // Normalize: remove 0x if present, lowercase, pad to 64 chars, then add 0x\n const rawHex = userKeyHash.startsWith('0x') \n ? userKeyHash.slice(2).toLowerCase()\n : userKeyHash.toLowerCase();\n const keyHashHex = `0x${rawHex.padStart(64, '0')}`;\n \n // First check if vault exists to avoid noisy 400 errors in console\n const existsResponse = await this.client.view({\n payload: {\n function: `${this.moduleAddress}::spoke::vault_exists`,\n typeArguments: [],\n functionArguments: [keyHashHex],\n }\n });\n if (!existsResponse || existsResponse.length === 0 || existsResponse[0] !== true) {\n return null; // Vault doesn't exist\n }\n \n // Vault exists, now get the address\n const response = await this.client.view({\n payload: {\n function: `${this.moduleAddress}::spoke::get_vault_address`,\n typeArguments: [],\n functionArguments: [keyHashHex],\n }\n });\n \n if (response && response.length > 0) {\n const vaultAddress = response[0] as string;\n return vaultAddress;\n }\n\n return null;\n } catch (error: any) {\n // E_VAULT_NOT_FOUND (error code 6) means vault doesn't exist in registry\n if (error?.message?.includes('E_VAULT_NOT_FOUND') || \n error?.message?.includes('error code 6') ||\n error?.status === 404) {\n return null;\n }\n console.error('Error getting vault address from registry:', error);\n return null;\n }\n }\n\n /**\n * @deprecated Use getVaultAddress() instead - this method uses incorrect address derivation.\n * On Aptos, vaults are created as named objects by the relayer, not resource accounts.\n * The vault address depends on which relayer created it, so must be queried on-chain.\n */\n computeVaultAddress(userKeyHash: string): string {\n console.warn(\n 'computeVaultAddress() is deprecated for Aptos. ' +\n 'Use getVaultAddress() to query the on-chain VaultRegistry instead.'\n );\n return this.computeVaultAddressFromHash(userKeyHash);\n }\n\n private computeVaultAddressFromHash(userKeyHash: string): string {\n // NOTE: This is kept for backward compatibility but produces INCORRECT addresses!\n // Aptos spoke uses object::create_named_object(creator, key_hash) where:\n // - creator = relayer address (not module address)\n // - scheme = 0xFD (named object, not 0xFE resource account)\n // The correct approach is to query the VaultRegistry on-chain.\n\n const sourceAddress = this.hexToBytes(this.moduleAddress.replace('0x', ''));\n const seed = this.hexToBytes(userKeyHash.replace('0x', ''));\n const scheme = new Uint8Array([0xFE]); // INCORRECT - kept for backward compat\n\n const combined = new Uint8Array([...sourceAddress, ...seed, ...scheme]);\n const hash = sha3_256(combined);\n\n return '0x' + hash;\n }\n\n /**\n * Convert hex string to Uint8Array (browser-compatible)\n */\n private hexToBytes(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.substr(i, 2), 16);\n }\n return bytes;\n }\n\n async vaultExists(userKeyHash: string): Promise<boolean> {\n const address = await this.getVaultAddress(userKeyHash);\n return address !== null;\n }\n\n async createVault(userKeyHash: string, signer: any): Promise<VaultCreationResult> {\n void userKeyHash;\n void signer;\n throw new Error(\n 'Vault creation on Aptos must be done via cross-chain message from Hub. ' +\n 'Use the Hub chain client to dispatch a vault creation action targeting Aptos.'\n );\n }\n\n async createVaultSponsored?(\n userKeyHash: string,\n sponsorPrivateKey: string,\n rpcUrl?: string\n ): Promise<VaultCreationResult> {\n void userKeyHash;\n void sponsorPrivateKey;\n void rpcUrl;\n throw new Error(\n 'Vault creation on Aptos must be done via cross-chain message from Hub. ' +\n 'Use relayer gasless submission to create vault.'\n );\n }\n\n /**\n * Create a vault via the relayer (sponsored/gasless)\n * This is the recommended way to create Aptos vaults\n * \n * The relayer will dispatch a vault creation action from Hub to Aptos spoke\n */\n async createVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<VaultCreationResult> {\n const response = await fetch(`${relayerUrl}/api/v1/aptos/vault`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n userKeyHash,\n chainId: this.config.wormholeChainId,\n }),\n });\n\n const result = await response.json();\n\n if (!response.ok || !result.success) {\n throw new Error(result.error || 'Failed to create vault via relayer');\n }\n\n return {\n address: result.vaultAddress,\n transactionHash: result.transactionHash || '',\n blockNumber: 0,\n gasUsed: 0n,\n alreadyExisted: result.alreadyExists || false,\n sponsoredBy: 'relayer',\n };\n }\n\n /**\n * Get vault info via relayer (includes existence check)\n */\n async getVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<{ vaultAddress: string; exists: boolean }> {\n const response = await fetch(\n `${relayerUrl}/api/v1/aptos/vault/${userKeyHash}?chainId=${this.config.wormholeChainId}`\n );\n\n if (!response.ok) {\n throw new Error('Failed to get vault info from relayer');\n }\n\n const result = await response.json();\n return {\n vaultAddress: result.vaultAddress,\n exists: result.exists,\n };\n }\n\n async estimateVaultCreationGas(userKeyHash: string): Promise<bigint> {\n void userKeyHash;\n // Return APT estimate for vault creation\n // ~0.001 APT for account creation + gas\n return 100_000n; // 0.001 APT in octas (1 APT = 100M octas)\n }\n\n getFactoryAddress(): string | undefined {\n // Aptos uses module addresses, not factory pattern\n return undefined;\n }\n\n getImplementationAddress(): string | undefined {\n // Aptos uses module addresses, not implementation pattern\n return undefined;\n }\n\n // ========================================================================\n // Balance Methods\n // ========================================================================\n\n /**\n * Get native APT balance\n */\n async getNativeBalance(address: string): Promise<bigint> {\n try {\n const resource = await this.client.getAccountResource({\n accountAddress: address,\n resourceType: '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>'\n });\n\n if (resource && resource.data) {\n const data = resource.data as any;\n return BigInt(data.coin?.value || 0);\n }\n\n return 0n;\n } catch (error) {\n console.error('Error getting native balance:', error);\n return 0n;\n }\n }\n\n /**\n * Get fungible asset (FA) or coin balance\n */\n async getTokenBalance(tokenAddress: string, ownerAddress: string): Promise<bigint> {\n try {\n // Try as Coin type first\n const coinType = tokenAddress.includes('::')\n ? tokenAddress\n : `${tokenAddress}::coin::Coin`;\n\n const resource = await this.client.getAccountResource({\n accountAddress: ownerAddress,\n resourceType: `0x1::coin::CoinStore<${coinType}>`\n });\n\n if (resource && resource.data) {\n const data = resource.data as any;\n return BigInt(data.coin?.value || 0);\n }\n\n return 0n;\n } catch (error) {\n // If Coin query fails, try Fungible Asset (FA) format\n try {\n // FA balances are stored differently\n // Would need to query the FA resource\n console.warn('FA balance query not fully implemented yet');\n return 0n;\n } catch (faError) {\n console.error('Error getting token balance:', error);\n return 0n;\n }\n }\n }\n\n // ========================================================================\n // Utility Methods\n // ========================================================================\n\n /**\n * Compute key hash from public key coordinates\n * Matches EVM keccak256(abi.encode(publicKeyX, publicKeyY))\n */\n private computeKeyHash(publicKeyX: bigint, publicKeyY: bigint): string {\n // Write as big-endian to match EVM encoding\n const xHex = publicKeyX.toString(16).padStart(64, '0');\n const yHex = publicKeyY.toString(16).padStart(64, '0');\n\n const xBytes = this.hexToBytes(xHex);\n const yBytes = this.hexToBytes(yHex);\n\n // Use SHA3-256 for Aptos (which is what Aptos uses natively)\n // For cross-chain compatibility, this should match the EVM hash\n const combined = new Uint8Array([...xBytes, ...yBytes]);\n const hash = sha3_256(combined);\n\n return '0x' + hash;\n }\n\n /**\n * Build message for signing (matches Hub chain format)\n */\n private buildMessage(\n keyHash: string,\n targetChain: number,\n actionPayload: string,\n nonce: bigint\n ): string {\n const keyHashBytes = this.hexToBytes(keyHash.replace('0x', ''));\n const targetChainBytes = new Uint8Array(2);\n targetChainBytes[0] = (targetChain >> 8) & 0xFF;\n targetChainBytes[1] = targetChain & 0xFF;\n const payloadBytes = this.hexToBytes(actionPayload.replace('0x', ''));\n const nonceHex = nonce.toString(16).padStart(64, '0');\n const nonceBytes = this.hexToBytes(nonceHex);\n\n const combined = new Uint8Array([\n ...keyHashBytes,\n ...targetChainBytes,\n ...payloadBytes,\n ...nonceBytes,\n ]);\n\n const hash = sha3_256(combined);\n return '0x' + hash;\n }\n\n /**\n * Get Aptos client instance for advanced usage\n */\n getClient(): Aptos {\n return this.client;\n }\n\n /**\n * Get module address\n */\n getModuleAddress(): string {\n return this.moduleAddress;\n }\n\n /**\n * Get current ledger version\n */\n async getLedgerVersion(): Promise<bigint> {\n const ledgerInfo = await this.client.getLedgerInfo();\n return BigInt(ledgerInfo.ledger_version);\n }\n\n /**\n * Get transaction by hash\n */\n async getTransaction(txHash: string): Promise<any> {\n return await this.client.getTransactionByHash({ transactionHash: txHash });\n }\n\n /**\n * Wait for transaction confirmation\n */\n async waitForTransaction(txHash: string, timeoutSecs: number = 30): Promise<any> {\n return await this.client.waitForTransaction({\n transactionHash: txHash,\n options: {\n timeoutSecs,\n checkSuccess: true,\n }\n });\n }\n\n // ============================================================================\n // Social Recovery Methods (Issue #23)\n // ============================================================================\n // \n // Note: Social recovery is managed on the Hub chain (EVM).\n // Aptos spokes receive and execute recovery VAAs broadcast from the Hub.\n // The relayer service handles submitting recovery transactions to Aptos.\n //\n // SDK users should use EVMClient methods for guardian management and\n // recovery initiation on the Hub chain.\n // ============================================================================\n\n /**\n * Get vault resource for an owner\n * \n * @param ownerKeyHash - Owner's passkey hash (32 bytes as hex)\n * @returns Vault resource data or null if not found\n */\n async getVaultResource(ownerKeyHash: string): Promise<{\n ownerKeyHash: string;\n authorizedSigners: string[];\n nonce: bigint;\n } | null> {\n try {\n const vaultAddress = this.computeVaultAddressFromHash(ownerKeyHash);\n\n const resource = await this.client.getAccountResource({\n accountAddress: vaultAddress,\n resourceType: `${this.moduleAddress}::vault::Vault`\n });\n\n if (!resource || !resource.data) {\n return null;\n }\n\n const data = resource.data as any;\n return {\n ownerKeyHash: data.owner_key_hash || ownerKeyHash,\n authorizedSigners: data.authorized_signers || [],\n nonce: BigInt(data.nonce || 0),\n };\n } catch (error) {\n console.error('Error getting vault resource:', error);\n return null;\n }\n }\n\n /**\n * Get authorized signers for a vault\n * \n * @param ownerKeyHash - Owner's passkey hash (32 bytes as hex)\n * @returns Array of authorized signer key hashes\n */\n async getAuthorizedSigners(ownerKeyHash: string): Promise<string[]> {\n const vaultResource = await this.getVaultResource(ownerKeyHash);\n return vaultResource?.authorizedSigners || [];\n }\n\n /**\n * Check if a VAA has been processed (for replay protection)\n * \n * @param vaaHash - VAA hash as hex string\n * @returns Whether the VAA has been processed\n */\n async isVaaProcessed(vaaHash: string): Promise<boolean> {\n try {\n const resource = await this.client.getAccountResource({\n accountAddress: this.moduleAddress,\n resourceType: `${this.moduleAddress}::spoke::ProcessedVAAs`\n });\n\n if (!resource || !resource.data) {\n return false;\n }\n\n const data = resource.data as any;\n const processedVaas = data.processed || [];\n\n // Check if vaaHash is in the processed list\n const normalizedHash = vaaHash.toLowerCase().replace('0x', '');\n return processedVaas.some((hash: string) => \n hash.toLowerCase().replace('0x', '') === normalizedHash\n );\n } catch (error) {\n console.error('Error checking VAA status:', error);\n return false;\n }\n }\n\n /**\n * Check if protocol is paused\n * \n * @returns Whether the protocol is paused\n */\n async isProtocolPaused(): Promise<boolean> {\n try {\n const resource = await this.client.getAccountResource({\n accountAddress: this.moduleAddress,\n resourceType: `${this.moduleAddress}::spoke::Config`\n });\n\n if (!resource || !resource.data) {\n return false;\n }\n\n const data = resource.data as any;\n return data.paused === true;\n } catch (error) {\n console.error('Error checking pause status:', error);\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
|