import '@polkadot/api-augment'; import type { ApiPromise } from '@polkadot/api'; import type { SubmittableExtrinsic } from '@polkadot/api/types'; import type { Hash } from '@polkadot/types/interfaces/runtime'; import type { ISubmittableResult } from '@polkadot/types/types'; import { type AcurastSigner } from './signer.js'; /** * Optional per-submission hooks. `onResult` receives every * {@link ISubmittableResult} that `signAndSend` emits for the batch this call * was included in, letting callers (e.g. `registerJob`) extract events / drive * their own follow-up subscriptions without owning the submission itself. */ export interface EnqueueHandlers { onResult?(result: ISubmittableResult): void | Promise; } /** * A single submission authority for one account. Every extrinsic the SDK sends * on behalf of a given signer should flow through one `TransactionQueue` so * that nonces never collide (the `Transaction has too low priority to replace * another transaction` / 1014 error). * * The public contract is intentionally tiny — `enqueue` plus introspection. * The `dequeue` / `prepareNonce` / `submit` seams live on * {@link BaseTransactionQueue} as protected hooks so both the bundled adapters * and bring-your-own implementations share the same processing loop. */ export interface TransactionQueue { /** Submit one extrinsic. Resolves with the in-block hash; rejects on dispatchError. */ enqueue(call: SubmittableExtrinsic<'promise'>, handlers?: EnqueueHandlers): Promise; /** Pending item count — for health / introspection. */ readonly size: number; /** Tear down any background timers/subscriptions. Optional (Sequential has none). */ stop?(): void | Promise; } export interface QueuedItem { call: SubmittableExtrinsic<'promise'>; handlers?: EnqueueHandlers; resolve(hash: Hash): void; reject(error: unknown): void; /** Block height when enqueued — used by block-age flushing (Batching only). */ enqueuedAtBlock?: number; } /** * Shared machinery for {@link SequentialTransactionQueue} and * {@link BatchingTransactionQueue}. Holds the item list and runs a single, * non-overlapping processing loop: `dequeue()` → `prepareNonce()` → * `combine()` → sign+send → await in-block → resolve/reject. Subclasses * override the three protected hooks to change batching / nonce strategy. */ export declare abstract class BaseTransactionQueue implements TransactionQueue { protected readonly api: ApiPromise; protected readonly signer: AcurastSigner; protected readonly queue: QueuedItem[]; protected processing: boolean; constructor(api: ApiPromise, signer: AcurastSigner); get size(): number; enqueue(call: SubmittableExtrinsic<'promise'>, handlers?: EnqueueHandlers): Promise; /** Hook fired synchronously when an item is added (Batching stamps the block). */ protected onEnqueue(_item: QueuedItem): void; /** * Select the items to submit this round. Return `[]` to submit nothing yet * (e.g. Batching waiting for its size/age threshold). Selected items must be * removed from `this.queue`. */ protected abstract dequeue(): QueuedItem[]; /** * Resolve the nonce for the next submission. Return `undefined` to let * `signAndSend` fetch a fresh `accountNextIndex` (Sequential). Return a * number for client-side nonce management (Batching). * * Kept separate from `dequeue` because nonce prep is an async, account-scoped * concern independent of which items are picked. */ protected abstract prepareNonce(): Promise; /** Combine selected items into one extrinsic. Default: single, else forceBatch. */ protected combine(items: QueuedItem[]): SubmittableExtrinsic<'promise'>; /** * Called when a submission attempt throws. Default: reject every item. * Batching overrides to reset the nonce and re-queue on nonce errors. */ protected onSubmitError(error: unknown, items: QueuedItem[]): void; protected runOnce(): Promise; /** Sign + send `call`, forwarding every result to each item's `onResult`. */ protected send(call: SubmittableExtrinsic<'promise'>, nonce: number | undefined, items: QueuedItem[]): Promise; /** * Map a submission result to a per-item view. Base: every item sees the full * result (correct for one-call-per-tx). {@link BatchingTransactionQueue} * overrides this to give each item only its own slice of a batch's events. */ protected scopeResults(result: ISubmittableResult, items: QueuedItem[]): ISubmittableResult[]; } /** * Default adapter: submit exactly one extrinsic at a time, only starting the * next once the previous is in a block, letting the node hand out a fresh * `accountNextIndex` for each. No client-side nonce state, so it is * self-correcting — the safe choice for correctness over throughput. */ export declare class SequentialTransactionQueue extends BaseTransactionQueue { protected dequeue(): QueuedItem[]; protected prepareNonce(): Promise; } export interface BatchingQueueOptions { /** Max extrinsics per `utility.forceBatch`. Default 20. */ maxBatchSize?: number; /** Flush once the oldest queued item is this many blocks old. Default 2. */ maxBlockAge?: number; /** How often the flush timer checks the queue, in ms. Default 3000. */ pollIntervalMs?: number; /** Max consecutive nonce-error retries before giving up. Default 3. */ maxNonceRetries?: number; } /** * Advanced adapter (blueprint: hyperdrive-relayer's transaction queue, minus * its `process.exit` / `api.disconnect` behavior). Auto-batches queued * extrinsics into `utility.forceBatch`, flushing when the queue reaches * `maxBatchSize` or the oldest item is `maxBlockAge` blocks old. Manages the * nonce client-side (seeded from `accountNextIndex`, incremented per batch) and * resets it from chain + re-queues on a nonce/priority error. * * Note: all enqueued calls should be built on the same `api` as this queue — * `forceBatch` wraps them with this queue's `api`. Per-item on-chain failures * surface as `utility.ItemFailed`/`BatchInterrupted` events (visible via * `onResult`), not a rejected `enqueue` promise. */ export declare class BatchingTransactionQueue extends BaseTransactionQueue { private readonly maxBatchSize; private readonly maxBlockAge; private readonly pollIntervalMs; private readonly maxNonceRetries; private currentBlock; private currentNonce; private nonceRetries; private timer; private unsubHeads; constructor(api: ApiPromise, signer: AcurastSigner, options?: BatchingQueueOptions); protected onEnqueue(item: QueuedItem): void; private ensureStarted; protected dequeue(): QueuedItem[]; protected prepareNonce(): Promise; private resetNonce; protected onSubmitError(error: unknown, items: QueuedItem[]): void; protected combine(items: QueuedItem[]): SubmittableExtrinsic<'promise'>; /** * De-multiplex a `forceBatch` result so each item's `onResult` sees only its * own call's events. Without this, a batched `registerJob` would read every * job's `JobRegistrationStoredV2` event and latch onto the wrong job id. * `combine` batches items in order, so the i-th event group maps to items[i]. */ protected scopeResults(result: ISubmittableResult, items: QueuedItem[]): ISubmittableResult[]; stop(): void; } /** * Returns the queue shared by every submission for the given account on the * given chain. If a consumer installed one via {@link setDefaultQueue}, that * instance is returned; otherwise a {@link SequentialTransactionQueue} is * lazily created. This is what lets all SDK code paths (registerJob, * setEnvVars, editScript, …) share one nonce authority without threading a * queue object through every call. * * Per-call/per-service `queue` overrides still win over this — pass one to * deviate a single operation onto a different queue. */ export declare function getDefaultQueue(api: ApiPromise, signer: AcurastSigner): TransactionQueue; /** * Installs `queue` as *the* default for the given account+chain, so every SDK * path that doesn't receive an explicit queue resolves to this one instance. * Call once at startup with your chosen adapter (e.g. a * {@link BatchingTransactionQueue}) to guarantee a single nonce authority per * account — otherwise mixing an injected queue on some calls with the * lazily-created default on others would create two authorities that race. */ export declare function setDefaultQueue(api: ApiPromise, signer: AcurastSigner, queue: TransactionQueue): void; //# sourceMappingURL=tx-queue.d.ts.map