UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

1,641 lines (1,627 loc) 692 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/platform/shared/pgp-utils.ts function processPGPKeyOptions(options) { return { name: options?.name ?? "Vana User", email: options?.email ?? "user@vana.org", passphrase: options?.passphrase }; } function getPGPKeyGenParams(options) { const { name, email, passphrase } = processPGPKeyOptions(options); return { type: "rsa", rsaBits: 2048, userIDs: [{ name, email }], passphrase, config: STANDARD_PGP_CONFIG }; } var STANDARD_PGP_CONFIG; var init_pgp_utils = __esm({ "src/platform/shared/pgp-utils.ts"() { "use strict"; STANDARD_PGP_CONFIG = { preferredCompressionAlgorithm: 2, // zlib (openpgp.enums.compression.zlib) preferredSymmetricAlgorithm: 7 // aes256 (openpgp.enums.symmetric.aes256) }; } }); // src/platform/shared/error-utils.ts function wrapCryptoError(operation, error) { const message = error instanceof Error ? error.message : "Unknown error"; return new Error(`${operation} failed: ${message}`); } var init_error_utils = __esm({ "src/platform/shared/error-utils.ts"() { "use strict"; } }); // src/utils/lazy-import.ts function lazyImport(importFn) { let cached = null; return () => { cached ??= importFn().catch((err) => { cached = null; throw new Error("Failed to load module", { cause: err }); }); return cached; }; } var init_lazy_import = __esm({ "src/utils/lazy-import.ts"() { "use strict"; } }); // src/utils/crypto-utils.ts import { fromHex } from "viem"; function processWalletPublicKey(publicKey) { return typeof publicKey === "string" ? fromHex( publicKey.startsWith("0x") ? publicKey : `0x${publicKey}`, "bytes" ) : publicKey; } function processWalletPrivateKey(privateKey) { return typeof privateKey === "string" ? fromHex( privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`, "bytes" ) : privateKey; } function parseEncryptedDataBuffer(encryptedBuffer) { return { iv: encryptedBuffer.slice(0, 16), ephemPublicKey: encryptedBuffer.slice(16, 81), // 65 bytes for uncompressed public key ciphertext: encryptedBuffer.slice(81, -32), mac: encryptedBuffer.slice(-32) }; } var init_crypto_utils = __esm({ "src/utils/crypto-utils.ts"() { "use strict"; } }); // src/crypto/services/WalletKeyEncryptionService.ts import { stringToBytes, bytesToString, toHex as toHex2, fromHex as fromHex2, concat } from "viem"; var WalletKeyEncryptionService; var init_WalletKeyEncryptionService = __esm({ "src/crypto/services/WalletKeyEncryptionService.ts"() { "use strict"; init_crypto_utils(); WalletKeyEncryptionService = class { eciesProvider; constructor(config) { this.eciesProvider = config.eciesProvider; } /** * Encrypts data using a wallet's public key. * * @param data - The plaintext message to encrypt for the wallet owner. * @param publicKey - The recipient wallet's public key for encryption. * @returns A promise that resolves to the encrypted data as a hex string. * @throws {Error} When encryption fails due to invalid key format. * * @example * ```typescript * const encrypted = await processor.encryptWithWalletPublicKey( * "Secret message", * "0x04..." // 65-byte uncompressed public key * ); * console.log(`Encrypted: ${encrypted}`); * ``` */ async encryptWithWalletPublicKey(data, publicKey) { const publicKeyBytes = processWalletPublicKey(publicKey); const normalizedKey = this.eciesProvider.normalizeToUncompressed(publicKeyBytes); const dataBytes = stringToBytes(data); const encrypted = await this.eciesProvider.encrypt( normalizedKey, dataBytes ); const result = concat([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return toHex2(result).slice(2); } /** * Decrypts data using a wallet's private key. * * @param encryptedData - The hex-encoded encrypted data to decrypt. * @param privateKey - The wallet's private key for decryption. * @returns A promise that resolves to the decrypted plaintext message. * @throws {Error} When decryption fails due to invalid data or key format. * * @example * ```typescript * const decrypted = await processor.decryptWithWalletPrivateKey( * encryptedHexString, * "0x..." // 32-byte private key * ); * console.log(`Decrypted: ${decrypted}`); * ``` */ async decryptWithWalletPrivateKey(encryptedData, privateKey) { const privateKeyBytes = processWalletPrivateKey(privateKey); const prefixedHex = encryptedData.startsWith("0x") ? encryptedData : `0x${encryptedData}`; const encryptedBytes = fromHex2(prefixedHex, "bytes"); const encrypted = parseEncryptedDataBuffer(encryptedBytes); const decrypted = await this.eciesProvider.decrypt( privateKeyBytes, encrypted ); return bytesToString(decrypted); } /** * Encrypts a Uint8Array with a wallet public key * * @param data - Binary data to encrypt * @param publicKey - Public key as hex string or Uint8Array * @returns Encrypted data structure */ async encryptBinary(data, publicKey) { const publicKeyBytes = processWalletPublicKey(publicKey); const normalizedKey = this.eciesProvider.normalizeToUncompressed(publicKeyBytes); return this.eciesProvider.encrypt(normalizedKey, data); } /** * Decrypts to a Uint8Array with a wallet private key * * @param encrypted - Encrypted data structure * @param privateKey - Private key as hex string or Uint8Array * @returns Decrypted binary data */ async decryptBinary(encrypted, privateKey) { const privateKeyBytes = processWalletPrivateKey(privateKey); return this.eciesProvider.decrypt(privateKeyBytes, encrypted); } /** * Gets the underlying ECIES provider * * @returns The ECIES provider instance */ getECIESProvider() { return this.eciesProvider; } }; } }); // src/crypto/ecies/constants.ts var CURVE, CIPHER, KDF, MAC, FORMAT; var init_constants = __esm({ "src/crypto/ecies/constants.ts"() { "use strict"; CURVE = { /** The elliptic curve used (secp256k1 - same as Bitcoin/Ethereum) */ name: "secp256k1", /** Private key length in bytes */ PRIVATE_KEY_LENGTH: 32, /** Compressed public key length in bytes (0x02 or 0x03 prefix + 32 bytes) */ COMPRESSED_PUBLIC_KEY_LENGTH: 33, /** Uncompressed public key length in bytes (0x04 prefix + 64 bytes) */ UNCOMPRESSED_PUBLIC_KEY_LENGTH: 65, /** ECDH shared secret X coordinate length */ SHARED_SECRET_LENGTH: 32, /** Public key prefixes */ PREFIX: { /** Uncompressed public key prefix */ UNCOMPRESSED: 4, /** Compressed public key prefix for even Y */ COMPRESSED_EVEN: 2, /** Compressed public key prefix for odd Y */ COMPRESSED_ODD: 3 }, /** X coordinate starts at byte 1 (after prefix) */ X_COORDINATE_OFFSET: 1, /** X coordinate ends at byte 33 (1 + 32) */ X_COORDINATE_END: 33 }; CIPHER = { /** Cipher algorithm - must match eccrypto */ algorithm: "aes-256-cbc", /** AES key length in bytes */ KEY_LENGTH: 32, /** Initialization vector length in bytes */ IV_LENGTH: 16, /** Block size for AES */ BLOCK_SIZE: 16 }; KDF = { /** Hash algorithm for key derivation - must match eccrypto */ algorithm: "sha512", /** Output length of SHA-512 in bytes */ OUTPUT_LENGTH: 64, /** Encryption key slice (first 32 bytes of KDF output) */ ENCRYPTION_KEY_OFFSET: 0, ENCRYPTION_KEY_LENGTH: 32, /** MAC key slice (last 32 bytes of KDF output) */ MAC_KEY_OFFSET: 32, MAC_KEY_LENGTH: 32 }; MAC = { /** MAC algorithm - must match eccrypto */ algorithm: "sha256", /** HMAC-SHA256 output length in bytes */ LENGTH: 32 }; FORMAT = { /** Offsets for each component in serialized format */ IV_OFFSET: 0, IV_LENGTH: CIPHER.IV_LENGTH, /** Ephemeral public key (always uncompressed in eccrypto format) */ EPHEMERAL_KEY_OFFSET: CIPHER.IV_LENGTH, EPHEMERAL_KEY_LENGTH: CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH, /** Ciphertext starts after IV and ephemeral key */ CIPHERTEXT_OFFSET: CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH, /** MAC is always the last 32 bytes */ MAC_LENGTH: MAC.LENGTH, /** Minimum size of encrypted data (IV + ephemKey + MAC, no ciphertext) */ MIN_ENCRYPTED_LENGTH: CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH + MAC.LENGTH, /** * Helper to calculate total length of encrypted data * * @param ciphertextLength - Length of the ciphertext portion * @returns Total length including all components */ getTotalLength: (ciphertextLength) => CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH + ciphertextLength + MAC.LENGTH }; } }); // src/crypto/ecies/interface.ts import { fromHex as fromHex3, toHex as toHex3 } from "viem"; function isECIESEncrypted(obj) { if (!obj || typeof obj !== "object") return false; const enc = obj; const isUint8Array = (value) => { return value instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(value); }; return isUint8Array(enc.iv) && enc.iv.length === CIPHER.IV_LENGTH && isUint8Array(enc.ephemPublicKey) && (enc.ephemPublicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH || enc.ephemPublicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) && isUint8Array(enc.ciphertext) && enc.ciphertext.length > 0 && isUint8Array(enc.mac) && enc.mac.length === MAC.LENGTH; } function serializeECIES(encrypted) { const combined = new Uint8Array( encrypted.iv.length + encrypted.ephemPublicKey.length + encrypted.ciphertext.length + encrypted.mac.length ); let offset = 0; combined.set(encrypted.iv, offset); offset += encrypted.iv.length; combined.set(encrypted.ephemPublicKey, offset); offset += encrypted.ephemPublicKey.length; combined.set(encrypted.ciphertext, offset); offset += encrypted.ciphertext.length; combined.set(encrypted.mac, offset); return toHex3(combined).slice(2); } function deserializeECIES(hex) { const hexWithPrefix = hex.startsWith("0x") ? hex : `0x${hex}`; const bytes = fromHex3(hexWithPrefix, "bytes"); const absoluteMinLength = FORMAT.IV_LENGTH + 1 + MAC.LENGTH + 1; if (bytes.length < absoluteMinLength) { throw new ECIESError( `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${absoluteMinLength} bytes required)`, "DECRYPTION_FAILED" ); } const prefix = bytes[FORMAT.EPHEMERAL_KEY_OFFSET]; if (prefix !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( `Invalid ephemeral public key: must be uncompressed format (0x04 prefix), got 0x${prefix.toString(16).padStart(2, "0")}`, "DECRYPTION_FAILED" ); } const ephemKeySize = CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH; const minLength = FORMAT.IV_LENGTH + ephemKeySize + MAC.LENGTH + 1; if (bytes.length < minLength) { throw new ECIESError( `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${minLength} bytes required)`, "DECRYPTION_FAILED" ); } return { iv: bytes.subarray(FORMAT.IV_OFFSET, FORMAT.IV_OFFSET + FORMAT.IV_LENGTH), ephemPublicKey: bytes.subarray( FORMAT.EPHEMERAL_KEY_OFFSET, FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize ), ciphertext: bytes.subarray( FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize, bytes.length - MAC.LENGTH ), mac: bytes.subarray(bytes.length - MAC.LENGTH) }; } var ECIESError; var init_interface = __esm({ "src/crypto/ecies/interface.ts"() { "use strict"; init_constants(); ECIESError = class extends Error { constructor(message, code, cause) { super(message); this.code = code; this.cause = cause; this.name = "ECIESError"; } code; cause; }; } }); // src/crypto/ecies/utils.ts function constantTimeEqual(a, b) { if (a.length !== b.length) return false; let result = 0; for (let i = 0; i < a.length; i++) { result |= a[i] ^ b[i]; } return result === 0; } var init_utils = __esm({ "src/crypto/ecies/utils.ts"() { "use strict"; } }); // src/crypto/ecies/base.ts import { concat as concat2 } from "viem"; var BaseECIESUint8; var init_base = __esm({ "src/crypto/ecies/base.ts"() { "use strict"; init_interface(); init_constants(); init_utils(); BaseECIESUint8 = class _BaseECIESUint8 { // Cache for validated public keys to avoid repeated validation static validatedKeys = /* @__PURE__ */ new WeakMap(); /** * Normalizes a public key to uncompressed format. * * @param publicKey - Public key in any format. * @returns Uncompressed public key (65 bytes). * @throws {ECIESError} If key format is invalid. */ normalizePublicKey(publicKey) { if (_BaseECIESUint8.validatedKeys.get(publicKey)) { return publicKey; } if (publicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( "Invalid uncompressed public key prefix", "INVALID_KEY" ); } if (!this.validatePublicKey(publicKey)) { throw new ECIESError("Invalid public key", "INVALID_KEY"); } _BaseECIESUint8.validatedKeys.set(publicKey, true); return publicKey; } if (publicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] === CURVE.PREFIX.COMPRESSED_EVEN || publicKey[0] === CURVE.PREFIX.COMPRESSED_ODD) { const decompressed = this.decompressPublicKey(publicKey); if (!decompressed) { throw new ECIESError( "Failed to decompress public key", "INVALID_KEY" ); } _BaseECIESUint8.validatedKeys.set(decompressed, true); return decompressed; } throw new ECIESError( `Invalid compressed public key prefix: expected 0x02 or 0x03, got 0x${publicKey[0].toString(16).padStart(2, "0")}`, "INVALID_KEY" ); } throw new ECIESError( `Invalid public key length: ${publicKey.length}`, "INVALID_KEY" ); } /** * Encrypts data using ECIES. * * @param publicKey - The recipient's public key (compressed or uncompressed) * @param message - The data to encrypt * @returns Promise resolving to encrypted data structure */ async encrypt(publicKey, message) { let ephemeralPrivateKey; let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(publicKey instanceof Uint8Array)) { throw new ECIESError("Public key must be a Uint8Array", "INVALID_KEY"); } if (!(message instanceof Uint8Array)) { throw new ECIESError( "Message must be a Uint8Array", "ENCRYPTION_FAILED" ); } if (publicKey.length === 0) { throw new ECIESError("Public key cannot be empty", "INVALID_KEY"); } const pubKey = this.normalizePublicKey(publicKey); do { ephemeralPrivateKey = this.generateRandomBytes( CURVE.PRIVATE_KEY_LENGTH ); } while (!this.verifyPrivateKey(ephemeralPrivateKey)); const ephemeralPublicKey = this.createPublicKey( ephemeralPrivateKey, false ); if (!ephemeralPublicKey) { throw new ECIESError( "Failed to generate ephemeral public key", "ENCRYPTION_FAILED" ); } sharedSecret = this.performECDH(pubKey, ephemeralPrivateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( KDF.ENCRYPTION_KEY_OFFSET, KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( KDF.MAC_KEY_OFFSET, KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH ); const iv = this.generateRandomBytes(CIPHER.IV_LENGTH); const ciphertext = await this.aesEncrypt(encryptionKey, iv, message); const macData = concat2([iv, ephemeralPublicKey, ciphertext]); const mac = this.hmacSha256(macKey, macData); return { iv, ephemPublicKey: ephemeralPublicKey, ciphertext, mac }; } catch (error) { if (error instanceof ECIESError) throw error; throw new ECIESError( `Encryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "ENCRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (ephemeralPrivateKey) this.clearBuffer(ephemeralPrivateKey); if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Decrypts ECIES encrypted data. * * @param privateKey - The recipient's private key (32 bytes) * @param encrypted - The encrypted data structure from encrypt() * @returns Promise resolving to the original plaintext */ async decrypt(privateKey, encrypted) { let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(privateKey instanceof Uint8Array)) { throw new ECIESError("Private key must be a Uint8Array", "INVALID_KEY"); } if (!isECIESEncrypted(encrypted)) { throw new ECIESError( "Invalid encrypted data structure", "DECRYPTION_FAILED" ); } if (privateKey.length !== CURVE.PRIVATE_KEY_LENGTH) { throw new ECIESError( `Invalid private key length: ${privateKey.length}`, "INVALID_KEY" ); } if (!this.verifyPrivateKey(privateKey)) { throw new ECIESError("Invalid private key", "INVALID_KEY"); } if (encrypted.ephemPublicKey.length !== CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { throw new ECIESError( `Invalid ephemeral public key: expected ${CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH} bytes (uncompressed), got ${encrypted.ephemPublicKey.length} bytes`, "INVALID_KEY" ); } if (encrypted.ephemPublicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) { throw new ECIESError( "Invalid ephemeral public key: must be uncompressed format with 0x04 prefix (eccrypto standard)", "INVALID_KEY" ); } if (!this.validatePublicKey(encrypted.ephemPublicKey)) { throw new ECIESError("Invalid ephemeral public key", "INVALID_KEY"); } const ephemeralPublicKey = encrypted.ephemPublicKey; sharedSecret = this.performECDH(ephemeralPublicKey, privateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( KDF.ENCRYPTION_KEY_OFFSET, KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( KDF.MAC_KEY_OFFSET, KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH ); const macData = concat2([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext ]); const expectedMac = this.hmacSha256(macKey, macData); if (!constantTimeEqual(encrypted.mac, expectedMac)) { throw new ECIESError("MAC verification failed", "MAC_MISMATCH"); } const decrypted = await this.aesDecrypt( encryptionKey, encrypted.iv, encrypted.ciphertext ); return decrypted; } catch (error) { if (error instanceof ECIESError) throw error; throw new ECIESError( `Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "DECRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Clears sensitive data from memory using multi-pass overwrite. * * @remarks * Uses multiple passes with different patterns to make it harder * for JIT compilers to optimize away the operation. While not * guaranteed in JavaScript, this is a best-effort approach to * clear sensitive data from memory. * * @param buffer - The buffer to clear */ clearBuffer(buffer) { if (buffer && buffer.length > 0) { buffer.fill(0); buffer.fill(255); buffer.fill(170); buffer.fill(0); for (let i = 0; i < buffer.length; i++) { buffer[i] = i & 255 ^ 90; } buffer.fill(0); } } }; } }); // src/crypto/ecies/browser.ts import * as secp256k12 from "@noble/secp256k1"; import { toHex as toHex5 } from "viem"; import { hmac as hmac2 } from "@noble/hashes/hmac"; import { sha256 as sha2563, sha512 as nobleSha512 } from "@noble/hashes/sha2"; var BrowserECIESUint8Provider; var init_browser = __esm({ "src/crypto/ecies/browser.ts"() { "use strict"; init_base(); BrowserECIESUint8Provider = class extends BaseECIESUint8 { generateRandomBytes(length) { const bytes = new Uint8Array(length); crypto.getRandomValues(bytes); return bytes; } verifyPrivateKey(privateKey) { try { return secp256k12.utils.isValidPrivateKey(privateKey); } catch { return false; } } createPublicKey(privateKey, compressed) { try { return secp256k12.getPublicKey(privateKey, compressed); } catch { return null; } } validatePublicKey(publicKey) { try { secp256k12.Point.fromHex(publicKey); return true; } catch { return false; } } decompressPublicKey(publicKey) { try { const point = secp256k12.Point.fromHex(publicKey); return point.toRawBytes(false); } catch { return null; } } performECDH(publicKey, privateKey) { try { const sharedPoint = secp256k12.getSharedSecret( privateKey, publicKey, true ); return sharedPoint.slice(1); } catch (error) { throw new Error( `ECDH failed: ${error instanceof Error ? error.message : "Unknown error"}` ); } } sha512(data) { return nobleSha512(data); } hmacSha256(key, data) { return hmac2(sha2563, key, data); } async aesEncrypt(key, iv, plaintext) { const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "AES-CBC" }, false, ["encrypt"] ); const encrypted = await crypto.subtle.encrypt( { name: "AES-CBC", iv }, cryptoKey, plaintext ); return new Uint8Array(encrypted); } async aesDecrypt(key, iv, ciphertext) { const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "AES-CBC" }, false, ["decrypt"] ); const decrypted = await crypto.subtle.decrypt( { name: "AES-CBC", iv }, cryptoKey, ciphertext ); return new Uint8Array(decrypted); } /** * Normalizes a public key to uncompressed format (65 bytes with 0x04 prefix). * Handles compressed (33 bytes) and uncompressed (65 bytes) formats only. * * @remarks * Strict policy: Does not accept 64-byte raw coordinates to avoid masking * malformed data. Callers must provide properly formatted keys. * * @param publicKey - The public key to normalize (33 or 65 bytes) * @returns The normalized uncompressed public key (65 bytes) * @throws {Error} When public key format is invalid or decompression fails */ normalizeToUncompressed(publicKey) { const len = publicKey.length; if (len === 65 && publicKey[0] === 4) { return publicKey; } if (len === 33 && (publicKey[0] === 2 || publicKey[0] === 3)) { const decompressed = this.decompressPublicKey(publicKey); if (!decompressed) { throw new Error( `Failed to decompress public key with prefix ${toHex5(publicKey[0])}` ); } return decompressed; } if (len === 64) { throw new Error( "Raw public key coordinates (64 bytes) are not accepted. Please provide a properly formatted compressed (33 bytes) or uncompressed (65 bytes) public key." ); } throw new Error( `Invalid public key format: expected compressed (33 bytes) or uncompressed (65 bytes), got ${len} bytes` ); } }; } }); // src/platform/browser.ts var browser_exports = {}; __export(browser_exports, { BrowserPlatformAdapter: () => BrowserPlatformAdapter }); import { toHex as toHex6, fromHex as fromHex4, stringToBytes as stringToBytes2, bytesToString as bytesToString2, concat as concat3 } from "viem"; import * as secp256k13 from "@noble/secp256k1"; var getOpenPGP2, BrowserCryptoAdapter, BrowserPGPAdapter, BrowserHttpAdapter, BrowserCacheAdapter, BrowserPlatformAdapter; var init_browser2 = __esm({ "src/platform/browser.ts"() { "use strict"; init_pgp_utils(); init_error_utils(); init_lazy_import(); init_WalletKeyEncryptionService(); init_crypto_utils(); init_browser(); getOpenPGP2 = lazyImport(() => import("openpgp")); BrowserCryptoAdapter = class { eciesProvider = new BrowserECIESUint8Provider(); walletService = new WalletKeyEncryptionService({ eciesProvider: this.eciesProvider }); /** * Encrypts data using ECIES with a public key. * * @param data - The plaintext string to encrypt. * Typically user data or sensitive information. * @param publicKeyHex - The recipient's public key in hex format. * Can include or omit the '0x' prefix. * @returns Encrypted data as a hex string without '0x' prefix * * @throws {Error} If encryption fails or public key is invalid */ async encryptWithPublicKey(data, publicKeyHex) { try { const prefixedHex = publicKeyHex.startsWith("0x") ? publicKeyHex : `0x${publicKeyHex}`; const publicKeyBytes = fromHex4(prefixedHex, "bytes"); const encrypted = await this.eciesProvider.encrypt( publicKeyBytes, stringToBytes2(data) ); const result = concat3([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext, encrypted.mac ]); return toHex6(result).slice(2); } catch (error) { throw wrapCryptoError("encryptWithPublicKey", error); } } /** * Decrypts ECIES-encrypted data using a private key. * * @param encryptedData - Hex string containing encrypted data. * Can include or omit the '0x' prefix. * @param privateKeyHex - The private key in hex format. * Must correspond to the public key used for encryption. * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or MAC verification fails */ async decryptWithPrivateKey(encryptedData, privateKeyHex) { try { const encryptedHex = encryptedData.startsWith("0x") ? encryptedData : `0x${encryptedData}`; const privateHex = privateKeyHex.startsWith("0x") ? privateKeyHex : `0x${privateKeyHex}`; const encryptedBytes = fromHex4(encryptedHex, "bytes"); const privateKeyBytes = fromHex4(privateHex, "bytes"); const encrypted = parseEncryptedDataBuffer(encryptedBytes); const decrypted = await this.eciesProvider.decrypt( privateKeyBytes, encrypted ); return bytesToString2(decrypted); } catch (error) { throw wrapCryptoError("decryptWithPrivateKey", error); } } /** * Encrypts data using a wallet's public key. * * @param data - The plaintext string to encrypt. * Typically permission data or DLP metadata. * @param publicKey - The wallet's public key. * Automatically handles compressed/uncompressed formats. * @returns Encrypted data as a hex string * * @throws {Error} If encryption fails or key processing fails */ async encryptWithWalletPublicKey(data, publicKey) { try { return await this.walletService.encryptWithWalletPublicKey( data, publicKey ); } catch (error) { throw wrapCryptoError("encryptWithWalletPublicKey", error); } } /** * Decrypts data using a wallet's private key. * * @param encryptedData - Hex string containing encrypted data. * Must be encrypted with corresponding wallet public key. * @param privateKey - The wallet's private key. * Obtain from wallet connection (handle with care). * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or key is invalid */ async decryptWithWalletPrivateKey(encryptedData, privateKey) { try { return await this.walletService.decryptWithWalletPrivateKey( encryptedData, privateKey ); } catch (error) { throw wrapCryptoError("decryptWithWalletPrivateKey", error); } } /** * Generates a new secp256k1 key pair for ECIES operations. * * @returns Object containing hex-encoded public and private keys * @returns returns.privateKey - Private key in hex format without '0x' * @returns returns.publicKey - Compressed public key in hex format without '0x' * * @throws {Error} If key generation fails */ async generateKeyPair() { try { const privateKeyBytes = secp256k13.utils.randomPrivateKey(); const publicKeyBytes = secp256k13.getPublicKey(privateKeyBytes, true); return { privateKey: toHex6(privateKeyBytes).slice(2), publicKey: toHex6(publicKeyBytes).slice(2) }; } catch (error) { throw wrapCryptoError("generateKeyPair", error); } } /** * Encrypts binary data using password-based encryption. * * @param data - Binary data to encrypt. * Typically file contents or serialized objects. * @param password - Password for encryption. * Often derived from wallet signatures. * @returns Encrypted data as Uint8Array * * @remarks * Uses OpenPGP for password-based encryption with automatic * salt generation for security. * * @throws {Error} If encryption fails */ async encryptWithPassword(data, password) { try { const openpgp2 = await getOpenPGP2(); const message = await openpgp2.createMessage({ binary: data }); const encrypted = await openpgp2.encrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(encrypted); } catch (error) { throw wrapCryptoError("encryptWithPassword", error); } } /** * Decrypts password-encrypted binary data. * * @param encryptedData - Password-encrypted data as Uint8Array. * Must be encrypted with the same password. * @param password - Password for decryption. * Must match the encryption password. * @returns Decrypted data as Uint8Array * * @throws {Error} If decryption fails or password is incorrect */ async decryptWithPassword(encryptedData, password) { try { const openpgp2 = await getOpenPGP2(); const message = await openpgp2.readMessage({ binaryMessage: encryptedData }); const { data } = await openpgp2.decrypt({ message, passwords: [password], format: "binary" }); return new Uint8Array(data); } catch (error) { throw wrapCryptoError("decryptWithPassword", error); } } }; BrowserPGPAdapter = class { /** * Encrypts data using PGP public key encryption. * * @param data - The plaintext string to encrypt. * Typically messages or structured data. * @param publicKeyArmored - ASCII-armored PGP public key. * Obtain from PGP key generation or key servers. * @returns ASCII-armored encrypted message * * @throws {Error} If encryption fails or public key is invalid */ async encrypt(data, publicKeyArmored) { try { const openpgp2 = await getOpenPGP2(); const publicKey = await openpgp2.readKey({ armoredKey: publicKeyArmored }); const encrypted = await openpgp2.encrypt({ message: await openpgp2.createMessage({ text: data }), encryptionKeys: publicKey, config: { preferredCompressionAlgorithm: openpgp2.enums.compression.zlib } }); return encrypted; } catch (error) { throw new Error(`PGP encryption failed: ${String(error)}`); } } /** * Decrypts PGP-encrypted data using a private key. * * @param encryptedData - ASCII-armored encrypted message. * Must be encrypted with corresponding public key. * @param privateKeyArmored - ASCII-armored PGP private key. * Must correspond to the public key used for encryption. * @returns The decrypted plaintext string * * @throws {Error} If decryption fails or private key is invalid */ async decrypt(encryptedData, privateKeyArmored) { try { const openpgp2 = await getOpenPGP2(); const privateKey = await openpgp2.readPrivateKey({ armoredKey: privateKeyArmored }); const message = await openpgp2.readMessage({ armoredMessage: encryptedData }); const { data: decrypted } = await openpgp2.decrypt({ message, decryptionKeys: privateKey }); return decrypted; } catch (error) { throw new Error(`PGP decryption failed: ${String(error)}`); } } /** * Generates a new PGP key pair. * * @param options - Key generation options * @param options.name - Name for the key identity. * Defaults to 'Vana User'. * @param options.email - Email for the key identity. * Defaults to 'user@vana.com'. * @param options.passphrase - Passphrase to protect the private key. * If not provided, key is unprotected. * @returns ASCII-armored public and private keys * * @throws {Error} If key generation fails */ async generateKeyPair(options) { try { const openpgp2 = await getOpenPGP2(); const keyGenParams = getPGPKeyGenParams(options); const { privateKey, publicKey } = await openpgp2.generateKey(keyGenParams); return { publicKey, privateKey }; } catch (error) { throw wrapCryptoError("PGP key generation", error); } } }; BrowserHttpAdapter = class { /** * Performs an HTTP request using the Fetch API. * * @param url - The URL to fetch. * Must be a valid HTTP/HTTPS URL. * @param options - Standard fetch options. * See MDN fetch documentation for details. * @returns Standard fetch Response object */ async fetch(url, options) { return fetch(url, options); } }; BrowserCacheAdapter = class { prefix = "vana_cache_"; /** * Retrieves a cached value by key. * * @param key - The cache key to look up. * Automatically prefixed to avoid conflicts. * @returns The cached value or null if not found */ get(key) { try { if (typeof sessionStorage === "undefined") { return null; } return sessionStorage.getItem(this.prefix + key); } catch { return null; } } /** * Stores a value in sessionStorage. * * @param key - The cache key. * Automatically prefixed with 'vana_cache_'. * @param value - The value to cache. * Will be cleared when tab closes. */ set(key, value) { try { if (typeof sessionStorage === "undefined") { return; } sessionStorage.setItem(this.prefix + key, value); } catch { } } /** * Removes a specific key from the cache. * * @param key - The cache key to remove. * Only removes the prefixed key. */ delete(key) { try { if (typeof sessionStorage === "undefined") { return; } sessionStorage.removeItem(this.prefix + key); } catch { } } /** * Clears all Vana-prefixed cache entries. * * @remarks * Only removes entries with the 'vana_cache_' prefix, * preserving other sessionStorage data. */ clear() { try { if (typeof sessionStorage === "undefined") { return; } const keysToRemove = []; for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); if (key?.startsWith(this.prefix)) { keysToRemove.push(key); } } keysToRemove.forEach((key) => { sessionStorage.removeItem(key); }); } catch { } } }; BrowserPlatformAdapter = class { crypto = new BrowserCryptoAdapter(); pgp = new BrowserPGPAdapter(); http = new BrowserHttpAdapter(); cache = new BrowserCacheAdapter(); platform = "browser"; }; } }); // src/errors.ts var VanaError = class extends Error { constructor(message, code) { super(message); this.code = code; this.name = this.constructor.name; if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } code; }; var RelayerError = class extends VanaError { constructor(message, statusCode, response) { super(message, "RELAYER_ERROR"); this.statusCode = statusCode; this.response = response; } statusCode; response; }; var UserRejectedRequestError = class extends VanaError { constructor(message = "User rejected the signature request") { super(message, "USER_REJECTED_REQUEST"); } }; var InvalidConfigurationError = class extends VanaError { constructor(message) { super(message, "INVALID_CONFIGURATION"); } }; var ContractNotFoundError = class extends VanaError { constructor(contractName, chainId) { super( `Contract ${contractName} not found on chain ${chainId}`, "CONTRACT_NOT_FOUND" ); } }; var BlockchainError = class extends VanaError { constructor(message, originalError) { super(message, "BLOCKCHAIN_ERROR"); this.originalError = originalError; } originalError; }; var SerializationError = class extends VanaError { constructor(message) { super(message, "SERIALIZATION_ERROR"); } }; var SignatureError = class extends VanaError { constructor(message, originalError) { super(message, "SIGNATURE_ERROR"); this.originalError = originalError; } originalError; }; var NetworkError = class extends VanaError { constructor(message, originalError) { super(message, "NETWORK_ERROR"); this.originalError = originalError; } originalError; }; var NonceError = class extends VanaError { constructor(message) { super(message, "NONCE_ERROR"); } }; var PersonalServerError = class extends VanaError { constructor(message, originalError) { super(message, "PERSONAL_SERVER_ERROR"); this.originalError = originalError; } originalError; }; var ServerUrlMismatchError = class extends VanaError { constructor(existingUrl, providedUrl, serverId) { super( `Server ${serverId} is already registered with URL "${existingUrl}". Cannot change to "${providedUrl}".`, "SERVER_URL_MISMATCH" ); this.existingUrl = existingUrl; this.providedUrl = providedUrl; this.serverId = serverId; } existingUrl; providedUrl; serverId; }; var PermissionError = class extends VanaError { constructor(message, originalError) { super(message, "PERMISSION_ERROR"); this.originalError = originalError; } originalError; }; var ReadOnlyError = class extends VanaError { constructor(operation, suggestion = "Initialize the SDK with a walletClient to perform this operation") { super( `Operation '${operation}' requires a wallet client. ${suggestion}`, "READ_ONLY_ERROR" ); this.operation = operation; this.suggestion = suggestion; } /** The operation that was attempted */ operation; /** Suggested solution for fixing the error */ suggestion; }; var TransactionPendingError = class extends VanaError { constructor(operationId, message, lastKnownStatus) { super( `Transaction operation pending: ${message} (operationId: ${operationId})`, "TRANSACTION_PENDING" ); this.operationId = operationId; this.lastKnownStatus = lastKnownStatus; } operationId; lastKnownStatus; /** * Converts the error to a JSON-serializable format. * * @remarks * Useful for logging, storage, or transmission of error details. * * @returns JSON representation of the error */ toJSON() { return { name: this.name, code: this.code, message: this.message, operationId: this.operationId, lastKnownStatus: this.lastKnownStatus }; } }; // src/contracts/contractController.ts import { getContract } from "viem"; // src/generated/abi/ComputeEngineImplementation.ts var ComputeEngineABI = [ { inputs: [], stateMutability: "nonpayable", type: "constructor" }, { inputs: [], name: "AccessControlBadConfirmation", type: "error" }, { inputs: [ { internalType: "address", name: "account", type: "address" }, { internalType: "bytes32", name: "neededRole", type: "bytes32" } ], name: "AccessControlUnauthorizedAccount", type: "error" }, { inputs: [ { internalType: "address", name: "target", type: "address" } ], name: "AddressEmptyCode", type: "error" }, { inputs: [ { internalType: "address", name: "account", type: "address" } ], name: "AddressInsufficientBalance", type: "error" }, { inputs: [ { internalType: "address", name: "implementation", type: "address" } ], name: "ERC1967InvalidImplementation", type: "error" }, { inputs: [], name: "ERC1967NonPayable", type: "error" }, { inputs: [], name: "EnforcedPause", type: "error" }, { inputs: [], name: "ExpectedPause", type: "error" }, { inputs: [], name: "FailedInnerCall", type: "error" }, { inputs: [], name: "FailedToAssignTee", type: "error" }, { inputs: [ { internalType: "uint256", name: "computeInstructionId", type: "uint256" } ], name: "InstructionNotFound", type: "error" }, { inputs: [], name: "InsufficientBalance", type: "error" }, { inputs: [], name: "InvalidAmount", type: "error" }, { inputs: [], name: "InvalidInitialization", type: "error" }, { inputs: [ { internalType: "enum IComputeEngine.JobStatus", name: "currentStatus", type: "uint8" }, { internalType: "enum IComputeEngine.JobStatus", name: "newStatus", type: "uint8" } ], name: "InvalidStatusTransition", type: "error" }, { inputs: [], name: "InvalidVanaAmount", type: "error" }, { inputs: [], name: "JobAlreadyDone", type: "error" }, { inputs: [], name: "JobLifeCycleEnded", type: "error" }, { inputs: [ { internalType: "uint256", name: "jobId", type: "uint256" } ], name: "JobNotFound", type: "error" }, { inputs: [ { internalType: "uint256", name: "jobId", type: "uint256" } ], name: "JobNotSubmitted", type: "error" }, { inputs: [], name: "NotInitializing", type: "error" }, { inputs: [], name: "NotJobOwner", type: "error" }, { inputs: [], name: "NotLongRunningJob", type: "error" }, { inputs: [], name: "NotQueryEngine", type: "error" }, { inputs: [], name: "NotTee", type: "error" }, { inputs: [], name: "OnlyRegisteredJobStatus", type: "error" }, { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" }, { inputs: [ { internalType: "address", name: "token", type: "address" } ], name: "SafeERC20FailedOperation", type: "error" }, { inputs: [ { internalType: "uint256", name: "jobId", type: "uint256" } ], name: "TeeAlreadyAssigned", type: "error" }, { inputs: [], name: "TeePoolNotFound", type: "error" }, { inputs: [], name: "UUPSUnauthorizedCallContext", type: "error" }, { inputs: [ { internalType: "bytes32", name: "slot", type: "bytes32" } ], name: "UUPSUnsupportedProxiableUUID", type: "error" }, { inputs: [], name: "UnauthorizedPaymentRequestor", type: "error" }, { inputs: [], name: "UnexpectedVanaDeposit", type: "error" }, { inputs: [], name: "ZeroAddress", type: "error" }, { inputs: [], name: "ZeroTeeAddress", type: "error" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: true, internalType: "address", name: "token", type: "address" }, { indexed: false, internalType: "uint256", name: "amount", type: "uint256" } ], name: "Deposit", type: "event" }, { anonymous: false, inputs: [ { indexed: false, internalType: "uint64", name: "version", type: "uint64" } ], name: "Initialized", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "uint256", name: "jobId", type: "uint256" } ], name: "JobCanceled", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "uint256", name: "jobId", type: "uint256" }, { indexed: true, internalType: "address", name: "ownerAddress", type: "address" } ], name: "JobRegistered", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "uint256", name: "jobId", type: "uint256" }, { indexed: false, internalType: "enum IComputeEngi