@veridex/sdk 1.0.1 → 1.1.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 (87) hide show
  1. package/dist/auth/prepareAuth.js +107 -32
  2. package/dist/auth/prepareAuth.js.map +1 -1
  3. package/dist/auth/prepareAuth.mjs +6 -6
  4. package/dist/auth/prepareAuth.mjs.map +1 -1
  5. package/dist/chains/aptos/index.js.map +1 -1
  6. package/dist/chains/aptos/index.mjs +3 -3
  7. package/dist/chains/avalanche/index.js.map +1 -1
  8. package/dist/chains/avalanche/index.mjs +4 -4
  9. package/dist/chains/evm/index.d.mts +2 -2
  10. package/dist/chains/evm/index.js.map +1 -1
  11. package/dist/chains/evm/index.mjs +3 -3
  12. package/dist/chains/solana/index.js.map +1 -1
  13. package/dist/chains/solana/index.mjs +3 -3
  14. package/dist/chains/stacks/index.d.mts +1 -1
  15. package/dist/chains/stacks/index.js.map +1 -1
  16. package/dist/chains/stacks/index.mjs +3 -3
  17. package/dist/chains/starknet/index.d.mts +2 -3
  18. package/dist/chains/starknet/index.js +1 -13
  19. package/dist/chains/starknet/index.js.map +1 -1
  20. package/dist/chains/starknet/index.mjs +3 -3
  21. package/dist/chains/sui/index.d.mts +3 -3
  22. package/dist/chains/sui/index.js +2 -2
  23. package/dist/chains/sui/index.js.map +1 -1
  24. package/dist/chains/sui/index.mjs +3 -3
  25. package/dist/{chunk-Q5O3M5LP.mjs → chunk-2TS375ET.mjs} +2 -2
  26. package/dist/{chunk-QT4ZZ4GM.mjs → chunk-5FDOTI5G.mjs} +2 -2
  27. package/dist/{chunk-5T6KPH7A.mjs → chunk-CSU4IV2F.mjs} +2 -2
  28. package/dist/{chunk-YCUJZ6Z7.mjs → chunk-CTYDGO6E.mjs} +63 -6
  29. package/dist/chunk-CTYDGO6E.mjs.map +1 -0
  30. package/dist/{chunk-PRHNGA4G.mjs → chunk-E3SU36C2.mjs} +4 -4
  31. package/dist/{chunk-PRHNGA4G.mjs.map → chunk-E3SU36C2.mjs.map} +1 -1
  32. package/dist/{chunk-NUWSMJFJ.mjs → chunk-EFIXFA6V.mjs} +2 -2
  33. package/dist/{chunk-EFIURACP.mjs → chunk-GM5DKEHD.mjs} +3 -15
  34. package/dist/chunk-GM5DKEHD.mjs.map +1 -0
  35. package/dist/{chunk-GWJRKDSA.mjs → chunk-GOWXQPTW.mjs} +3 -3
  36. package/dist/{chunk-OVMMTL6H.mjs → chunk-ICGB3AHI.mjs} +2 -2
  37. package/dist/{chunk-QDO6NQ7P.mjs → chunk-M3GUNREX.mjs} +20 -3
  38. package/dist/{chunk-QDO6NQ7P.mjs.map → chunk-M3GUNREX.mjs.map} +1 -1
  39. package/dist/{chunk-N4A2RMUN.mjs → chunk-PEGOXMBU.mjs} +2 -2
  40. package/dist/{chunk-X7BZMSPQ.mjs → chunk-RD6ZYUVG.mjs} +52 -30
  41. package/dist/chunk-RD6ZYUVG.mjs.map +1 -0
  42. package/dist/{chunk-F3YAGZSW.mjs → chunk-TPEP6XUA.mjs} +2 -2
  43. package/dist/{chunk-M3MM4YMF.mjs → chunk-UPO55SBK.mjs} +2 -2
  44. package/dist/{chunk-USDA5JTN.mjs → chunk-YBN2VC6E.mjs} +2 -2
  45. package/dist/{chunk-PDHZ5X5O.mjs → chunk-YYT3V7CI.mjs} +2 -2
  46. package/dist/constants.d.mts +2 -2
  47. package/dist/constants.js +51 -29
  48. package/dist/constants.js.map +1 -1
  49. package/dist/constants.mjs +1 -1
  50. package/dist/{index-DDalBhAm.d.mts → index-CySMITQ9.d.mts} +9 -6
  51. package/dist/index.d.mts +44 -13
  52. package/dist/index.js +466 -162
  53. package/dist/index.js.map +1 -1
  54. package/dist/index.mjs +345 -127
  55. package/dist/index.mjs.map +1 -1
  56. package/dist/passkey.js +57 -4
  57. package/dist/passkey.js.map +1 -1
  58. package/dist/passkey.mjs +3 -3
  59. package/dist/payload.js.map +1 -1
  60. package/dist/payload.mjs +2 -2
  61. package/dist/portfolio-JA4OTF7Y.mjs +13 -0
  62. package/dist/queries/index.js +49 -27
  63. package/dist/queries/index.js.map +1 -1
  64. package/dist/queries/index.mjs +5 -5
  65. package/dist/{types-B7V5VNbO.d.mts → types-DWx-5jmz.d.mts} +12 -3
  66. package/dist/utils.js +49 -27
  67. package/dist/utils.js.map +1 -1
  68. package/dist/utils.mjs +2 -2
  69. package/dist/wormhole.js.map +1 -1
  70. package/dist/wormhole.mjs +2 -2
  71. package/package.json +1 -1
  72. package/dist/chunk-EFIURACP.mjs.map +0 -1
  73. package/dist/chunk-X7BZMSPQ.mjs.map +0 -1
  74. package/dist/chunk-YCUJZ6Z7.mjs.map +0 -1
  75. package/dist/portfolio-V347KZOL.mjs +0 -13
  76. /package/dist/{chunk-Q5O3M5LP.mjs.map → chunk-2TS375ET.mjs.map} +0 -0
  77. /package/dist/{chunk-QT4ZZ4GM.mjs.map → chunk-5FDOTI5G.mjs.map} +0 -0
  78. /package/dist/{chunk-5T6KPH7A.mjs.map → chunk-CSU4IV2F.mjs.map} +0 -0
  79. /package/dist/{chunk-NUWSMJFJ.mjs.map → chunk-EFIXFA6V.mjs.map} +0 -0
  80. /package/dist/{chunk-GWJRKDSA.mjs.map → chunk-GOWXQPTW.mjs.map} +0 -0
  81. /package/dist/{chunk-OVMMTL6H.mjs.map → chunk-ICGB3AHI.mjs.map} +0 -0
  82. /package/dist/{chunk-N4A2RMUN.mjs.map → chunk-PEGOXMBU.mjs.map} +0 -0
  83. /package/dist/{chunk-F3YAGZSW.mjs.map → chunk-TPEP6XUA.mjs.map} +0 -0
  84. /package/dist/{chunk-M3MM4YMF.mjs.map → chunk-UPO55SBK.mjs.map} +0 -0
  85. /package/dist/{chunk-USDA5JTN.mjs.map → chunk-YBN2VC6E.mjs.map} +0 -0
  86. /package/dist/{chunk-PDHZ5X5O.mjs.map → chunk-YYT3V7CI.mjs.map} +0 -0
  87. /package/dist/{portfolio-V347KZOL.mjs.map → portfolio-JA4OTF7Y.mjs.map} +0 -0
