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

@warptoad/skinny-fat-imt-js

v0.0.8

Published

js library counter part of skinny-fat-imt contracts, used to sync the onchain tree client side

Downloads

442

Readme

@warptoad/skinny-fat-imt-js

JS library counterpart of the skinny-fat-imt contracts, used to sync the onchain tree client side.

Reads a contract's LeanIMT state over viem — from storage or from events, whichever the contract exposes — and keeps a local mirror in sync.

TODO

Decide where to keep the create2 artifacts and all that byte code. Using this package in frontend also downloads all that? Probably less of an issues when using bundlers?

Install

pnpm add @warptoad/skinny-fat-imt-js viem

viem is a peer dependency, so it isn't installed for you. That's deliberate: two copies of viem in one tree produce PublicClient types that look identical but don't unify, and you'd get baffling assignability errors passing your client into Trees.

ESM only, Node >= 22.

Usage

import { Trees } from "@warptoad/skinny-fat-imt-js";
import { createPublicClient, http } from "viem";

const client = createPublicClient({ transport: http(rpcUrl) });
const trees = new Trees(contractAddress, client);

// full node: read the trees straight from contract storage
const synced = await trees.sync([treeId]);

// archive node: replay the contract's events, discovering treeIds as it goes
const all = await trees.sync([], { fullNodeMode: false, autoDiscovery: true });

fullNodeMode (default true) reads current storage, so a plain full node is enough — that's what the name means. It's the cheap path, but it only sees the tree as it is now.

Turning it off replays events, which needs a provider that still has the logs. Recent history is fine on a full node; anything older than roughly a year wants an archive node, and autoDiscovery scans all the way back to the deployment block, so it always does. In exchange you get history: syncToRoot to reconstruct an old root, and treeId discovery, neither of which storage reads can do.

syncTreesStorage / syncTreesEvent are the two paths directly if you'd rather not go through sync.

The event-scanning helpers are a separate entry point, so you can chunk getLogs calls against any contract without pulling in the tree machinery:

import { queryEventInChunks, queryMultiEventsInChunks, minBigInt } from "@warptoad/skinny-fat-imt-js/event-scanning";

| Entry point | Exports | | --- | --- | | @warptoad/skinny-fat-imt-js | Trees, identifyTree, getEventFilter, copyTree, ERC165_IDS, the ABIs, getInterfaceId, DEPLOY_BLOCK, … | | @warptoad/skinny-fat-imt-js/event-scanning | queryEventInChunks, queryMultiEventsInChunks, minBigInt, EventLog, PostQueryEventFilter | | @warptoad/skinny-fat-imt-js/create2 | makeCreate2, deployCreate2, predictCreate2Address, create2MiningTarget, verifyOnEtherscan, verifyOnSourcify, matchesOnchainBytecode | | @warptoad/skinny-fat-imt-js/create2/evm-artifacts/<Contract> | The frozen Create2Artifact JSON for each shipped IMT library |

Deterministic CREATE2 for any contract

/create2 is a general toolkit, independent of the IMT libraries — point it at any Hardhat 3 contract:

const artifact = await makeCreate2("MyContract", { constructorArgs: [...] });
const { address } = await deployCreate2({ artifact, salt, walletClient, publicClient });
await verifyOnEtherscan({ artifact, address, chainId, apiKey });

makeCreate2 freezes the init code together with the solc input that produced it, so nothing downstream recompiles and the address holds on every chain. The IMT libraries ship pre-frozen, so bringing them to a new chain needs no compiler:

import artifact from "@warptoad/skinny-fat-imt-js/create2/evm-artifacts/SkinnyIMTPoseidon2Read" with { type: "json" };

See CREATE2.md.

Development

pnpm install
pnpm compile     # hardhat compile, then regenerate src/abis.ts
pnpm test        # hardhat test
pnpm typecheck   # tsc over src + test + scripts + hardhat.config.ts
pnpm build       # emit dist/ from src/ via tsconfig.build.json

About src/abis.ts

