zkverifyjs
Version:
Submit proofs to zkVerify and query proof state with ease using our npm package.
106 lines • 4.56 kB
JavaScript
import { ZkVerifyEvents } from "../../enums.js";
/**
* Subscribes to `aggregation.NewAggregationReceipt` events and triggers the provided callback.
*
* - If both `domainId` and `aggregationId` are provided, the listener stops after the matching receipt is found.
* - If only `domainId` is provided, listens indefinitely for all receipts within that domain.
* - If neither is provided, listens to all receipts across all domains.
* - Throws if `aggregationId` is provided without a `domainId`.
*
* @param {ApiPromise} api - The Polkadot.js API instance.
* @param callback
* @param options - NewAggregationEventSubscriptionOptions containing domainId, aggregationId and optional timeout.
* @param emitter - EventEmitter
* @returns {EventEmitter} EventEmitter for listening to emitted events and unsubscribing.
*/
export async function subscribeToNewAggregationReceipts(api, callback, options = undefined, emitter) {
return new Promise((resolve, reject) => {
const DEFAULT_MATCH_TIMEOUT = 180000;
let domainId = undefined;
let aggregationId = undefined;
let timeoutId;
if (options) {
domainId = options.domainId?.toString().trim();
if ('aggregationId' in options) {
aggregationId = options.aggregationId?.toString().trim();
if (!domainId) {
reject(new Error('Cannot filter by aggregationId without also providing domainId.'));
return;
}
}
}
if (aggregationId && domainId) {
const timeoutValue = options && 'timeout' in options && typeof options.timeout === 'number' ? options.timeout : DEFAULT_MATCH_TIMEOUT;
timeoutId = setTimeout(() => {
unsubscribe(emitter);
reject(new Error(`Timeout exceeded: No event received within ${timeoutValue} ms`));
}, timeoutValue);
}
try {
api.rpc.chain.subscribeFinalizedHeads(async header => {
const blockHash = header.hash.toHex();
const apiAt = await api.at(blockHash);
const events = await apiAt.query.system.events();
events.forEach(record => {
const {
event,
phase
} = record;
if (event.section === 'aggregate' && event.method === 'NewAggregationReceipt') {
let currentDomainId;
let currentAggregationId;
const eventData = event.data.toHuman ? event.data.toHuman() : Array.from(event.data, item => item.toString());
const eventObject = {
event: ZkVerifyEvents.NewAggregationReceipt,
blockHash,
data: eventData,
phase: phase && typeof phase.toJSON === 'function' ? phase.toJSON() : phase?.toString() || ''
};
try {
currentDomainId = event.data[0]?.toString();
currentAggregationId = event.data[1]?.toString();
if (!currentDomainId || !currentAggregationId) {
reject(new Error('Event data is missing required fields: domainId or aggregationId.'));
return;
}
} catch (error) {
emitter.emit(ZkVerifyEvents.ErrorEvent, error);
reject(error);
return;
}
if (!options || !aggregationId && !domainId) {
emitter.emit(ZkVerifyEvents.NewAggregationReceipt, eventObject);
callback(eventObject);
} else if (domainId && !aggregationId && domainId === currentDomainId) {
emitter.emit(ZkVerifyEvents.NewAggregationReceipt, eventObject);
callback(eventObject);
} else if (domainId === currentDomainId && currentAggregationId === aggregationId) {
if (timeoutId) clearTimeout(timeoutId);
emitter.emit(ZkVerifyEvents.NewAggregationReceipt, eventObject);
callback(eventObject);
resolve(emitter);
return;
}
}
});
});
} catch (error) {
if (timeoutId) clearTimeout(timeoutId);
emitter.emit(ZkVerifyEvents.ErrorEvent, error);
reject(error);
}
return emitter;
});
}
/**
* Unsubscribes from all event tracking.
*
* - Emits a `ZkVerifyEvents.Unsubscribe` event before removing all listeners.
* - Use this to manually stop listening when not auto-unsubscribing on matched receipts.
*
* @param {EventEmitter} emitter - The EventEmitter instance returned by the subscription.
*/
export function unsubscribe(emitter) {
emitter.emit(ZkVerifyEvents.Unsubscribe);
emitter.removeAllListeners();
}