@arkade-os/sdk
Version:
Bitcoin wallet SDK with Taproot and Ark integration
184 lines (183 loc) • 7.73 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Batch = void 0;
const ark_1 = require("../providers/ark");
const txTree_1 = require("../tree/txTree");
const base_1 = require("@scure/base");
/**
* Batch namespace provides utilities for joining and processing batch session.
* The batch settlement process involves multiple events, this namespace provides abstractions and types to handle them.
* @see https://docs.arkadeos.com/learn/pillars/batch-swaps
* @example
* ```typescript
* // use wallet handler or create a custom one
* const handler = wallet.createBatchHandler(intentId, inputs, musig2session);
*
* const abortController = new AbortController();
* // Get event stream from Ark provider
* const eventStream = arkProvider.getEventStream(
* abortController.signal,
* ['your-topic-1', 'your-topic-2']
* );
*
* // Join the batch and process events
* try {
* const commitmentTxid = await Batch.join(eventStream, handler);
* console.log('Batch completed with commitment:', commitmentTxid);
* } catch (error) {
* console.error('Batch processing failed:', error);
* } finally {
* abortController.abort();
* }
* ```
*/
var Batch;
(function (Batch) {
// State machine steps for batch session
let Step;
(function (Step) {
Step["Start"] = "start";
Step["BatchStarted"] = "batch_started";
Step["TreeSigningStarted"] = "tree_signing_started";
Step["TreeNoncesAggregated"] = "tree_nonces_aggregated";
Step["BatchFinalization"] = "batch_finalization";
})(Step || (Step = {}));
/**
* Start the state machine that will process the batch events and join a batch.
* @param eventIterator - The events stream to process.
* @param handler - How to react to events.
* @param options - Options.
*/
async function join(eventIterator, handler, options = {}) {
const { abortController, skipVtxoTreeSigning = false, eventCallback, } = options;
let step = Step.Start;
// keep track of tree transactions as they arrive
const flatVtxoTree = [];
const flatConnectorTree = [];
// once everything is collected, the TxTree objects are created
let vtxoTree = undefined;
let connectorTree = undefined;
for await (const event of eventIterator) {
if (abortController?.signal.aborted) {
throw new Error("canceled");
}
if (eventCallback) {
// don't wait for the callback to complete and ignore errors
eventCallback(event).catch(() => { });
}
switch (event.type) {
case ark_1.SettlementEventType.BatchStarted: {
const e = event;
const { skip } = await handler.onBatchStarted(e);
if (!skip) {
step = Step.BatchStarted;
if (skipVtxoTreeSigning) {
// skip TxTree events and musig2 signatures and nonces
step = Step.TreeNoncesAggregated;
}
}
continue;
}
case ark_1.SettlementEventType.BatchFinalized: {
if (step !== Step.BatchFinalization) {
continue;
}
if (handler.onBatchFinalized) {
await handler.onBatchFinalized(event);
}
return event.commitmentTxid;
}
case ark_1.SettlementEventType.BatchFailed: {
if (handler.onBatchFailed) {
await handler.onBatchFailed(event);
continue;
}
throw new Error(event.reason);
}
case ark_1.SettlementEventType.TreeTx: {
if (step !== Step.BatchStarted &&
step !== Step.TreeNoncesAggregated) {
continue;
}
// batchIndex 0 = vtxo tree, batchIndex 1 = connector tree
if (event.batchIndex === 0) {
flatVtxoTree.push(event.chunk);
}
else {
flatConnectorTree.push(event.chunk);
}
if (handler.onTreeTxEvent) {
await handler.onTreeTxEvent(event);
}
continue;
}
case ark_1.SettlementEventType.TreeSignature: {
if (step !== Step.TreeNoncesAggregated) {
continue;
}
if (!vtxoTree) {
throw new Error("vtxo tree not initialized");
}
// push signature to the vtxo tree
const tapKeySig = base_1.hex.decode(event.signature);
vtxoTree.update(event.txid, (tx) => {
tx.updateInput(0, {
tapKeySig,
});
});
if (handler.onTreeSignatureEvent) {
await handler.onTreeSignatureEvent(event);
}
continue;
}
case ark_1.SettlementEventType.TreeSigningStarted: {
if (step !== Step.BatchStarted) {
continue;
}
// create vtxo tree from collected chunks
vtxoTree = txTree_1.TxTree.create(flatVtxoTree);
const { skip } = await handler.onTreeSigningStarted(event, vtxoTree);
if (!skip) {
step = Step.TreeSigningStarted;
}
continue;
}
case ark_1.SettlementEventType.TreeNonces: {
if (step !== Step.TreeSigningStarted) {
continue;
}
const { fullySigned } = await handler.onTreeNonces(event);
if (fullySigned) {
step = Step.TreeNoncesAggregated;
}
continue;
}
case ark_1.SettlementEventType.BatchFinalization: {
if (step !== Step.TreeNoncesAggregated) {
continue;
}
// Build vtxo tree if it hasn't been built yet
if (!vtxoTree && flatVtxoTree.length > 0) {
vtxoTree = txTree_1.TxTree.create(flatVtxoTree);
}
if (!vtxoTree && !skipVtxoTreeSigning) {
throw new Error("vtxo tree not initialized");
}
// Build connector tree if we have chunks
if (flatConnectorTree.length > 0) {
connectorTree = txTree_1.TxTree.create(flatConnectorTree);
}
await handler.onBatchFinalization(event, vtxoTree, connectorTree);
step = Step.BatchFinalization;
continue;
}
default:
// unknown event type, continue
continue;
}
}
// iterator closed without finalization, something went wrong
throw new Error("event stream closed");
}
Batch.join = join;
})(Batch || (exports.Batch = Batch = {}));