@veridex/sdk 1.0.0-beta.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/LICENSE +170 -21
  2. package/README.md +574 -117
  3. package/dist/EVMClient-DtqvdfUP.d.mts +376 -0
  4. package/dist/auth/prepareAuth.d.mts +25 -0
  5. package/dist/auth/prepareAuth.js +2406 -0
  6. package/dist/auth/prepareAuth.js.map +1 -0
  7. package/dist/auth/prepareAuth.mjs +151 -0
  8. package/dist/auth/prepareAuth.mjs.map +1 -0
  9. package/dist/chains/aptos/index.d.mts +6 -5
  10. package/dist/chains/aptos/index.js +66 -39
  11. package/dist/chains/aptos/index.js.map +1 -1
  12. package/dist/chains/aptos/index.mjs +5 -547
  13. package/dist/chains/aptos/index.mjs.map +1 -1
  14. package/dist/chains/avalanche/index.d.mts +137 -0
  15. package/dist/chains/avalanche/index.js +1555 -0
  16. package/dist/chains/avalanche/index.js.map +1 -0
  17. package/dist/chains/avalanche/index.mjs +10 -0
  18. package/dist/chains/avalanche/index.mjs.map +1 -0
  19. package/dist/chains/evm/index.d.mts +5 -3
  20. package/dist/chains/evm/index.js +165 -3
  21. package/dist/chains/evm/index.js.map +1 -1
  22. package/dist/chains/evm/index.mjs +8 -1200
  23. package/dist/chains/evm/index.mjs.map +1 -1
  24. package/dist/chains/solana/index.d.mts +1 -1
  25. package/dist/chains/solana/index.js.map +1 -1
  26. package/dist/chains/solana/index.mjs +4 -486
  27. package/dist/chains/solana/index.mjs.map +1 -1
  28. package/dist/chains/stacks/index.d.mts +559 -0
  29. package/dist/chains/stacks/index.js +1207 -0
  30. package/dist/chains/stacks/index.js.map +1 -0
  31. package/dist/chains/stacks/index.mjs +71 -0
  32. package/dist/chains/stacks/index.mjs.map +1 -0
  33. package/dist/chains/starknet/index.d.mts +3 -3
  34. package/dist/chains/starknet/index.js.map +1 -1
  35. package/dist/chains/starknet/index.mjs +5 -503
  36. package/dist/chains/starknet/index.mjs.map +1 -1
  37. package/dist/chains/sui/index.d.mts +2 -2
  38. package/dist/chains/sui/index.js.map +1 -1
  39. package/dist/chains/sui/index.mjs +5 -529
  40. package/dist/chains/sui/index.mjs.map +1 -1
  41. package/dist/chunk-5T6KPH7A.mjs +1082 -0
  42. package/dist/chunk-5T6KPH7A.mjs.map +1 -0
  43. package/dist/chunk-72ZA3OYQ.mjs +20 -0
  44. package/dist/chunk-72ZA3OYQ.mjs.map +1 -0
  45. package/dist/chunk-EFIURACP.mjs +438 -0
  46. package/dist/chunk-EFIURACP.mjs.map +1 -0
  47. package/dist/chunk-F3YAGZSW.mjs +269 -0
  48. package/dist/chunk-F3YAGZSW.mjs.map +1 -0
  49. package/dist/chunk-GWJRKDSA.mjs +549 -0
  50. package/dist/chunk-GWJRKDSA.mjs.map +1 -0
  51. package/dist/chunk-M3MM4YMF.mjs +417 -0
  52. package/dist/chunk-M3MM4YMF.mjs.map +1 -0
  53. package/dist/chunk-N4A2RMUN.mjs +216 -0
  54. package/dist/chunk-N4A2RMUN.mjs.map +1 -0
  55. package/dist/chunk-NUWSMJFJ.mjs +179 -0
  56. package/dist/chunk-NUWSMJFJ.mjs.map +1 -0
  57. package/dist/chunk-OVMMTL6H.mjs +330 -0
  58. package/dist/chunk-OVMMTL6H.mjs.map +1 -0
  59. package/dist/chunk-PDHZ5X5O.mjs +565 -0
  60. package/dist/chunk-PDHZ5X5O.mjs.map +1 -0
  61. package/dist/chunk-PRHNGA4G.mjs +464 -0
  62. package/dist/chunk-PRHNGA4G.mjs.map +1 -0
  63. package/dist/chunk-Q5O3M5LP.mjs +422 -0
  64. package/dist/chunk-Q5O3M5LP.mjs.map +1 -0
  65. package/dist/chunk-QDO6NQ7P.mjs +840 -0
  66. package/dist/chunk-QDO6NQ7P.mjs.map +1 -0
  67. package/dist/chunk-QT4ZZ4GM.mjs +509 -0
  68. package/dist/chunk-QT4ZZ4GM.mjs.map +1 -0
  69. package/dist/chunk-USDA5JTN.mjs +1249 -0
  70. package/dist/chunk-USDA5JTN.mjs.map +1 -0
  71. package/dist/chunk-V636MIV3.mjs +52 -0
  72. package/dist/chunk-V636MIV3.mjs.map +1 -0
  73. package/dist/chunk-X7BZMSPQ.mjs +407 -0
  74. package/dist/chunk-X7BZMSPQ.mjs.map +1 -0
  75. package/dist/chunk-YCUJZ6Z7.mjs +829 -0
  76. package/dist/chunk-YCUJZ6Z7.mjs.map +1 -0
  77. package/dist/constants.d.mts +1 -1
  78. package/dist/constants.js +26 -12
  79. package/dist/constants.js.map +1 -1
  80. package/dist/constants.mjs +16 -375
  81. package/dist/constants.mjs.map +1 -1
  82. package/dist/index-DDalBhAm.d.mts +243 -0
  83. package/dist/index.d.mts +2508 -556
  84. package/dist/index.js +14576 -9628
  85. package/dist/index.js.map +1 -1
  86. package/dist/index.mjs +4108 -7840
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/passkey.d.mts +182 -0
  89. package/dist/passkey.js +914 -0
  90. package/dist/passkey.js.map +1 -0
  91. package/dist/passkey.mjs +15 -0
  92. package/dist/passkey.mjs.map +1 -0
  93. package/dist/payload.js.map +1 -1
  94. package/dist/payload.mjs +25 -244
  95. package/dist/payload.mjs.map +1 -1
  96. package/dist/portfolio-V347KZOL.mjs +13 -0
  97. package/dist/portfolio-V347KZOL.mjs.map +1 -0
  98. package/dist/queries/index.js +145 -12
  99. package/dist/queries/index.js.map +1 -1
  100. package/dist/queries/index.mjs +14 -1496
  101. package/dist/queries/index.mjs.map +1 -1
  102. package/dist/{types-FJL7j6gQ.d.ts → types-B7V5VNbO.d.mts} +6 -2
  103. package/dist/{types-ChIsqCiw.d.mts → types-DP2CQT8p.d.mts} +12 -1
  104. package/dist/types.d.mts +16 -0
  105. package/dist/types.js.map +1 -1
  106. package/dist/utils.js +25 -11
  107. package/dist/utils.js.map +1 -1
  108. package/dist/utils.mjs +19 -371
  109. package/dist/utils.mjs.map +1 -1
  110. package/dist/wormhole.js.map +1 -1
  111. package/dist/wormhole.mjs +25 -397
  112. package/dist/wormhole.mjs.map +1 -1
  113. package/package.json +28 -3
  114. package/scripts/patch-noble-curves.js +78 -0
  115. package/dist/chains/aptos/index.d.ts +0 -145
  116. package/dist/chains/evm/index.d.ts +0 -5
  117. package/dist/chains/solana/index.d.ts +0 -116
  118. package/dist/chains/starknet/index.d.ts +0 -172
  119. package/dist/chains/sui/index.d.ts +0 -182
  120. package/dist/constants.d.ts +0 -150
  121. package/dist/index-0NXfbk0z.d.ts +0 -637
  122. package/dist/index-D0dLVjTA.d.mts +0 -637
  123. package/dist/index.d.ts +0 -3123
  124. package/dist/payload.d.ts +0 -125
  125. package/dist/queries/index.d.ts +0 -148
  126. package/dist/types-ChIsqCiw.d.ts +0 -565
  127. package/dist/types-FJL7j6gQ.d.mts +0 -172
  128. package/dist/types.d.ts +0 -407
  129. package/dist/utils.d.ts +0 -81
  130. package/dist/wormhole.d.ts +0 -167
