npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@symbioticfi/relay-stats-ts

v0.3.1

Published

TypeScript library for deriving validator sets from Symbiotic network contracts

Readme

@symbioticfi/relay-stats-ts

npm version License: MIT Node.js Version

TypeScript utilities for deriving Symbiotic validator-set data from on-chain contracts. The library mirrors the Go reference implementation and exposes helpers for SSZ encoding, MiMC hashing, and aggregator extra data generation.

Installation

Local installation

git clone https://github.com/symbioticfi/relay-stats-ts.git
cd relay-stats-ts
pnpm install            # install dependencies
pnpm run build          # compile TypeScript to dist/

You can now import from the src/ or freshly built dist/ folders locally, or run the example script (see examples/README.md).

From npm

pnpm add @symbioticfi/relay-stats-ts

Requires Node.js 18 or newer.

Quick Start

Create a deriver that talks to the ValSet driver and fetch the current validator set:

import { ValidatorSetDeriver } from '@symbioticfi/relay-stats-ts';

const deriver = await ValidatorSetDeriver.create({
    rpcUrls: ['https://ethereum.publicnode.com'],
    driverAddress: {
        chainId: 1,
        address: '0xDriverAddress',
    },
});

const validatorSet = await deriver.getCurrentValidatorSet();
console.log(`Epoch: ${validatorSet.epoch}`);
console.log(`Active validators: ${validatorSet.validators.filter(v => v.isActive).length}`);
console.log(`Settlement status: ${validatorSet.status}`);
console.log(`Integrity: ${validatorSet.integrity}`);

Aggregator extra data

const extraData = await deriver.getAggregatorsExtraData('zk');
extraData.forEach(({ key, value }) => console.log(key, value));

Pass 'simple' for simple mode or provide custom keyTags when you need non-default key selection.

Single-call epoch snapshot

getEpochData pulls the validator set, network metadata, optional log event, and aggregator extras in one request:

const snapshot = await deriver.getEpochData({
    epoch,
    finalized: true,
    includeNetworkData: true,
    includeValSetEvent: true,
});

console.log(snapshot.validatorSet.status);
console.log(snapshot.networkData?.address);
console.log(snapshot.aggregatorsExtraData?.length ?? 0);
console.log(
    snapshot.settlementStatuses?.map(s => ({
        chainId: s.settlement.chainId,
        committed: s.committed,
    }))
);
console.log(
    snapshot.valSetEvents?.map(entry => ({
        chainId: entry.settlement.chainId,
        hasEvent: Boolean(entry.event),
    }))
);

Batch epoch snapshots

getEpochsData returns the same snapshot shape for an epoch range in one call (results are returned in ascending order):

const snapshots = await deriver.getEpochsData({
    epochRange: { from: 1, to: 3 },
    finalized: true,
    includeNetworkData: true,
    includeValSetEvent: true,
});

snapshots.forEach(snapshot => {
    console.log(snapshot.epoch, snapshot.validatorSet.status);
});

Batch helpers use multicall to minimize RPCs when available.

Aggregator extra data returned by getEpochData automatically uses the network configuration's verificationType (simple vs zk). Provide aggregatorKeyTags only when you need to override the defaults coming from the config.

See displayEpochSnapshot in examples/example.ts for a full walkthrough that prints the combined response.

Range helpers

For range-style reads (status, settlement logs, timings), use the batch APIs:

const epochRange = { from: 10, to: 12 };

const statuses = await deriver.getValSetStatuses(epochRange, true);
statuses.forEach(({ epoch, status }) => {
    console.log(epoch, status.status, status.integrity);
});

const logEvents = await deriver.getValSetLogEventsForEpochs({
    epochRange,
    finalized: true,
});
logEvents.forEach(({ epoch, logs }) => {
    console.log(
        epoch,
        logs.map(log => log.committed)
    );
});

const [starts, durations] = await Promise.all([
    deriver.getEpochStarts(epochRange, true),
    deriver.getEpochDurations(epochRange, true),
]);
console.log(starts, durations);

Validator set events

Validator-set commitment events expose on-chain metadata (block number, block timestamp, transaction hash) together with the parsed header. The deriver only attempts to load the event once the validator set status is committed, so pending epochs return null without additional RPC calls:

const events = await deriver.getValSetLogEvents({ epoch, finalized: true });

