Skip to main content

@effectstream/sync

Package: @effectstream/sync · Source

The blockchain-sync service inside an EffectStream node. Reads finalized blocks from every chain you've configured (EVM, Bitcoin, Cardano, Midnight, Avail, Celestia, NEAR…), normalizes them into a single rollup ordering, and stages the inputs the state machine consumes.

Install

bun add @effectstream/sync
# or
npm install @effectstream/sync

Usage

This package pairs with @effectstream/runtime, which boots sync as part of start(): it calls genSyncProtocols(...) against the syncProtocols section of your @effectstream/config, then drives the resulting fetcher + state pairs every block. As an app author you declare which protocols to sync in your config; everything else runs automatically.

If you're building a new chain integration, implement the sync-protocol interfaces in src/sync-protocols/.

Inside EffectStream

genSyncProtocols(config) is what the runtime calls during boot. It walks the syncProtocols section of your @effectstream/config, instantiates a fetcher for each chain (viem for EVM, UTXORpc for Cardano, the Midnight SDK for Midnight, etc.), and writes paginated blocks into PostgreSQL through @effectstream/db. The state machine then drains that queue.

import { genSyncProtocols } from "@effectstream/sync";
// inside the runtime startup path:
const protocols = await genSyncProtocols(config);
// protocols.parallelEvmRPC_fast.runOne() // poll one block

Key exports

  • genSyncProtocols(dbConn, syncInfo) — Effection generator that instantiates a runtime fetcher + state pair for every protocol in syncInfo (from config.syncProtocols). Called from the runtime's process-blocks loop.
  • AllSyncProtocols — union type covering every supported protocol; useful when authoring config that fans out.
  • ChainBlock, plus base Fetcher/State types from sync-protocols/base/ — the wire shape per chain.

Per-chain Fetcher / SyncState classes (EvmFetcher, BitcoinFetcher, MidnightFetcher, AvailFetcher, UtxoRpcFetcher, NtpFetcher, CelestiaFetcher, NearFetcher, and matching *SyncState classes) are exported but are internal to the factory wiring — application code drives them through genSyncProtocols rather than instantiating them directly. Reach for them only if you're writing a custom orchestration layer.

Examples

End-to-end sync test (boots a node, reads blocks, asserts the DB): e2e/evm/sync/.

Runnable: test/examples.test.ts.