UNPKG

zkverifyjs

Version:

Submit proofs to zkVerify and query proof state with ease using our npm package.

237 lines 8.69 kB
import { Destination } from "../../enums.js"; import { proofConfigurations, ProofType } from "../../config/index.js"; import { decodeDispatchError } from "../transactions/errors/index.js"; /** * Waits for the zkVerify node to sync. * @param api - The ApiPromise instance. * @returns A promise that resolves when the node is synced. */ export async function waitForNodeToSync(api) { let isSyncing = true; while (isSyncing) { const health = await api.rpc.system.health(); isSyncing = health.isSyncing.isTrue; if (isSyncing) { await new Promise(resolve => setTimeout(resolve, 1000)); } } } export function getProofProcessor(proofType) { const config = proofConfigurations[proofType]; if (!config) { throw new Error(`No config found for Proof Processor: ${proofType}`); } return config.processor; } export function getProofPallet(proofType) { const config = proofConfigurations[proofType]; if (!config) { throw new Error(`No config found for Proof Pallet: ${proofType}`); } return config.pallet; } export function checkReadOnly(connection) { if ((!('accounts' in connection) || 'accounts' in connection && connection.accounts.size === 0) && !('injector' in connection)) { throw new Error('This action requires an active account. The session is currently in read-only mode because no account is associated with it. Please provide an account at session start, or add one to the current session using `addAccount`.'); } } /** * Interprets a dry run response and returns whether it was successful and any error message. * @param api - The Polkadot.js API instance. * @param resultHex - The hex-encoded response from a dry run. * @returns An object containing `success` (boolean) and `message` (string). */ export const interpretDryRunResponse = async (api, resultHex) => { try { const responseBytes = Uint8Array.from(Buffer.from(resultHex.replace('0x', ''), 'hex')); if (responseBytes[0] === 0x00 && responseBytes[1] === 0x00) { return { success: true, message: 'Optimistic Verification Successful!' }; } if (responseBytes[0] === 0x00 && responseBytes[1] === 0x01) { const dispatchError = api.registry.createType('DispatchError', responseBytes.slice(2)); const errorMessage = decodeDispatchError(api, dispatchError); return { success: false, message: errorMessage }; } return { success: false, message: `Unexpected response format: ${resultHex}` }; } catch (error) { return { success: false, message: `Failed to interpret dry run result: ${error}` }; } }; /** * Binds all methods from the source object to the target object, * preserving the original `this` context. * * Throws an error if a method with the same name already exists on the target. * * @param target - The object to bind methods to. * @param source - The object containing the methods to bind. * * @throws {Error} If a method with the same name already exists on the target. */ export function bindMethods(target, source) { const propertyNames = Object.getOwnPropertyNames(Object.getPrototypeOf(source)); for (const name of propertyNames) { const method = source[name]; if (typeof method === 'function' && name !== 'constructor') { if (name in target) { throw new Error(`❌ Method collision detected: "${name}". Binding aborted.`); } target[name] = method.bind(source); } } } /** * Retrieves the selected account from the connection based on the provided account address. * If no account address is provided, it defaults to the first available account. * * @param {AccountConnection} connection - The connection containing account information. * @param {string | undefined} accountAddress - The optional account address to retrieve. * @returns {KeyringPair} - The selected account. * @throws {Error} If the account is not found. */ export const getSelectedAccount = (connection, accountAddress) => { let selectedAccount; if (accountAddress) { selectedAccount = connection.accounts.get(accountAddress); } else { selectedAccount = Array.from(connection.accounts.values())[0]; } if (!selectedAccount) { throw new Error(`Account ${accountAddress ?? ''} not found in session.`); } return selectedAccount; }; /** * Retrieves the selected `KeyringPair` from the given connection if it is an `AccountConnection`. * * - If the connection has local `accounts` (i.e., it's an `AccountConnection`), it uses the provided `accountAddress` * to select the appropriate account via `getSelectedAccount`. * - If the connection is a `WalletConnection`, returns `undefined`. * * @param {AccountConnection | WalletConnection} connection - The connection object which may contain accounts. * @param {string} [accountAddress] - Optional address of the account to select. * @returns {KeyringPair | undefined} - The selected `KeyringPair` if available, otherwise `undefined`. */ export function getKeyringAccountIfAvailable(connection, accountAddress) { return 'accounts' in connection ? getSelectedAccount(connection, accountAddress) : undefined; } /** * Converts a `DeliveryInput` into a properly formatted `Delivery` object. * Supports either a `None` variant or a `Hyperbridge` delivery configuration. * * @returns A `Delivery` object formatted for on-chain use. * @throws {Error} If required fields for Hyperbridge delivery are missing or invalid. * @param options */ export function normalizeDeliveryFromOptions(options) { if (options.destination === Destination.None) { return { None: null }; } const { deliveryInput } = options; return { destination: { Hyperbridge: { destinationChain: deliveryInput.destinationChain, destination_module: deliveryInput.destination_module, timeout: deliveryInput.timeout } }, price: deliveryInput.price }; } /** * Extracts a human-readable error message from various error types. * * @param err - The error object to extract a message from. * @returns A string message describing the error. */ export const extractErrorMessage = err => { if (err instanceof Error) { return err.message; } if (typeof err === 'object' && err !== null) { const maybeError = err; if (typeof maybeError.error === 'string') { return maybeError.error; } return JSON.stringify(err); } return String(err); }; /** * Safe wrapper for emitting events without crashing. */ export const safeEmit = (emitter, event, data) => { try { emitter.emit(event, data); } catch (error) { console.debug(`Failed to emit event ${event}:`, error); } }; /** * Type guard for Groth16Config */ export function isGroth16Config(options) { return options.proofType === ProofType.groth16 && options.config !== undefined && options.config.library !== undefined && options.config.curve !== undefined; } /** * Type guard for Plonky2Config */ export function isPlonky2Config(options) { return options.proofType === ProofType.plonky2 && options.config !== undefined && options.config.hashFunction !== undefined; } /** * Type guard for Risc0Config */ export function isRisc0Config(options) { return options.proofType === ProofType.risc0 && options.config !== undefined && options.config.version !== undefined; } /** * Type guard for Ultraplonk Config */ export function isUltraplonkConfig(options) { return options.proofType === ProofType.ultraplonk && options.config !== undefined && options.config.numberOfPublicInputs !== undefined; } // ADD_NEW_PROOF_TYPE if it has a config options object /** * Type guard to check if an object is a SubmittableExtrinsic<'promise'>. * * A SubmittableExtrinsic is identified by the presence of a `signAsync` method. * * @param obj - The object to evaluate. * @returns True if the object is a SubmittableExtrinsic, otherwise false. */ function isSubmittableExtrinsic(obj) { return typeof obj === 'object' && obj !== null && 'signAsync' in obj && typeof obj.signAsync === 'function'; } /** * Ensures the provided extrinsic is a SubmittableExtrinsic. * Converts a raw Extrinsic to Submittable if needed using the given API instance. * * @param extrinsic - A SubmittableExtrinsic or raw Extrinsic. * @param api - An instance of ApiPromise used to convert the extrinsic. * @returns A SubmittableExtrinsic<'promise'> ready to be signed and submitted. */ export function toSubmittableExtrinsic(extrinsic, api) { if (isSubmittableExtrinsic(extrinsic)) { return extrinsic; } const call = api.createType('Call', extrinsic.method); return api.tx(call); }