@@ -0,0 +1 @@
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":[]}
@@ -2,7 +2,7 @@ import {
2
2
  encodeBridgeAction,
3
3
  encodeExecuteAction,
4
4
  encodeTransferAction
5
- } from "./chunk-F3YAGZSW.mjs";
5
+ } from "./chunk-TPEP6XUA.mjs";
6
6
 
7
7
  // src/chains/sui/SuiClient.ts
8
8
  import { SuiClient as MystenSuiClient } from "@mysten/sui/client";
@@ -341,7 +341,7 @@ var SuiClient = class {
341
341
  * @param registryObjectId - Shared VaultRegistry object ID
342
342
  * @returns Vault object ID or null if not found
343
343
  */
344
- async getVaultId(ownerKeyHash, registryObjectId) {
344
+ async getVaultId(_ownerKeyHash, registryObjectId) {
345
345
  try {
346
346
  const registryObject = await this.client.getObject({
347
347
  id: registryObjectId,
@@ -418,7 +418,7 @@ var SuiClient = class {
418
418
  * @param processedVaasObjectId - ProcessedVAAs shared object ID
419
419
  * @returns Whether the VAA has been processed
420
420
  */
421
- async isVaaProcessed(vaaHash, processedVaasObjectId) {
421
+ async isVaaProcessed(_vaaHash, processedVaasObjectId) {
422
422
  try {
423
423
  const processedObject = await this.client.getObject({
424
424
  id: processedVaasObjectId,
@@ -461,4 +461,4 @@ var SuiClient = class {
461
461
  export {
462
462
  SuiClient
463
463
  };
464
- //# sourceMappingURL=chunk-PRHNGA4G.mjs.map
464
+ //# sourceMappingURL=chunk-E3SU36C2.mjs.map
@@ -1 +1 @@
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":"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","names":[]}
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":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MAINNET_CHAINS,
3
3
  TESTNET_CHAINS
4
- } from "./chunk-X7BZMSPQ.mjs";
4
+ } from "./chunk-RD6ZYUVG.mjs";
5
5
 
6
6
  // src/utils.ts
7
7
  import { ethers } from "ethers";
@@ -176,4 +176,4 @@ export {
176
176
  isValidWormholeChainId,
177
177
  retryWithBackoff
178
178
  };
179
- //# sourceMappingURL=chunk-NUWSMJFJ.mjs.map
179
+ //# sourceMappingURL=chunk-EFIXFA6V.mjs.map
@@ -2,12 +2,11 @@ import {
2
2
  encodeBridgeAction,
3
3
  encodeExecuteAction,
4
4
  encodeTransferAction
5
- } from "./chunk-F3YAGZSW.mjs";
5
+ } from "./chunk-TPEP6XUA.mjs";
6
6
 
7
7
  // src/chains/starknet/StarknetClient.ts
8
8
  import { createHash } from "crypto";
9
9
  import { RpcProvider } from "starknet";
10
- var FELT252_MAX = BigInt("0x0800000000000000000000000000000000000000000000000000000000000000") - 1n;
11
10
  var U128_MAX = BigInt("0x100000000000000000000000000000000");
12
11
  function toStarknetU256(keyHash) {
13
12
  const cleanHash = keyHash.replace("0x", "").padStart(64, "0");
@@ -250,7 +249,7 @@ var StarknetClient = class {
250
249
  * @param sessionKeyHash - Hash of session key to validate
251
250
  * @returns Session validation result with expiry and limits
252
251
  */
253
- async isSessionActive(userKeyHash, sessionKeyHash) {
252
+ async isSessionActive(_userKeyHash, _sessionKeyHash) {
254
253
  if (!this.hubRpcUrl || !this.hubContractAddress) {
255
254
  throw new Error(
256
255
  "Hub configuration required for session validation. Provide hubRpcUrl and hubContractAddress in StarknetClientConfig."
@@ -342,17 +341,6 @@ var StarknetClient = class {
342
341
  const hash = createHash("sha256").update(combined).digest("hex");
343
342
  return "0x" + hash;
344
343
  }
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
344
  // ============================================================================
357
345
  // Social Recovery Methods (Issue #23)
358
346
  // ============================================================================
@@ -435,4 +423,4 @@ var StarknetClient = class {
435
423
  export {
436
424
  StarknetClient
437
425
  };
438
- //# sourceMappingURL=chunk-EFIURACP.mjs.map
426
+ //# sourceMappingURL=chunk-GM5DKEHD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chains/starknet/StarknetClient.ts"],"sourcesContent":["/**\n * Veridex Protocol SDK - Starknet Chain Client\n *\n * Production-grade implementation of ChainClient interface for Starknet.\n * Supports custom bridge attestation, gasless execution via Hub dispatch.\n *\n * Security:\n * - Native starknet::eth_signature::verify_eth_signature for validation\n * - Custom bridge with multi-relayer threshold attestations\n * - Replay protection via nonce verification on Hub\n * - Bridge validates source_chain == hub_chain_id (10004 = Base Sepolia)\n *\n * Architecture:\n * - Starknet actions MUST be dispatched via Hub (Base Sepolia)\n * - Hub publishes Wormhole message → relayer monitors → relayer submits attestation\n * - Bridge accumulates attestations → threshold reached → spoke executes\n * - Spoke validates source_chain == hubChainId (NOT targetChain)\n * \n * Custom Bridge:\n * - Bridge address: 0x700488242f8f03248b2311edddc394f0408a18c36181446eabd265067809c83\n * - Chain ID: 50001 (custom range 50000+, reserved for non-Wormhole chains)\n * - Hub Chain ID: 10004 (Base Sepolia - what bridge validates as source)\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 { createHash } from 'crypto';\nimport { RpcProvider } from 'starknet';\nimport { encodeTransferAction, encodeExecuteAction, encodeBridgeAction } from '../../payload.js';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n// 2^128 for splitting u256 into low/high\nconst U128_MAX = BigInt('0x100000000000000000000000000000000');\n\n/**\n * Convert a 256-bit keyHash to Starknet u256 format (low, high felt252 pair).\n * Starknet u256 is represented as two felt252 values: (low_128_bits, high_128_bits)\n * \n * @param keyHash - 256-bit hex string (with or without 0x prefix)\n * @returns Array of two hex strings [low, high] for Starknet calldata\n */\nfunction toStarknetU256(keyHash: string): [string, string] {\n const cleanHash = keyHash.replace('0x', '').padStart(64, '0');\n const value = BigInt('0x' + cleanHash);\n \n // Split into low 128 bits and high 128 bits\n const low = value % U128_MAX;\n const high = value / U128_MAX;\n \n return [\n '0x' + low.toString(16),\n '0x' + high.toString(16)\n ];\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StarknetClientConfig {\n wormholeChainId: number;\n rpcUrl: string;\n spokeContractAddress?: string;\n bridgeContractAddress?: string;\n network?: 'mainnet' | 'sepolia' | 'testnet';\n hubRpcUrl?: string; // Hub chain RPC for session management\n hubContractAddress?: string; // Hub contract for session management\n}\n\n// ============================================================================\n// StarknetClient\n// ============================================================================\n\nexport class StarknetClient implements ChainClient {\n private config: ChainConfig;\n private provider: RpcProvider;\n private hubRpcUrl?: string;\n private hubContractAddress?: string;\n\n constructor(config: StarknetClientConfig) {\n this.config = {\n name: `Starknet ${config.network || 'mainnet'}`,\n chainId: 0,\n wormholeChainId: config.wormholeChainId,\n rpcUrl: config.rpcUrl,\n explorerUrl: config.network === 'sepolia'\n ? 'https://sepolia.starkscan.co'\n : 'https://starkscan.co',\n isEvm: false,\n contracts: {\n hub: config.spokeContractAddress,\n wormholeCoreBridge: config.bridgeContractAddress ?? '',\n },\n };\n\n this.hubRpcUrl = config.hubRpcUrl;\n this.hubContractAddress = config.hubContractAddress;\n\n this.provider = new RpcProvider({ nodeUrl: config.rpcUrl });\n }\n\n getConfig(): ChainConfig {\n return this.config;\n }\n\n async getNonce(_userKeyHash: string): Promise<bigint> {\n return 0n;\n }\n\n async getMessageFee(): Promise<bigint> {\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 Starknet. ' +\n 'Starknet actions are executed via the Veridex Hub (Base Sepolia) + custom bridge. ' +\n 'Use dispatchGasless() to route through relayer, which will submit attestations to the bridge.'\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 /**\n * Starknet gasless execution flow:\n * 1. User signs action with passkey (on client)\n * 2. SDK submits to relayer with targetChain=50001 (Starknet)\n * 3. Relayer dispatches to Hub (Base Sepolia) with targetChain=50001\n * 4. Hub publishes Wormhole message\n * 5. Relayer monitors Hub Dispatch event\n * 6. Relayer submits attestation to Starknet Bridge\n * 7. Bridge accumulates attestations from multiple relayers\n * 8. When threshold reached, Bridge calls spoke.execute()\n * 9. Spoke validates source_chain == hubChainId (10004)\n * 10. Spoke executes action on user's vault\n * \n * Result: Completely gasless for user - relayer pays all fees\n */\n const keyHash = this.computeKeyHash(publicKeyX, publicKeyY);\n\n // Submit to relayer for Hub dispatch + bridge attestation\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 targetChain, // 50001 for Starknet\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();\n\n return {\n transactionHash: result.transactionHash ?? result.txHash ?? result.hubTxHash,\n sequence: BigInt(result.sequence || 0),\n userKeyHash: keyHash,\n targetChain,\n };\n }\n\n // Note: getVaultAddress is now defined in the Social Recovery section below\n // with enhanced spoke contract querying support.\n\n computeVaultAddress(userKeyHash: string): string {\n /**\n * Starknet vault derivation:\n * - Vaults are created via spoke contract\n * - Address is deterministic from userKeyHash\n * - Uses keyHash directly as vault identifier (felt252)\n * \n * Note: Actual vault address on Starknet may differ based on\n * spoke implementation. This is a best-effort derivation.\n */\n const clean = userKeyHash.replace(/^0x/, '');\n return '0x' + clean;\n }\n\n async vaultExists(userKeyHash: string): Promise<boolean> {\n /**\n * Check if vault exists on Starknet spoke\n * Best-effort: queries spoke contract if available\n */\n if (!this.config.contracts.hub) {\n return false;\n }\n\n try {\n const vaultAddress = await this.getVaultAddress(userKeyHash);\n if (!vaultAddress) {\n return false;\n }\n\n // Query Starknet RPC for contract code at vault address\n const anyProvider = this.provider as any;\n if (typeof anyProvider.getClassHashAt === 'function') {\n await anyProvider.getClassHashAt(vaultAddress);\n return true;\n }\n } catch {\n // Vault doesn't exist or RPC doesn't support query\n }\n\n return false;\n }\n\n async createVault(userKeyHash: string, signer: any): Promise<VaultCreationResult> {\n void signer;\n throw new Error(\n 'Vault creation on Starknet must be done via Hub dispatch + custom bridge attestation. ' +\n 'Use Hub client (Base Sepolia) to dispatch a CREATE_VAULT action with targetChain=50001. ' +\n `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 Starknet must be done via Hub dispatch + custom bridge attestation. ' +\n 'Use Hub client (Base Sepolia) with sponsor key to dispatch CREATE_VAULT action.'\n );\n }\n\n /**\n * Create a vault via the relayer (sponsored/gasless)\n * This is the recommended way to create Starknet vaults\n * \n * The relayer will dispatch a vault creation action from Hub via custom bridge to Starknet spoke\n */\n async createVaultViaRelayer(\n userKeyHash: string,\n relayerUrl: string\n ): Promise<VaultCreationResult> {\n const response = await fetch(`${relayerUrl}/api/v1/starknet/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/starknet/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 return 0n;\n }\n\n getFactoryAddress(): string | undefined {\n return undefined;\n }\n\n getImplementationAddress(): string | undefined {\n return undefined;\n }\n\n // ========================================================================\n // Balance utility (best-effort)\n // ========================================================================\n\n async getNativeBalance(address: string): Promise<bigint> {\n // Best-effort: some Starknet RPCs support getBalance, but it is not universal.\n try {\n const anyProvider = this.provider as any;\n if (typeof anyProvider.getBalance === 'function') {\n const res = await anyProvider.getBalance(address);\n return BigInt(res);\n }\n } catch {\n // ignore\n }\n return 0n;\n }\n\n getProvider(): RpcProvider {\n return this.provider;\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 * Starknet 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 StarknetClientConfig.'\n );\n }\n\n // Query Hub contract for session status\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 Starknet.'\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 StarknetClientConfig.'\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 StarknetClientConfig.'\n );\n }\n\n // Query Hub contract for user state\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 StarknetClientConfig.'\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 Starknet\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 Starknet 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 Starknet requires relayer integration. ' +\n 'Use relayer API to submit query-validated transactions. ' +\n 'Relayer will call veridex_spoke::execute_with_query on Starknet.'\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 // ============================================================================\n // Social Recovery Methods (Issue #23)\n // ============================================================================\n // \n // Note: Social recovery is managed on the Hub chain (EVM).\n // Starknet spokes receive and execute recovery VAAs broadcast from the Hub.\n // The relayer service handles submitting recovery transactions to Starknet.\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 address by owner key hash\n * \n * @param ownerKeyHash - Owner's passkey hash\n * @returns Vault address on Starknet (felt252 as hex string)\n */\n async getVaultAddress(ownerKeyHash: string): Promise<string | null> {\n try {\n const spokeAddress = this.config.contracts.hub;\n if (!spokeAddress) {\n throw new Error('Spoke contract address not configured');\n }\n\n // Starknet spoke expects u256 (low, high) format for keyHash\n const [low, high] = toStarknetU256(ownerKeyHash);\n\n // Call get_vault on spoke contract with u256 split into [low, high]\n const result = await this.provider.callContract({\n contractAddress: spokeAddress,\n entrypoint: 'get_vault',\n calldata: [low, high],\n });\n\n // result[0] is the vault address (0 if not found)\n const vaultAddress = result[0];\n if (vaultAddress === '0x0' || vaultAddress === '0') {\n return null;\n }\n\n return vaultAddress;\n } catch (error) {\n console.error('Error getting vault address:', error);\n return null;\n }\n }\n\n /**\n * Check if vault exists and get basic info\n * \n * @param ownerKeyHash - Owner's passkey hash \n * @returns Vault info or null if not found\n */\n async getVaultInfo(ownerKeyHash: string): Promise<{\n address: string;\n ownerKeyHash: string;\n } | null> {\n const vaultAddress = await this.getVaultAddress(ownerKeyHash);\n if (!vaultAddress) {\n return null;\n }\n\n return {\n address: vaultAddress,\n ownerKeyHash,\n };\n }\n\n /**\n * Check if spoke contract is paused\n * \n * @returns Whether the protocol is paused\n */\n async isProtocolPaused(): Promise<boolean> {\n try {\n const spokeAddress = this.config.contracts.hub;\n if (!spokeAddress) {\n throw new Error('Spoke contract address not configured');\n }\n\n const result = await this.provider.callContract({\n contractAddress: spokeAddress,\n entrypoint: 'is_paused',\n calldata: [],\n });\n\n return result[0] === '0x1' || result[0] === '1';\n } catch (error) {\n console.error('Error checking pause status:', error);\n return false;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -5,11 +5,11 @@ import {
5
5
  WalletManager,
6
6
  getAllTokens,
7
7
  isNativeToken
8
- } from "./chunk-PDHZ5X5O.mjs";
8
+ } from "./chunk-YYT3V7CI.mjs";
9
9
  import {
10
10
  MAINNET_CHAINS,
11
11
  TESTNET_CHAINS
12
- } from "./chunk-X7BZMSPQ.mjs";
12
+ } from "./chunk-RD6ZYUVG.mjs";
13
13
 
14
14
  // src/queries/portfolio.ts
15
15
  import axios from "axios";
@@ -546,4 +546,4 @@ export {
546
546
  QueryPortfolioError,
547
547
  queryPortfolio
548
548
  };
549
- //# sourceMappingURL=chunk-GWJRKDSA.mjs.map
549
+ //# sourceMappingURL=chunk-GOWXQPTW.mjs.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  MAINNET_CHAINS,
6
6
  TESTNET_CHAINS
7
- } from "./chunk-X7BZMSPQ.mjs";
7
+ } from "./chunk-RD6ZYUVG.mjs";
8
8
 
9
9
  // src/queries/hubState.ts
10
10
  import axios from "axios";
@@ -327,4 +327,4 @@ export {
327
327
  QueryHubStateError,
328
328
  queryHubState
329
329
  };
330
- //# sourceMappingURL=chunk-OVMMTL6H.mjs.map
330
+ //# sourceMappingURL=chunk-ICGB3AHI.mjs.map
@@ -679,7 +679,7 @@ var CHAIN_PRESETS = {
679
679
  chainId: 0,
680
680
  wormholeChainId: 50001,
681
681
  // Custom bridge (non-Wormhole)
682
- rpcUrl: "https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/tsOnfTBZDKMXcUA26OED-",
682
+ rpcUrl: "https://starknet-sepolia-rpc.publicnode.com",
683
683
  explorerUrl: "https://sepolia.starkscan.co",
684
684
  isEvm: false,
685
685
  contracts: {
@@ -778,6 +778,16 @@ var CHAIN_PRESETS = {
778
778
  }
779
779
  }
780
780
  };
781
+ var _rpcOverrides = {};
782
+ function configureDefaultRpcUrls(overrides) {
783
+ for (const key of Object.keys(_rpcOverrides)) {
784
+ delete _rpcOverrides[key];
785
+ }
786
+ Object.assign(_rpcOverrides, overrides);
787
+ }
788
+ function getRpcUrlOverride(chain, network) {
789
+ return _rpcOverrides[chain]?.[network];
790
+ }
781
791
  function getChainConfig(chain, network = "testnet") {
782
792
  const preset = CHAIN_PRESETS[chain];
783
793
  if (!preset) {
@@ -785,7 +795,12 @@ function getChainConfig(chain, network = "testnet") {
785
795
  `Unknown chain: "${chain}". Supported chains: ${Object.keys(CHAIN_PRESETS).join(", ")}`
786
796
  );
787
797
  }
788
- return preset[network];
798
+ const config = preset[network];
799
+ const rpcOverride = getRpcUrlOverride(chain, network);
800
+ if (rpcOverride) {
801
+ return { ...config, rpcUrl: rpcOverride };
802
+ }
803
+ return config;
789
804
  }
790
805
  function getChainPreset(chain) {
791
806
  const preset = CHAIN_PRESETS[chain];
@@ -829,6 +844,8 @@ export {
829
844
  getEffectivePrimaryHub,
830
845
  CHAIN_NAMES,
831
846
  CHAIN_PRESETS,
847
+ configureDefaultRpcUrls,
848
+ getRpcUrlOverride,
832
849
  getChainConfig,
833
850
  getChainPreset,
834
851
  getSupportedChains,
@@ -837,4 +854,4 @@ export {
837
854
  isHubChain,
838
855
  getDefaultHub
839
856
  };
840
- //# sourceMappingURL=chunk-QDO6NQ7P.mjs.map
857
+ //# sourceMappingURL=chunk-M3GUNREX.mjs.map