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

ether-state

v0.2.1

Published

Library for syncing state from contracts.

Downloads

2

Readme

ether-state

A Library for syncing state from contracts. Trigger Ethereum contract calls whenever there is a new block, matching event or by time interval. ether-state bundles calls with Multicall2 to reduce the amount of RPC calls and tries to reduce the amount of event listeners needed for all actions.

Originally designed for managing state in Svelte Kit Ethers Template.

TODO

  • [ ] Start using Multicall3
  • [ ] Add more default actions and action generators

Basic Usage

import { getDefaultProvider, formatEther, Interface, id, Log } from 'ethers';
import { EtherState, Actions, TriggerType } from 'ether-state';

const IERC20 = new Interface([
	'function totalSupply() external view returns (uint256)',
	'function balanceOf(address) external view returns (uint256)',
	'event Transfer(address indexed from, address indexed to, uint256 value)',
]);

// Check totalSupply of DAI every block, check balance of every DAI recipient on Transfer event
const actions: Actions[] = [
	{
		trigger: {
			type: TriggerType.BLOCK
		},
		input: () => [],
		call: {
			target: () => '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI contract
			interface: IERC20,
			selector: 'totalSupply',
		},
		output: (returnParams) => {
			console.log('DAI Total Supply:', formatEther(returnParams[0]));
		},
	},
	{
		trigger: {
			type: TriggerType.EVENT,
			eventFilter: {
				address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
				topics: [id('Transfer(address,address,uint256)')]
			}
		},
		// Use the transfer recipient as the input param for balanceOf call
		input: (log: Log) => {
			const event = IERC20.decodeEventLog(
				'Transfer',
				log.data,
				log.topics
			);
			console.log(
				`${event.from} sent ${formatEther(event.value)} DAI to ${
					event.to
				}`
			);
			return [event.to];
		},
		call: {
			target: () => '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI contract
			interface: IERC20,
			selector: 'balanceOf',
		},
		output: (returnParams) => {
			console.log('Recipents balance is now: ', formatEther(returnParams[0]), ' DAI');
		},
	},
];

const provider = getDefaultProvider();
const etherState = new EtherState(actions, provider);

API

EtherState

Takes an array of Actions and manages calling contracts based on their triggers.

import { EtherState, Action, TriggerType } from 'ether-state';
import { getDefaultProvider } from 'ethers';

const provider = getDefaultProvider();
const actions: Action[] = [];

// Create instance
const etherState = new EtherState(actions, provider)

// Manually trigger contract calls to all actions with BLOCK trigger
etherState.update(TriggerType.BLOCK);

// Destory instance, turn off all event listeners
etherState.destroy();

Create new instance -new EtherState(actions: Action[], provider: Provider, options?: { customMulticallAddress?: string, populateTimeAndBlock?: boolean })

Manually Trigger Updates - EtherState.update(type: TriggerType.TIME | TriggerType.BLOCK) Triggers update calls to all Actions with trigger types of either TIME or BLOCK.

Stop Updates = EtherState.destroy() Removes all event listeners and stops all updates

Triggers

There are 3 trigger types, BLOCK, TIME and EVENT.

Time actions are triggered by setInterval, set the interval amount in ms. Event actions are triggered by a matching ethers' EventFilter, the Log from the event filter and block is passed to the inputs method of the action.

enum TriggerType {
	BLOCK,
	EVENT,
	TIME,
}

type BlockTrigger = { type: TriggerType.BLOCK }
type TimeTrigger = { type: TriggerType.TIME, interval: number }
type EventTrigger = { type: TriggerType.EVENT, eventFilter: EventFilter }

Actions

There are 3 variation of Action for the 3 different trigger types. They all include trigger, input, call and output with type specific parameters for the input and output functions.

The input function returns an array of input parameters for a contract function call. The output function gets passed the return value of the contract call as well as trigger specific data like the Log of an event triggered action. The call object defines the interface and function name to be called and a function that returns the contract address:

type ContractCall = {
	target: () => string
	interface: Interface
	selector: string
}

Variations of Actions

type BlockAction = {
	trigger: BlockTrigger
	input: (blockNumber: bigint) => Array<bigint | BytesLike | string | boolean>
	call: ContractCall
	output: (returnValues: Result, blockNumber: bigint) => unknown
}

type TimeAction = {
	trigger: TimeTrigger
	input: (currentTime: number) => Array<bigint | BytesLike | string | boolean>
	call: ContractCall
	output: (returnValues: Result, blockNumber: bigint) => unknown
}

type EventAction = {
	trigger: EventTrigger
	input: (log: Log, blockNumber: bigint) => Array<bigint | BytesLike | string | boolean>
	call: ContractCall
	output: (returnValues: Result, blockNumber: bigint, log: Log) => unknown
}