@@ -0,0 +1 @@
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":[]}
@@ -0,0 +1,20 @@
1
+ // src/queries/constants.ts
2
+ var WORMHOLE_QUERY_PROXY_URLS = {
3
+ mainnet: "https://query.wormhole.com/v1/query",
4
+ testnet: "https://testnet.query.wormhole.com/v1/query"
5
+ };
6
+ var WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND = 6;
7
+ var WORMHOLE_QUERY_CHAIN_IDS = {
8
+ ETHEREUM: 2,
9
+ POLYGON: 5,
10
+ ARBITRUM: 23,
11
+ OPTIMISM: 24,
12
+ BASE: 30
13
+ };
14
+
15
+ export {
16
+ WORMHOLE_QUERY_PROXY_URLS,
17
+ WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND,
18
+ WORMHOLE_QUERY_CHAIN_IDS
19
+ };
20
+ //# sourceMappingURL=chunk-72ZA3OYQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/queries/constants.ts"],"sourcesContent":["export const WORMHOLE_QUERY_PROXY_URLS = {\n mainnet: 'https://query.wormhole.com/v1/query',\n testnet: 'https://testnet.query.wormhole.com/v1/query',\n} as const;\n\n/**\n * Wormhole Query Proxy rate limit: 6 queries per second.\n * SDK callers should throttle requests accordingly.\n */\nexport const WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND = 6;\n\n/**\n * Convenience set of Wormhole chain IDs commonly supported by Queries.\n * This is not an exhaustive list of all Wormhole chains.\n */\nexport const WORMHOLE_QUERY_CHAIN_IDS = {\n ETHEREUM: 2,\n POLYGON: 5,\n ARBITRUM: 23,\n OPTIMISM: 24,\n BASE: 30,\n} as const;\n"],"mappings":";AAAO,IAAM,4BAA4B;AAAA,EACvC,SAAS;AAAA,EACT,SAAS;AACX;AAMO,IAAM,uCAAuC;AAM7C,IAAM,2BAA2B;AAAA,EACtC,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,MAAM;AACR;","names":[]}
@@ -0,0 +1,438 @@
1
+ import {
2
+ encodeBridgeAction,
3
+ encodeExecuteAction,
4
+ encodeTransferAction
5
+ } from "./chunk-F3YAGZSW.mjs";
6
+
7
+ // src/chains/starknet/StarknetClient.ts
8
+ import { createHash } from "crypto";
9
+ import { RpcProvider } from "starknet";
10
+ var FELT252_MAX = BigInt("0x0800000000000000000000000000000000000000000000000000000000000000") - 1n;
11
+ var U128_MAX = BigInt("0x100000000000000000000000000000000");
12
+ function toStarknetU256(keyHash) {
13
+ const cleanHash = keyHash.replace("0x", "").padStart(64, "0");
14
+ const value = BigInt("0x" + cleanHash);
15
+ const low = value % U128_MAX;
16
+ const high = value / U128_MAX;
17
+ return [
18
+ "0x" + low.toString(16),
19
+ "0x" + high.toString(16)
20
+ ];
21
+ }
22
+ var StarknetClient = class {
23
+ config;
24
+ provider;
25
+ hubRpcUrl;
26
+ hubContractAddress;
27
+ constructor(config) {
28
+ this.config = {
29
+ name: `Starknet ${config.network || "mainnet"}`,
30
+ chainId: 0,
31
+ wormholeChainId: config.wormholeChainId,
32
+ rpcUrl: config.rpcUrl,
33
+ explorerUrl: config.network === "sepolia" ? "https://sepolia.starkscan.co" : "https://starkscan.co",
34
+ isEvm: false,
35
+ contracts: {
36
+ hub: config.spokeContractAddress,
37
+ wormholeCoreBridge: config.bridgeContractAddress ?? ""
38
+ }
39
+ };
40
+ this.hubRpcUrl = config.hubRpcUrl;
41
+ this.hubContractAddress = config.hubContractAddress;
42
+ this.provider = new RpcProvider({ nodeUrl: config.rpcUrl });
43
+ }
44
+ getConfig() {
45
+ return this.config;
46
+ }
47
+ async getNonce(_userKeyHash) {
48
+ return 0n;
49
+ }
50
+ async getMessageFee() {
51
+ return 0n;
52
+ }
53
+ async buildTransferPayload(params) {
54
+ return encodeTransferAction(params.token, params.recipient, params.amount);
55
+ }
56
+ async buildExecutePayload(params) {
57
+ return encodeExecuteAction(params.target, params.value, params.data);
58
+ }
59
+ async buildBridgePayload(params) {
60
+ return encodeBridgeAction(params.token, params.amount, params.destinationChain, params.recipient);
61
+ }
62
+ async dispatch(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, signer) {
63
+ void signature;
64
+ void publicKeyX;
65
+ void publicKeyY;
66
+ void targetChain;
67
+ void actionPayload;
68
+ void nonce;
69
+ void signer;
70
+ throw new Error(
71
+ "Direct dispatch not supported on Starknet. Starknet actions are executed via the Veridex Hub (Base Sepolia) + custom bridge. Use dispatchGasless() to route through relayer, which will submit attestations to the bridge."
72
+ );
73
+ }
74
+ async dispatchGasless(signature, publicKeyX, publicKeyY, targetChain, actionPayload, nonce, relayerUrl) {
75
+ const keyHash = this.computeKeyHash(publicKeyX, publicKeyY);
76
+ const request = {
77
+ signature: {
78
+ r: "0x" + signature.r.toString(16).padStart(64, "0"),
79
+ s: "0x" + signature.s.toString(16).padStart(64, "0"),
80
+ authenticatorData: signature.authenticatorData,
81
+ clientDataJSON: signature.clientDataJSON,
82
+ challengeIndex: signature.challengeIndex,
83
+ typeIndex: signature.typeIndex
84
+ },
85
+ publicKeyX: "0x" + publicKeyX.toString(16).padStart(64, "0"),
86
+ publicKeyY: "0x" + publicKeyY.toString(16).padStart(64, "0"),
87
+ targetChain,
88
+ // 50001 for Starknet
89
+ actionPayload,
90
+ userNonce: Number(nonce)
91
+ };
92
+ const response = await fetch(`${relayerUrl}/api/v1/submit`, {
93
+ method: "POST",
94
+ headers: { "Content-Type": "application/json" },
95
+ body: JSON.stringify(request)
96
+ });
97
+ if (!response.ok) {
98
+ const errorText = await response.text().catch(() => "Unknown error");
99
+ throw new Error(
100
+ `Relayer submission failed: ${response.status} ${response.statusText}. Error: ${errorText}`
101
+ );
102
+ }
103
+ const result = await response.json();
104
+ return {
105
+ transactionHash: result.transactionHash ?? result.txHash ?? result.hubTxHash,
106
+ sequence: BigInt(result.sequence || 0),
107
+ userKeyHash: keyHash,
108
+ targetChain
109
+ };
110
+ }
111
+ // Note: getVaultAddress is now defined in the Social Recovery section below
112
+ // with enhanced spoke contract querying support.
113
+ computeVaultAddress(userKeyHash) {
114
+ const clean = userKeyHash.replace(/^0x/, "");
115
+ return "0x" + clean;
116
+ }
117
+ async vaultExists(userKeyHash) {
118
+ if (!this.config.contracts.hub) {
119
+ return false;
120
+ }
121
+ try {
122
+ const vaultAddress = await this.getVaultAddress(userKeyHash);
123
+ if (!vaultAddress) {
124
+ return false;
125
+ }
126
+ const anyProvider = this.provider;
127
+ if (typeof anyProvider.getClassHashAt === "function") {
128
+ await anyProvider.getClassHashAt(vaultAddress);
129
+ return true;
130
+ }
131
+ } catch {
132
+ }
133
+ return false;
134
+ }
135
+ async createVault(userKeyHash, signer) {
136
+ void signer;
137
+ throw new Error(
138
+ `Vault creation on Starknet must be done via Hub dispatch + custom bridge attestation. Use Hub client (Base Sepolia) to dispatch a CREATE_VAULT action with targetChain=50001. KeyHash=${userKeyHash}`
139
+ );
140
+ }
141
+ async createVaultSponsored(userKeyHash, sponsorPrivateKey, rpcUrl) {
142
+ void userKeyHash;
143
+ void sponsorPrivateKey;
144
+ void rpcUrl;
145
+ throw new Error(
146
+ "Vault creation on Starknet must be done via Hub dispatch + custom bridge attestation. Use Hub client (Base Sepolia) with sponsor key to dispatch CREATE_VAULT action."
147
+ );
148
+ }
149
+ /**
150
+ * Create a vault via the relayer (sponsored/gasless)
151
+ * This is the recommended way to create Starknet vaults
152
+ *
153
+ * The relayer will dispatch a vault creation action from Hub via custom bridge to Starknet spoke
154
+ */
155
+ async createVaultViaRelayer(userKeyHash, relayerUrl) {
156
+ const response = await fetch(`${relayerUrl}/api/v1/starknet/vault`, {
157
+ method: "POST",
158
+ headers: {
159
+ "Content-Type": "application/json"
160
+ },
161
+ body: JSON.stringify({
162
+ userKeyHash,
163
+ chainId: this.config.wormholeChainId
164
+ })
165
+ });
166
+ const result = await response.json();
167
+ if (!response.ok || !result.success) {
168
+ throw new Error(result.error || "Failed to create vault via relayer");
169
+ }
170
+ return {
171
+ address: result.vaultAddress,
172
+ transactionHash: result.transactionHash || "",
173
+ blockNumber: 0,
174
+ gasUsed: 0n,
175
+ alreadyExisted: result.alreadyExists || false,
176
+ sponsoredBy: "relayer"
177
+ };
178
+ }
179
+ /**
180
+ * Get vault info via relayer (includes existence check)
181
+ */
182
+ async getVaultViaRelayer(userKeyHash, relayerUrl) {
183
+ const response = await fetch(
184
+ `${relayerUrl}/api/v1/starknet/vault/${userKeyHash}?chainId=${this.config.wormholeChainId}`
185
+ );
186
+ if (!response.ok) {
187
+ throw new Error("Failed to get vault info from relayer");
188
+ }
189
+ const result = await response.json();
190
+ return {
191
+ vaultAddress: result.vaultAddress,
192
+ exists: result.exists
193
+ };
194
+ }
195
+ async estimateVaultCreationGas(_userKeyHash) {
196
+ return 0n;
197
+ }
198
+ getFactoryAddress() {
199
+ return void 0;
200
+ }
201
+ getImplementationAddress() {
202
+ return void 0;
203
+ }
204
+ // ========================================================================
205
+ // Balance utility (best-effort)
206
+ // ========================================================================
207
+ async getNativeBalance(address) {
208
+ try {
209
+ const anyProvider = this.provider;
210
+ if (typeof anyProvider.getBalance === "function") {
211
+ const res = await anyProvider.getBalance(address);
212
+ return BigInt(res);
213
+ }
214
+ } catch {
215
+ }
216
+ return 0n;
217
+ }
218
+ getProvider() {
219
+ return this.provider;
220
+ }
221
+ // ========================================================================
222
+ // Session Management (Issue #13)
223
+ // ========================================================================
224
+ /**
225
+ * Register a session key on the Hub (must be called via Hub client)
226
+ * Starknet spokes validate sessions via CCQ, but registration happens on Hub
227
+ *
228
+ * @throws Error - Session management must be done via Hub chain
229
+ */
230
+ async registerSession(_params) {
231
+ throw new Error(
232
+ "Session registration must be performed on the Hub chain (Base). Use EVMClient connected to the Hub to call registerSession()."
233
+ );
234
+ }
235
+ /**
236
+ * Revoke a session key on the Hub (must be called via Hub client)
237
+ *
238
+ * @throws Error - Session management must be done via Hub chain
239
+ */
240
+ async revokeSession(_params) {
241
+ throw new Error(
242
+ "Session revocation must be performed on the Hub chain (Base). Use EVMClient connected to the Hub to call revokeSession()."
243
+ );
244
+ }
245
+ /**
246
+ * Check if a session is active by querying the Hub
247
+ * This method queries the Hub contract directly for session validation
248
+ *
249
+ * @param userKeyHash - Hash of user's Passkey public key
250
+ * @param sessionKeyHash - Hash of session key to validate
251
+ * @returns Session validation result with expiry and limits
252
+ */
253
+ async isSessionActive(userKeyHash, sessionKeyHash) {
254
+ if (!this.hubRpcUrl || !this.hubContractAddress) {
255
+ throw new Error(
256
+ "Hub configuration required for session validation. Provide hubRpcUrl and hubContractAddress in StarknetClientConfig."
257
+ );
258
+ }
259
+ throw new Error(
260
+ "isSessionActive requires Hub client integration. Use EVMClient.isSessionActive() on the Hub chain, then pass the result to session execution on Starknet."
261
+ );
262
+ }
263
+ /**
264
+ * Get all sessions for a user from the Hub
265
+ *
266
+ * @param userKeyHash - Hash of user's Passkey public key
267
+ * @returns Array of all sessions (active and expired/revoked)
268
+ */
269
+ async getUserSessions(userKeyHash) {
270
+ if (!this.hubRpcUrl || !this.hubContractAddress) {
271
+ throw new Error(
272
+ "Hub configuration required for session queries. Provide hubRpcUrl and hubContractAddress in StarknetClientConfig."
273
+ );
274
+ }
275
+ throw new Error(
276
+ `getUserSessions requires Hub client integration. Use EVMClient.getUserSessions() on the Hub chain. User: ${userKeyHash}`
277
+ );
278
+ }
279
+ // ========================================================================
280
+ // Query-Based Execution (Issue #9/#10)
281
+ // ========================================================================
282
+ /**
283
+ * Get user state from Hub (comprehensive state query)
284
+ * Returns key hash, nonce, and last action hash for CCQ validation
285
+ *
286
+ * @param userKeyHash - Hash of user's Passkey public key
287
+ * @returns User state with nonce and last action hash
288
+ */
289
+ async getUserState(userKeyHash) {
290
+ if (!this.hubRpcUrl || !this.hubContractAddress) {
291
+ throw new Error(
292
+ "Hub configuration required for state queries. Provide hubRpcUrl and hubContractAddress in StarknetClientConfig."
293
+ );
294
+ }
295
+ throw new Error(
296
+ `getUserState requires Hub client integration. Use EVMClient.getUserState() on the Hub chain. User: ${userKeyHash}`
297
+ );
298
+ }
299
+ /**
300
+ * Get user's last action hash from Hub
301
+ * Used for optimistic execution and nonce validation
302
+ *
303
+ * @param userKeyHash - Hash of user's Passkey public key
304
+ * @returns Last action hash (zero hash if no actions)
305
+ */
306
+ async getUserLastActionHash(userKeyHash) {
307
+ if (!this.hubRpcUrl || !this.hubContractAddress) {
308
+ throw new Error(
309
+ "Hub configuration required for action hash queries. Provide hubRpcUrl and hubContractAddress in StarknetClientConfig."
310
+ );
311
+ }
312
+ throw new Error(
313
+ `getUserLastActionHash requires Hub client integration. Use EVMClient.getUserLastActionHash() on the Hub chain. User: ${userKeyHash}`
314
+ );
315
+ }
316
+ /**
317
+ * Execute with query-based validation (faster than VAA, ~23s vs 60-90s)
318
+ * Uses Wormhole CCQ to validate Hub state, then executes on Starknet
319
+ *
320
+ * @param params Query execution parameters with CCQ response
321
+ * @returns Dispatch result with transaction hash
322
+ *
323
+ * @remarks
324
+ * Query-based execution flow:
325
+ * 1. Query Hub state via Wormhole CCQ
326
+ * 2. Validate Guardian signatures on query response
327
+ * 3. Execute on Starknet with validated state
328
+ * 4. Hub state must be < 60s stale (enforced by QueryVerifier)
329
+ */
330
+ async executeWithQuery(_params) {
331
+ throw new Error(
332
+ "Query-based execution on Starknet requires relayer integration. Use relayer API to submit query-validated transactions. Relayer will call veridex_spoke::execute_with_query on Starknet."
333
+ );
334
+ }
335
+ // ========================================================================
336
+ // Internal helpers
337
+ // ========================================================================
338
+ computeKeyHash(publicKeyX, publicKeyY) {
339
+ const xHex = publicKeyX.toString(16).padStart(64, "0");
340
+ const yHex = publicKeyY.toString(16).padStart(64, "0");
341
+ const combined = Buffer.from(xHex + yHex, "hex");
342
+ const hash = createHash("sha256").update(combined).digest("hex");
343
+ return "0x" + hash;
344
+ }
345
+ buildMessageHash(keyHash, targetChain, actionPayload, nonce) {
346
+ const keyHashBuffer = Buffer.from(keyHash.replace(/^0x/, ""), "hex");
347
+ const targetChainBuffer = Buffer.alloc(2);
348
+ targetChainBuffer.writeUInt16BE(targetChain);
349
+ const payloadBuffer = Buffer.from(actionPayload.replace(/^0x/, ""), "hex");
350
+ const nonceHex = nonce.toString(16).padStart(64, "0");
351
+ const nonceBuffer = Buffer.from(nonceHex, "hex");
352
+ const combined = Buffer.concat([keyHashBuffer, targetChainBuffer, payloadBuffer, nonceBuffer]);
353
+ const hash = createHash("sha256").update(combined).digest("hex");
354
+ return "0x" + hash;
355
+ }
356
+ // ============================================================================
357
+ // Social Recovery Methods (Issue #23)
358
+ // ============================================================================
359
+ //
360
+ // Note: Social recovery is managed on the Hub chain (EVM).
361
+ // Starknet spokes receive and execute recovery VAAs broadcast from the Hub.
362
+ // The relayer service handles submitting recovery transactions to Starknet.
363
+ //
364
+ // SDK users should use EVMClient methods for guardian management and
365
+ // recovery initiation on the Hub chain.
366
+ // ============================================================================
367
+ /**
368
+ * Get vault address by owner key hash
369
+ *
370
+ * @param ownerKeyHash - Owner's passkey hash
371
+ * @returns Vault address on Starknet (felt252 as hex string)
372
+ */
373
+ async getVaultAddress(ownerKeyHash) {
374
+ try {
375
+ const spokeAddress = this.config.contracts.hub;
376
+ if (!spokeAddress) {
377
+ throw new Error("Spoke contract address not configured");
378
+ }
379
+ const [low, high] = toStarknetU256(ownerKeyHash);
380
+ const result = await this.provider.callContract({
381
+ contractAddress: spokeAddress,
382
+ entrypoint: "get_vault",
383
+ calldata: [low, high]
384
+ });
385
+ const vaultAddress = result[0];
386
+ if (vaultAddress === "0x0" || vaultAddress === "0") {
387
+ return null;
388
+ }
389
+ return vaultAddress;
390
+ } catch (error) {
391
+ console.error("Error getting vault address:", error);
392
+ return null;
393
+ }
394
+ }
395
+ /**
396
+ * Check if vault exists and get basic info
397
+ *
398
+ * @param ownerKeyHash - Owner's passkey hash
399
+ * @returns Vault info or null if not found
400
+ */
401
+ async getVaultInfo(ownerKeyHash) {
402
+ const vaultAddress = await this.getVaultAddress(ownerKeyHash);
403
+ if (!vaultAddress) {
404
+ return null;
405
+ }
406
+ return {
407
+ address: vaultAddress,
408
+ ownerKeyHash
409
+ };
410
+ }
411
+ /**
412
+ * Check if spoke contract is paused
413
+ *
414
+ * @returns Whether the protocol is paused
415
+ */
416
+ async isProtocolPaused() {
417
+ try {
418
+ const spokeAddress = this.config.contracts.hub;
419
+ if (!spokeAddress) {
420
+ throw new Error("Spoke contract address not configured");
421
+ }
422
+ const result = await this.provider.callContract({
423
+ contractAddress: spokeAddress,
424
+ entrypoint: "is_paused",
425
+ calldata: []
426
+ });
427
+ return result[0] === "0x1" || result[0] === "1";
428
+ } catch (error) {
429
+ console.error("Error checking pause status:", error);
430
+ return false;
431
+ }
432
+ }
433
+ };
434
+
435
+ export {
436
+ StarknetClient
437
+ };
438
+ //# sourceMappingURL=chunk-EFIURACP.mjs.map