src/abis.ts is generated and committed. scripts/genAbis.ts copies nine ABIs out of hardhat's artifacts/ into as const tuples.

The library deliberately does not import artifacts/ directly, even though that's the obvious thing to do. artifacts/ is gitignored, ~13MB, and hardhat's generated artifacts.d.ts files end with:

declare module "hardhat/types/artifacts" { interface ArtifactMap { … } }

Shipping that would make the published typings require hardhat to be resolvable in the consumer's project, and would inject our contract names into their ArtifactMap. Baking the ABIs in keeps full viem inference with no hardhat dependency at all — hardhat stays a devDependency and never enters a consumer's graph.

Whenever the contracts change, run pnpm compile and commit the resulting src/abis.ts diff. Nothing regenerates it automatically at publish time — that's on purpose, so npm publish can never silently rewrite source.

deployment

pnpm hardhat gen-artifact-create2 --network sepolia;
pnpm hardhat mine-create2 --network sepolia --zeros 6;
pnpm hardhat deploy-create2 --network sepolia;
pnpm hardhat verify-create2 --network sepolia;

Publishing to npm

One-time setup

Log in. The @warptoad scope must already exist on npm and your account must be able to publish to it.

npm login
npm whoami          # confirm the right account

publishConfig.access is already set to public in package.json. Without it npm assumes scoped packages are private and rejects the publish on a free account.

The package name is @warptoad/skinny-fat-imt-js, all lowercase — npm rejects uppercase in package names, so skinny-fat-IMT-js is not a valid name.

Publishing a version

  1. Make sure the ABIs are current and everything passes:

    pnpm compile     # only needed if the contracts changed
    pnpm typecheck
    pnpm test

    Commit any src/abis.ts diff before continuing.

  2. Bump the version. This writes package.json, commits, and tags:

    npm version patch    # or: minor / major
  3. Check what will actually ship before you ship it:

    npm pack --dry-run

    Expect dist/**, src/**, README.md, CREATE2.md, LICENSE, package.json — about 50KB. If you see artifacts/ or a multi-MB tarball, something is wrong with files in package.json.

  4. Publish. prepublishOnly runs pnpm build (a clean rm -rf dist then tsc -p tsconfig.build.json), so dist/ is always rebuilt from current source:

    npm publish
  5. Push the commit and tag:

    git push --follow-tags

Publishing a prerelease

To try a version out without moving the latest tag:

npm version prerelease --preid=rc     # 0.0.1 -> 0.0.2-rc.0
npm publish --tag next

Consumers get it with pnpm add @warptoad/skinny-fat-imt-js@next; plain pnpm add @warptoad/skinny-fat-imt-js still resolves to the last stable release.

Verifying the published package

mkdir /tmp/check && cd /tmp/check && pnpm init
pnpm add @warptoad/skinny-fat-imt-js viem
node -e "import('@warptoad/skinny-fat-imt-js').then(m => console.log(Object.keys(m)))"

Notes

  • You cannot republish a version. Once 0.0.1 is out, 0.0.1 is taken forever, even if you unpublish. Bump and move on.
  • npm unpublish is only allowed within 72 hours, and only if nothing depends on it. Use npm deprecate instead for anything older.
  • If publish fails with 402 Payment Required, publishConfig.access got lost — scoped packages need "access": "public".
  • If it fails with 404, you're either not logged in or not a member of the @warptoad scope.

Deployed addresses sepolia

SkinnyIMTPoseidon2WriteStorage 0x00000066C760D24272a9E8A424EF7233A0F0da83
SkinnyIMTPoseidon2WriteEvent   0x000000D74387530f88b9Cf1A23CDf16fdDC01bf6
SkinnyIMTPoseidon2Read         0x000000A3a94867DD396753dEFABF989246211329
FatIMTPoseidon2WriteStorage    0x000000F35EFB6F537d3663CF263c04f467f48c73
FatIMTPoseidon2WriteEvent      0x00000012de70B336818C26517A4B2d7c0B949C7b
FatIMTPoseidon2Read            0x000000FF3329863F1Eb55773aDEF01863791370