events.forEach(({ settlement, committed, event }) => {
    console.log(`Settlement ${settlement.address} committed=${committed}`);
    if (event) {
        console.log('  kind:', event.kind);
        console.log('  blockTimestamp:', event.blockTimestamp);
        console.log('  txHash:', event.transactionHash);
    }
});

When you only need status data without retrieving logs, call:

const settlements = await deriver.getValSetSettlementStatuses({ epoch });
settlements.forEach(({ settlement, committed }) => {
    console.log(`Settlement ${settlement.address} committed=${committed}`);
});

getEpochData now mirrors this behaviour: when includeValSetEvent is true, the response includes settlementStatuses alongside valSetEvents, containing entries for every settlement and returning logs only for those that are already committed.

Internally the library batches settlement reads with Multicall3 when available.

Caching

ValidatorSetDeriver accepts any cache that conforms to the CacheInterface and only persists finalized data. Cache entries are namespaced by epoch and a string key, allowing multiple values per epoch. The deriver keeps a FIFO list of cached epochs and evicts them with cache.clear(epoch) once maxSavedEpochs is exceeded; non-epoch data (like network metadata) is stored under a persistent epoch sentinel. Implement the interface to integrate Redis, in-memory caches, or other stores:

import type { CacheInterface } from '@symbioticfi/relay-stats-ts';

class MapCache implements CacheInterface {
  private buckets = new Map<number, Map<string, unknown>>();

  async get(epoch: number, key: string) {
    return this.buckets.get(epoch)?.get(key) ?? null;
  }

  async set(epoch: number, key: string, value: unknown) {
    let bucket = this.buckets.get(epoch);
    if (!bucket) {
      bucket = new Map();
      this.buckets.set(epoch, bucket);
    }
    bucket.set(key, value);
  }

  async delete(epoch: number, key: string) {
    const bucket = this.buckets.get(epoch);
    if (!bucket) return;
    bucket.delete(key);
    if (bucket.size === 0) {
      this.buckets.delete(epoch);
    }
  }

  async clear(epoch: number) {
    this.buckets.delete(epoch);
  }
}

const deriver = await ValidatorSetDeriver.create({
  rpcUrls: ["..."],
  driverAddress: {...},
  cache: new MapCache(),
});

API Highlights

All exports live under @symbioticfi/relay-stats-ts. Key entry points:

  • ValidatorSetDeriver.create(config) – initialize clients (one per chain) and validate required RPC coverage.
  • getEpochData({ epoch?, finalized?, includeNetworkData?, includeValSetEvent?, aggregatorKeyTags? }) – single snapshot with validator set, optional network metadata, aggregator extras, settlement statuses, and per-settlement log events.
  • getEpochsData({ epochRange?, finalized?, includeNetworkData?, includeValSetEvent?, aggregatorKeyTags? }) – batch snapshot (array) in ascending order with the same shape as getEpochData.
  • getValidatorSet(epoch?, finalized?) / getNetworkConfig(epoch?, finalized?) / getNetworkData(settlement?, finalized?) – granular primitives backing the combined call, plus batch variants getValidatorSets(epochRange?) and getNetworkConfigs(epochRange?).
  • getEpochStart(epoch) / getEpochDuration(epoch) – driver timing helpers, plus batch variants getEpochStarts(epochRange) and getEpochDurations(epochRange).
  • getValSetStatus(epoch) / getValSetSettlementStatuses({ epoch?, settlements?, finalized? }) / getValSetLogEvents({ epoch?, settlements?, finalized? }) – settlement state and events, plus batch variants getValSetStatuses(epochRange), getValSetSettlementStatusesForEpochs(...), and getValSetLogEventsForEpochs(...).
  • getAggregatorsExtraData(mode, keyTags?, finalized?, epoch?) / getAggregatorsExtraDataForEpochs({ epochRange?, mode, keyTags?, finalized? }) – manual access to simple/zk aggregator payloads.
  • Utilities for downstream consumers: getTotalActiveVotingPower, getValidatorSetHeader, abiEncodeValidatorSetHeader, hashValidatorSetHeader, getValidatorSetHeaderHash.
  • Low-level helpers are re-exported: buildSimpleExtraData, buildZkExtraData, and the SSZ encoding utilities (serializeValidatorSet, getValidatorSetRoot, etc.).

Refer to src/types.ts for full type definitions.

Example script

See examples/README.md for step-by-step instructions on running the demonstration script against your own RPC endpoints.

Development

pnpm install
pnpm run check

License

MIT