@effectstream/batcher-sdk
Package:
@effectstream/batcher-sdk· Source
Effectstream's cross-chain input batcher. Accepts signed user inputs over HTTP, batches them per adapter, and submits each batch as a single on-chain transaction. Persists every input to storage before it acks, so a crashed batcher recovers without losing inputs.
- Storage is the source of truth. No in-memory pool; the batcher recovers from a restart by reading the same files (or DB rows) it wrote on accept.
- Per-adapter batching criteria: time window, size, value threshold, hybrid, or a function you provide.
- Pluggable everywhere: storage backend, blockchain adapter, batch builder, lifecycle listeners.
- Default chain adapters for Effectstream's L2, generic EVM (viem + Hardhat artifacts), Midnight, and Bitcoin regtest.
- Optional REST API on Fastify; bypassable if you want to drive the batcher from your own runtime.
Install
bun add @effectstream/batcher-sdk
# or
npm install @effectstream/batcher-sdk
Standalone usage
A minimal end-to-end example using FileStorage, the EffectstreamL2 adapter, and the bundled HTTP server.
import { main, suspend } from "effection";
import {
BatcherConfig,
createNewBatcher,
EffectstreamL2DefaultAdapter,
FileStorage,
} from "@effectstream/batcher-sdk";
const adapter = new EffectstreamL2DefaultAdapter(
"0x...", // contract address
"0x...", // submitter private key
0n, // fee
"parallelEvmRPC_fast",
);
const config: BatcherConfig = {
pollingIntervalMs: 1000,
enableHttpServer: true,
confirmationLevel: "wait-effectstream-processed",
enableEventSystem: true,
port: 3334,
};
const storage = new FileStorage("./batcher-data");
main(function* () {
const batcher = createNewBatcher(config, storage);
batcher.addBlockchainAdapter("effectstream-l2", adapter, {
criteriaType: "time",
timeWindowMs: 1000,
});
batcher.addStateTransition("startup", ({ publicConfig }) => {
console.log(`batcher up, polling every ${publicConfig.pollingIntervalMs}ms`);
});
yield* batcher.runBatcher();
yield* suspend();
});
That accepts inputs on http://localhost:3334, batches them on a 1s window, submits via the adapter, and stays up until you cancel the operation.
Submitting an input
curl -X POST http://localhost:3334/batch-input \
-H "Content-Type: application/json" \
-d '{
"address": "0x...",
"input": "myGameInput",
"signature": "0x...",
"timestamp": 1234567890
}'
signature is required for the EVM and Cardano adapters. Adapters that override verifySignature (Midnight, for example) accept inputs without it but must implement their own check.
Batching criteria
Per-adapter, you choose how runBatcher decides to submit:
time: everytimeWindowMsmilliseconds.size: whenmaxBatchSizeinputs are queued.value: when accumulated value (viavalueAccumulatorFn) reachestargetValue.hybrid: time OR size, whichever comes first.custom: yourisBatchReadyFn(inputs, lastProcessTime)returnstrue.
Confirmation levels
batcher.batchInput(input, level?) returns when the chosen level is reached:
no-wait: returns once the input is queued.wait-receipt: waits for the blockchain transaction receipt.wait-effectstream-processed: waits until Effectstream has processed the resulting rollup block.
Customising the batcher
The four interfaces you'd implement, in order of frequency:
BlockchainAdapter: submit, wait for receipt, estimate fee, report chain name. New chains plug in here.BatcherStorage: persist + load inputs. The default isFileStorage(JSONL on disk); Postgres / Redis / S3 implementations are straightforward.BatchDataBuilder<T>: control how inputs are serialised into the bytes the adapter submits.- State-transition listeners: hook into
startup,batch:process:start,batch:submit,batch:confirmed,error, and others for metrics or custom behaviour.
Inside Effectstream
The batcher is the on-ramp between user wallets and Effectstream's state machine. Frontends sign inputs through @effectstream/wallets, POST them here, and wait on the confirmation level they need. On submission, @effectstream/sync's fetchers pick up the resulting on-chain transaction and the state machine (@effectstream/sm) processes the contained subunits in a per-block transaction.
Key exports
createNewBatcher(config, storage): build a batcher instance.BatcherConfig: configuration type. SeepollingIntervalMs,adapters,defaultTarget,batchingCriteria,confirmationLevel,enableHttpServer,port,enableEventSystem,namespace,batchBuilding.FileStorage(dir): default JSONL storage.- Adapters:
EffectstreamL2DefaultAdapter,EvmContractAdapter,MidnightAdapter,BitcoinAdapter. - Batcher operations:
runBatcher,batchInput,addStateTransition,gracefulShutdownOp,getPublicConfig,getBatchingStatus. - HTTP endpoints (when enabled):
POST /batch-input,GET /status,GET /health,POST /force-process.
Examples
End-to-end batcher flow:
e2e/evm/sync/batcher.test.ts.
Links
- Docs: https://effectstream.github.io/docs/packages/batcher
- Source: https://github.com/effectstream/effectstream/tree/main/packages/batcher
- Companion:
@effectstream/walletsfor the signing side.