UNPKG

@cheqd/sdk

Version:

A TypeScript SDK built with CosmJS to interact with the cheqd network ledger

600 lines 26.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultBackoffOptions = exports.createMsgUpdateDidDocPayloadToSign = exports.EnglishMnemonic = exports.TImportableEd25519Key = void 0; exports.isEqualKeyValuePair = isEqualKeyValuePair; exports.createSignInputsFromImportableEd25519Key = createSignInputsFromImportableEd25519Key; exports.createKeyPairRaw = createKeyPairRaw; exports.createKeyPairBase64 = createKeyPairBase64; exports.createKeyPairHex = createKeyPairHex; exports.createVerificationKeys = createVerificationKeys; exports.createDidVerificationMethod = createDidVerificationMethod; exports.createDidPayload = createDidPayload; exports.validateSpecCompliantPayload = validateSpecCompliantPayload; exports.createCosmosPayerWallet = createCosmosPayerWallet; exports.toMultibaseRaw = toMultibaseRaw; exports.createMsgCreateDidDocPayloadToSign = createMsgCreateDidDocPayloadToSign; exports.createMsgDeactivateDidDocPayloadToSign = createMsgDeactivateDidDocPayloadToSign; exports.createMsgResourcePayloadToSign = createMsgResourcePayloadToSign; exports.getCosmosAccount = getCosmosAccount; exports.checkBalance = checkBalance; exports.isJSON = isJSON; exports.retry = retry; exports.normalizeAuthentication = normalizeAuthentication; exports.normalizeController = normalizeController; exports.normalizeService = normalizeService; exports.denormalizeService = denormalizeService; const types_1 = require("./types"); const uint8arrays_cjs_1 = require("uint8arrays-cjs"); const basics_1 = require("multiformats-cjs/basics"); const did_jwt_cjs_1 = require("did-jwt-cjs"); const ed25519_cjs_1 = require("@stablelib/ed25519-cjs"); const proto_signing_cjs_1 = require("@cosmjs/proto-signing-cjs"); const crypto_cjs_1 = require("@cosmjs/crypto-cjs"); const amino_cjs_1 = require("@cosmjs/amino-cjs"); const secp256k1_cjs_1 = __importDefault(require("secp256k1-cjs")); const uuid_cjs_1 = require("uuid-cjs"); const v2_1 = require("@cheqd/ts-proto-cjs/cheqd/did/v2"); const did_1 = require("./modules/did"); const v2_2 = require("@cheqd/ts-proto-cjs/cheqd/resource/v2"); const encoding_cjs_1 = require("@cosmjs/encoding-cjs"); const stargate_cjs_1 = require("@cosmjs/stargate-cjs"); const exponential_backoff_cjs_1 = require("exponential-backoff-cjs"); /** * Utility object for validating TImportableEd25519Key objects. * Provides type guard functionality to ensure key structure integrity. */ exports.TImportableEd25519Key = { /** * Type guard to validate if an object is a valid TImportableEd25519Key. * * @param key - Object to validate * @returns True if the object is a valid TImportableEd25519Key */ isValid(key) { return (typeof key === 'object' && key !== null && typeof key.publicKeyHex === 'string' && isHex(key.publicKeyHex) && typeof key.privateKeyHex === 'string' && isHex(key.privateKeyHex) && typeof key.kid === 'string' && key.type === 'Ed25519'); }, }; /** Multicodec header for Ed25519 public keys */ const MULTICODEC_ED25519_HEADER = new Uint8Array([0xed, 0x01]); /** * Compares two arrays of key-value pairs for equality. * * @param kv1 - First array of key-value pairs * @param kv2 - Second array of key-value pairs * @returns True if both arrays contain identical key-value pairs in the same order */ function isEqualKeyValuePair(kv1, kv2) { return kv1.every((item, index) => item.key === kv2[index].key && item.value === kv2[index].value); } /** * Extended English mnemonic class with additional validation patterns. * Provides regex pattern matching for mnemonic phrase validation. */ class EnglishMnemonic extends crypto_cjs_1.EnglishMnemonic { /** Regular expression pattern for validating English mnemonic phrases */ static _mnemonicMatcher = /^[a-z]+( [a-z]+)*$/; } exports.EnglishMnemonic = EnglishMnemonic; /** * Creates signing inputs from an importable Ed25519 key by matching it with verification methods. * Supports multiple verification method types and key formats. * * @param key - The Ed25519 key to create signing inputs from * @param verificationMethod - Array of verification methods to match against * @returns Signing inputs containing verification method ID and private key * @throws Error if key validation fails or no matching verification method is found */ function createSignInputsFromImportableEd25519Key(key, verificationMethod) { if (!exports.TImportableEd25519Key.isValid(key)) throw new Error(`Key validation failed. Expected ${Object.values(exports.TImportableEd25519Key).join(', ')}`); const publicKey = (0, uint8arrays_cjs_1.fromString)(key.publicKeyHex, 'hex'); for (const method of verificationMethod) { switch (method.type) { case types_1.VerificationMethods.Ed255192020: const publicKeyMultibase = toMultibaseRaw(publicKey); if (method.publicKeyMultibase === publicKeyMultibase) { return { verificationMethodId: method.id, privateKeyHex: key.privateKeyHex, }; } case types_1.VerificationMethods.Ed255192018: const publicKeyBase58 = basics_1.bases['base58btc'].encode(publicKey).slice(1); if (method.publicKeyBase58 === publicKeyBase58) { return { verificationMethodId: method.id, privateKeyHex: key.privateKeyHex, }; } case types_1.VerificationMethods.JWK: const publicKeyJwk = { crv: 'Ed25519', kty: 'OKP', x: (0, uint8arrays_cjs_1.toString)(publicKey, 'base64url'), }; if (JSON.stringify(method.publicKeyJwk) === JSON.stringify(publicKeyJwk)) { return { verificationMethodId: method.id, privateKeyHex: key.privateKeyHex, }; } } throw new Error(`Unsupported verification method type: ${method.type}. Expected one of: ${Object.values(types_1.VerificationMethods).join(', ')}`); } throw new Error(`No verification method type provided. Expected one of: ${Object.values(types_1.VerificationMethods).join(', ')}`); } /** * Creates a raw Ed25519 key pair using the StableLib library. * * @param seed - Optional seed string for deterministic key generation * @returns Raw KeyPair object with publicKey and secretKey as Uint8Arrays */ function createKeyPairRaw(seed) { return seed ? (0, ed25519_cjs_1.generateKeyPairFromSeed)((0, uint8arrays_cjs_1.fromString)(seed)) : (0, ed25519_cjs_1.generateKeyPair)(); } /** * Creates an Ed25519 key pair with Base64-encoded keys. * * @param seed - Optional seed string for deterministic key generation * @returns Key pair with Base64-encoded public and private keys */ function createKeyPairBase64(seed) { const keyPair = seed ? (0, ed25519_cjs_1.generateKeyPairFromSeed)((0, uint8arrays_cjs_1.fromString)(seed)) : (0, ed25519_cjs_1.generateKeyPair)(); return { publicKey: (0, uint8arrays_cjs_1.toString)(keyPair.publicKey, 'base64'), privateKey: (0, uint8arrays_cjs_1.toString)(keyPair.secretKey, 'base64'), }; } /** * Creates an Ed25519 key pair with hexadecimal-encoded keys. * * @param seed - Optional seed string for deterministic key generation * @returns Key pair with hexadecimal-encoded public and private keys */ function createKeyPairHex(seed) { const keyPair = createKeyPairRaw(seed); return { publicKey: (0, uint8arrays_cjs_1.toString)(keyPair.publicKey, 'hex'), privateKey: (0, uint8arrays_cjs_1.toString)(keyPair.secretKey, 'hex'), }; } /** * Creates verification keys structure with DID URLs and key identifiers. * Supports multiple algorithm types and network configurations. * * @param publicKey - Public key in base64 or hex format * @param algo - Algorithm for method-specific ID generation * @param keyFragment - Key fragment for the verification key identifier * @param network - Cheqd network (defaults to Testnet) * @param methodSpecificId - Optional pre-computed method-specific ID * @param didUrl - Optional pre-computed DID URL * @returns Verification keys structure with all identifiers * @throws Error if public key format is invalid */ function createVerificationKeys(publicKey, algo, keyFragment, network = types_1.CheqdNetwork.Testnet, methodSpecificId, didUrl) { if (isHex(publicKey)) { publicKey = (0, uint8arrays_cjs_1.toString)((0, uint8arrays_cjs_1.fromString)(publicKey, 'hex'), 'base64'); } else if (!isBase64(publicKey)) { throw new Error('publicKey validation failed. PublicKey should be in base64 or hex format'); } switch (algo) { case types_1.MethodSpecificIdAlgo.Base58: methodSpecificId ||= basics_1.bases['base58btc'].encode((0, did_jwt_cjs_1.base64ToBytes)(publicKey)); didUrl ||= `did:cheqd:${network}:${basics_1.bases['base58btc'] .encode((0, crypto_cjs_1.sha256)((0, did_jwt_cjs_1.base64ToBytes)(publicKey)).slice(0, 16)) .slice(1)}`; return { methodSpecificId, didUrl, keyId: `${didUrl}#${keyFragment}`, publicKey, }; case types_1.MethodSpecificIdAlgo.Uuid: methodSpecificId ||= (0, uuid_cjs_1.v4)(); didUrl ||= `did:cheqd:${network}:${methodSpecificId}`; return { methodSpecificId, didUrl, keyId: `${didUrl}#${keyFragment}`, publicKey, }; } } /** * Creates DID verification methods from verification method types and keys. * Supports Ed25519 keys in multiple formats (multibase, base58, JWK). * * @param verificationMethodTypes - Array of verification method types to create * @param verificationKeys - Array of verification keys corresponding to each type * @returns Array of formatted verification methods for DID documents */ function createDidVerificationMethod(verificationMethodTypes, verificationKeys) { return (verificationMethodTypes.map((type, _) => { switch (type) { case types_1.VerificationMethods.Ed255192020: return { id: verificationKeys[_].keyId, type, controller: verificationKeys[_].didUrl, publicKeyMultibase: toMultibaseRaw((0, did_jwt_cjs_1.base64ToBytes)(verificationKeys[_].publicKey)), }; case types_1.VerificationMethods.Ed255192018: return { id: verificationKeys[_].keyId, type, controller: verificationKeys[_].didUrl, publicKeyBase58: basics_1.bases['base58btc'] .encode((0, did_jwt_cjs_1.base64ToBytes)(verificationKeys[_].publicKey)) .slice(1), }; case types_1.VerificationMethods.JWK: return { id: verificationKeys[_].keyId, type, controller: verificationKeys[_].didUrl, publicKeyJwk: { crv: 'Ed25519', kty: 'OKP', x: (0, uint8arrays_cjs_1.toString)((0, uint8arrays_cjs_1.fromString)(verificationKeys[_].publicKey, 'base64pad'), 'base64url'), }, }; } }) ?? []); } /** * Creates a complete DID document payload with verification methods and controllers. * * @param verificationMethods - Array of verification methods for the DID * @param verificationKeys - Array of verification keys for authentication * @param controller - Optional array of controller DIDs (defaults to self-controlled) * @returns Complete DID document with all required fields * @throws Error if verification methods or keys are missing */ function createDidPayload(verificationMethods, verificationKeys, controller = []) { if (!verificationMethods || verificationMethods.length === 0) throw new Error('No verification methods provided'); if (!verificationKeys || verificationKeys.length === 0) throw new Error('No verification keys provided'); const did = verificationKeys[0].didUrl; return { id: did, controller: controller.length ? controller : Array.from(new Set(verificationKeys.map((key) => key.didUrl))), verificationMethod: verificationMethods, authentication: verificationKeys.map((key) => key.keyId), assertionMethod: verificationKeys.map((key) => key.keyId), }; } /** * Validates a DID document against the Cheqd specification and converts to protobuf format. * Ensures all required fields are present and verification methods are supported. * * @param didDocument - DID document to validate * @returns Validation result with protobuf conversion or error details */ function validateSpecCompliantPayload(didDocument) { // id is required, validated on both compile and runtime if (!didDocument?.id) return { valid: false, error: 'id is required' }; // verificationMethod is required if (!didDocument?.verificationMethod) return { valid: false, error: 'verificationMethod is required' }; // verificationMethod must be an array if (!Array.isArray(didDocument?.verificationMethod)) return { valid: false, error: 'verificationMethod must be an array' }; // verificationMethod types must be supported const protoVerificationMethod = didDocument.verificationMethod.map((vm) => { switch (vm?.type) { case types_1.VerificationMethods.Ed255192020: if (!vm.publicKeyMultibase) throw new Error('publicKeyMultibase is required'); return v2_1.VerificationMethod.fromPartial({ id: vm.id, controller: vm.controller, verificationMethodType: types_1.VerificationMethods.Ed255192020, verificationMaterial: vm.publicKeyMultibase, }); case types_1.VerificationMethods.JWK: if (!vm.publicKeyJwk) throw new Error('publicKeyJwk is required'); return v2_1.VerificationMethod.fromPartial({ id: vm.id, controller: vm.controller, verificationMethodType: types_1.VerificationMethods.JWK, verificationMaterial: JSON.stringify(vm.publicKeyJwk), }); case types_1.VerificationMethods.Ed255192018: if (!vm.publicKeyBase58) throw new Error('publicKeyBase58 is required'); return v2_1.VerificationMethod.fromPartial({ id: vm.id, controller: vm.controller, verificationMethodType: types_1.VerificationMethods.Ed255192018, verificationMaterial: vm.publicKeyBase58, }); default: throw new Error(`Unsupported verificationMethod type: ${vm?.type}`); } }); const protoService = normalizeService(didDocument); return { valid: true, protobufVerificationMethod: protoVerificationMethod, protobufService: protoService, }; } /** * Creates a Cosmos wallet from a seed phrase or private key. * Automatically detects whether the input is a mnemonic phrase or hex-encoded private key. * * @param cosmosPayerSeed - Mnemonic phrase or hexadecimal private key * @returns Promise resolving to DirectSecp256k1HdWallet or DirectSecp256k1Wallet */ function createCosmosPayerWallet(cosmosPayerSeed) { return EnglishMnemonic._mnemonicMatcher.test(cosmosPayerSeed) ? proto_signing_cjs_1.DirectSecp256k1HdWallet.fromMnemonic(cosmosPayerSeed, { prefix: 'cheqd' }) : proto_signing_cjs_1.DirectSecp256k1Wallet.fromKey((0, uint8arrays_cjs_1.fromString)(cosmosPayerSeed.replace(/^0x/, ''), 'hex'), 'cheqd'); } /** * Converts a raw Ed25519 public key to multibase format with proper multicodec header. * * @param key - Raw Ed25519 public key as Uint8Array * @returns Multibase-encoded string with Ed25519 multicodec prefix */ function toMultibaseRaw(key) { const multibase = new Uint8Array(MULTICODEC_ED25519_HEADER.length + key.length); multibase.set(MULTICODEC_ED25519_HEADER); multibase.set(key, MULTICODEC_ED25519_HEADER.length); return basics_1.bases['base58btc'].encode(multibase); } /** * Creates a MsgCreateDidDoc payload ready for signing. * Validates the DID document and converts it to protobuf format. * * @param didPayload - DID document to create message payload from * @param versionId - Version identifier for the DID document * @returns Encoded message payload bytes ready for signing */ async function createMsgCreateDidDocPayloadToSign(didPayload, versionId) { const { protobufVerificationMethod, protobufService } = await did_1.DIDModule.validateSpecCompliantPayload(didPayload); return v2_1.MsgCreateDidDocPayload.encode(v2_1.MsgCreateDidDocPayload.fromPartial({ context: didPayload?.['@context'], id: didPayload.id, controller: didPayload.controller, verificationMethod: protobufVerificationMethod, authentication: didPayload.authentication, assertionMethod: didPayload.assertionMethod, capabilityInvocation: didPayload.capabilityInvocation, capabilityDelegation: didPayload.capabilityDelegation, keyAgreement: didPayload.keyAgreement, service: protobufService, alsoKnownAs: didPayload.alsoKnownAs, versionId, })).finish(); } /** Alias for createMsgCreateDidDocPayloadToSign - used for DID document updates */ exports.createMsgUpdateDidDocPayloadToSign = createMsgCreateDidDocPayloadToSign; /** * Creates a MsgDeactivateDidDoc payload ready for signing. * * @param didPayload - DID document containing the ID to deactivate * @param versionId - Optional version identifier for the deactivation * @returns Encoded deactivation message payload bytes ready for signing */ function createMsgDeactivateDidDocPayloadToSign(didPayload, versionId) { return v2_1.MsgDeactivateDidDocPayload.encode(v2_1.MsgDeactivateDidDocPayload.fromPartial({ id: didPayload.id, versionId, })).finish(); } /** * Creates a MsgCreateResource payload ready for signing. * * @param payload - Resource creation payload (partial or complete) * @returns Encoded resource creation message payload bytes ready for signing */ function createMsgResourcePayloadToSign(payload) { return v2_2.MsgCreateResourcePayload.encode(v2_2.MsgCreateResourcePayload.fromPartial(payload)).finish(); } /** * Derives a Cosmos account address from a hexadecimal public key. * Converts the public key to the appropriate format and generates a bech32 address. * * @param publicKeyHex - Secp256k1 public key in hexadecimal format * @returns Bech32-encoded Cheqd account address */ function getCosmosAccount(publicKeyHex) { const { publicKeyConvert } = secp256k1_cjs_1.default; return (0, encoding_cjs_1.toBech32)('cheqd', (0, amino_cjs_1.rawSecp256k1PubkeyToRawAddress)(publicKeyConvert((0, uint8arrays_cjs_1.fromString)(publicKeyHex, 'hex'), true))); } /** * Checks the balance of all coins for a given address on the blockchain. * * @param address - Bech32-encoded account address to check balance for * @param rpcAddress - RPC endpoint URL of the blockchain node * @returns Promise resolving to array of coin balances */ async function checkBalance(address, rpcAddress) { const client = await stargate_cjs_1.StargateClient.connect(rpcAddress); return await client.getAllBalances(address); } /** * Checks if a given input is valid JSON. * * @param input - Input to validate as JSON * @returns True if the input is a valid JSON string */ function isJSON(input) { if (typeof input !== 'string') return false; try { JSON.parse(input); return true; } catch (e) { return false; } } /** Default configuration options for exponential backoff retry logic */ exports.DefaultBackoffOptions = { jitter: 'full', timeMultiple: 1, delayFirstAttempt: false, maxDelay: 100, startingDelay: 100, numOfAttempts: 3, }; /** * Retries a function with exponential backoff strategy. * Provides configurable retry logic with jitter and delay options. * * @template T - Return type of the function being retried * @param fn - Async function to retry * @param options - Optional backoff configuration (uses defaults if not provided) * @returns Promise resolving to the function result or undefined if all retries fail */ async function retry(fn, options) { // set default options if (!options) { options = exports.DefaultBackoffOptions; } else { // overwrite defaults with user supplied options options = { ...exports.DefaultBackoffOptions, ...options }; } let result; try { result = await (0, exponential_backoff_cjs_1.backOff)(fn, options); } catch (e) { console.error(e); } return result; } /** * Validates if a string is properly formatted Base64. * Performs pattern matching and encoding validation. * * @param str - String to validate as Base64 * @returns True if the string is valid Base64 */ function isBase64(str) { // Quick pattern check to filter obvious non-base64 strings const base64Pattern = /^[A-Za-z0-9+/]*={0,3}$/; if (!base64Pattern.test(str)) { return false; } try { return (0, uint8arrays_cjs_1.toString)((0, uint8arrays_cjs_1.fromString)(str, 'base64'), 'base64') === str; } catch (e) { return false; } } /** * Validates if a string is properly formatted hexadecimal. * Performs pattern matching and encoding validation. * * @param str - String to validate as hexadecimal * @returns True if the string is valid hexadecimal */ function isHex(str) { // Quick pattern check to filter obvious non-hex strings const hexPattern = /^[0-9a-fA-F]*$/; if (!hexPattern.test(str)) { return false; } try { return (0, uint8arrays_cjs_1.toString)((0, uint8arrays_cjs_1.fromString)(str, 'hex'), 'hex') === str; } catch { return false; } } /** * Normalizes the authentication property of a DID document to an array of strings. * Handles both string references and embedded verification method objects. * * @param didDocument - DID document to normalize authentication for * @returns Array of authentication method identifiers * @throws Error if authentication section is missing */ function normalizeAuthentication(didDocument) { if (!didDocument.authentication) throw new Error('Invalid DID Document: Authentication section is required in DID Document'); const authArray = Array.isArray(didDocument.authentication) ? didDocument.authentication : [didDocument.authentication]; return authArray.map((a) => (typeof a === 'string' ? a : a.id)); } /** * Normalizes the controller property of a DID document to an array of strings. * Defaults to self-controlled if no controller is specified. * * @param didDocument - DID document to normalize controller for * @returns Array of controller DID identifiers */ function normalizeController(didDocument) { if (!didDocument.controller) return [didDocument.id]; return Array.isArray(didDocument.controller) ? didDocument.controller : [didDocument.controller]; } /** * Normalizes DID document services to protobuf format. * Converts service endpoints to arrays and includes optional properties. * * @param didDocument - DID document containing services to normalize * @returns Array of protobuf-formatted services or undefined if no services */ function normalizeService(didDocument) { return didDocument.service?.map((s) => { return v2_1.Service.fromPartial({ id: s?.id, serviceType: s?.type, serviceEndpoint: s ? (Array.isArray(s.serviceEndpoint) ? s.serviceEndpoint : [s.serviceEndpoint]) : [], ...(s?.recipientKeys && { recipientKeys: s.recipientKeys }), ...(s?.routingKeys && { routingKeys: s.routingKeys }), ...(s?.accept && { accept: s.accept }), ...(s?.priority !== undefined && { priority: s.priority }), }); }); } /** * Converts protobuf services back to standard DID document service format. * Handles special context requirements for LinkedDomains services. * * @param didDocument - Protobuf DID document containing services to denormalize * @returns Array of standard DID document services */ function denormalizeService(didDocument) { return didDocument.service.map((s) => { if (s.serviceType === types_1.ServiceType.LinkedDomains) { const updatedContext = [...didDocument.context, did_1.contexts.LinkedDomainsContext]; didDocument = { ...didDocument, context: updatedContext }; } return { id: s.id, type: s.serviceType, serviceEndpoint: Array.isArray(s?.serviceEndpoint) ? s.serviceEndpoint.length === 1 ? s.serviceEndpoint[0] : s.serviceEndpoint : s?.serviceEndpoint, ...(s.recipientKeys && { recipientKeys: s.recipientKeys }), ...(s.routingKeys && { routingKeys: s.routingKeys }), ...(s.accept && { accept: s.accept }), ...(s.priority !== undefined && { priority: s.priority }), }; }); } //# sourceMappingURL=utils.js.map