UNPKG

zkverifyjs

Version:

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

149 lines 5.55 kB
import { subscribeToNewAggregationReceipts, unsubscribe } from "../../../api/aggregation/index.js"; import { EventEmitter } from 'events'; import { PUBLIC_ZK_VERIFY_EVENTS, ZkVerifyEvents } from "../../../enums.js"; export class EventManager { constructor(connectionManager) { this.unsubscribeFunctions = []; this.connectionManager = connectionManager; this.emitter = new EventEmitter(); } /** * Subscribes to specified ZkVerifyEvents. * For `NewAggregationReceipt`, `options` can include `domainId` and `aggregationId`. * For runtime events (e.g., ProofVerified), options are ignored. * * @param subscriptions - List of events to subscribe to with optional callback and filtering options. * @returns EventEmitter to allow listening to additional internal events (e.g., `Unsubscribe`). */ subscribe(subscriptions) { const { api } = this.connectionManager; const eventsToSubscribe = subscriptions?.length ? subscriptions : PUBLIC_ZK_VERIFY_EVENTS.map(event => ({ event, callback: undefined, options: event === ZkVerifyEvents.NewAggregationReceipt ? undefined : undefined })); eventsToSubscribe.forEach(({ event, callback, options }) => { switch (event) { case ZkVerifyEvents.NewAggregationReceipt: subscribeToNewAggregationReceipts(api, data => { this.emitter.emit(event, data); if (callback) callback(data); }, options, this.emitter); break; case ZkVerifyEvents.ProofVerified: case ZkVerifyEvents.NewProof: case ZkVerifyEvents.VkRegistered: case ZkVerifyEvents.NewDomain: case ZkVerifyEvents.DomainStateChanged: case ZkVerifyEvents.AggregationComplete: this._subscribeToRuntimeEvent(api, event, callback); break; default: throw new Error(`Unsupported event type for subscription: ${event}`); } }); return this.emitter; } /** * Subscribes to on-chain runtime events using api.query.system.events */ _subscribeToRuntimeEvent(api, eventType, callback) { const unsubscribeFn = api.query.system.events(records => { for (const { event, phase } of records) { const key = `${event.section}::${event.method}`; const matchMap = { [ZkVerifyEvents.ProofVerified]: /::ProofVerified/, [ZkVerifyEvents.CannotAggregate]: 'aggregate::CannotAggregate', [ZkVerifyEvents.NewProof]: 'aggregate::NewProof', [ZkVerifyEvents.VkRegistered]: /::VkRegistered/, [ZkVerifyEvents.AggregationComplete]: 'aggregate::AggregationComplete', [ZkVerifyEvents.NewDomain]: 'aggregate::NewDomain', [ZkVerifyEvents.DomainStateChanged]: 'aggregate::DomainStateChanged' }; const expected = matchMap[eventType]; if (expected && (typeof expected === 'string' && key === expected || expected instanceof RegExp && expected.test(key))) { const parsedPhase = phase.toJSON ? phase.toJSON() : phase.toString(); const eventPayload = { event: eventType, data: event.data.toHuman?.() ?? event.data.toString(), phase: parsedPhase }; this.emitter.emit(eventType, eventPayload); if (callback) { callback(eventPayload); } } } }); if (unsubscribeFn) { if (typeof unsubscribeFn === 'function') { this.unsubscribeFunctions.push(unsubscribeFn); } else if (unsubscribeFn && typeof unsubscribeFn.then === 'function') { unsubscribeFn.then(fn => { if (typeof fn === 'function') { this.unsubscribeFunctions.push(fn); } }).catch(error => { this.emitter.emit(ZkVerifyEvents.ErrorEvent, error); }); } } } /** * Waits for a specific `NewAggregationReceipt` event and returns the result as a NewAggregationReceipt object. * * @param domainId - The domain ID to listen for. * @param aggregationId - The aggregation ID to listen for. * @param timeout - Optional timeout value in milliseconds. * @returns {Promise<NewAggregationReceipt>} Resolves with the event data when found, or rejects on timeout/error. */ async waitForAggregationReceipt(domainId, aggregationId, timeout) { const { api } = this.connectionManager; const options = { domainId, aggregationId, timeout }; return new Promise((resolve, reject) => { subscribeToNewAggregationReceipts(api, eventObject => { const event = eventObject; if (event && event.data && event.data.domainId && event.data.aggregationId && event.data.receipt) { const result = { blockHash: event.blockHash ?? null, domainId: Number(event.data.domainId), aggregationId: Number(event.data.aggregationId), receipt: String(event.data.receipt) }; resolve(result); } else { reject(new Error('Invalid event data structure')); } }, options, this.emitter).catch(reject); }); } /** * Unsubscribes from all active subscriptions. */ unsubscribe() { this.unsubscribeFunctions.forEach(unsubscribeFn => { try { unsubscribeFn(); } catch (error) { console.debug('Error during subscription cleanup:', error); } }); this.unsubscribeFunctions.length = 0; unsubscribe(this.emitter); } }