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

@xlabs-xyz/fork-svm

v5.1.0

Published

Solana fork testing utility based on LiteSVM

Readme

@xlabs-xyz/fork-svm

npm version

Anvil for Solana: a local SVM fork with lazy account fetching. Built on LiteSVM and @solana/kit.

Why?

Testing Solana programs is painful. Your options:

  • solana-test-validator
    • Slow startup
    • Separate process
    • Must pre-declare every account to clone
    • State doesn't persist nicely between test runs
  • LiteSVM
    • Fast and in-process
    • But Rust-first; Node bindings are outdated and @solana/web3.js-based
    • No transparent forking
    • Manual account setup for everything
  • Just use devnet
    • Slow, flaky, funds-limited, rate-limited
    • No state manipulation

Meanwhile, EVM developers have had Anvil and Hardhat for years - local nodes that lazily fork state from mainnet/testnet, let you manipulate time, and provide a drop-in RPC.

This package brings that experience to Solana:

const fork = new ForkSvm({ url: "https://api.mainnet-beta.solana.com" });

// Load the program you're testing
fork.addProgramFromFile(programId, "./target/deploy/my_program.so");

// Read and modify any account
const acc = await fork.getAccount(someAddress);
fork.setAccount(someAddress, { ...acc, data: modifiedData });

// Accounts are fetched lazily - no upfront cloning
// Works with both legacy and v0 transactions (including ALTs)
const tx = await buildSomeTransaction();
await fork.sendTransaction(tx); // fetches what it needs automatically

// Or use as a drop-in RPC
const rpc = fork.createForkRpc();
await rpc.getBalance(myWallet).send(); // works with existing code ...
await myProgramSdk.doSomething(rpc, ...); //... including your contract's sdk

// Snapshot and restore
const snapshot = fork.save();
// ... run destructive tests ...
fork.load(snapshot);

// Persist to disk for reproducible CI
await writeToDisc("./fixtures/my-test", snapshot);

Quick Start

import { ForkSvm } from "@xlabs-xyz/fork-svm";

// Create a network fork
const fork = new ForkSvm({ url: "https://api.mainnet-beta.solana.com" });

// Or create an empty local SVM (no upstream)
const localSvm = new ForkSvm();

// Fund a test wallet
const wallet = address("...");
await fork.airdrop(wallet, 10_000_000_000n); // 10 SOL - see utils below for nicer amount spec

// Send transactions
await fork.sendTransaction(tx).then(meta => {
  console.log(meta.logs()); // transaction logs
  console.log(meta.computeUnitsConsumed()); // CU used
});

// Get account data (fetched from upstream if not cached)
const account = await fork.getAccount(someAddress);

Core API

Constructor

new ForkSvm({
  url?:                 string,  // RPC URL to fork from (undefined = empty local SVM)
  withDefaultPrograms?: boolean, // include SPL Token, ATA, etc. (default: true)
  withSysvars?:         boolean, // include clock, rent, etc.    (default: true)
  withBuiltins?:        boolean, // include system program, etc. (default: true)
});

Transactions

// Send a transaction (modifies state)
const meta = await fork.sendTransaction(tx);
meta.signature();           // transaction signature
meta.logs();                // program logs
meta.computeUnitsConsumed();
meta.returnData();          // program return data
meta.innerInstructions();   // CPI instructions

// Simulate without modifying state
const simMeta = await fork.simulateTransaction(tx);

// Retrieve a previously sent transaction
const retrieved = fork.getTransaction(signatureBytes);

Account Management

// Get account(s) - fetches from upstream if not cached
const acc = await fork.getAccount(address);
const accs = await fork.getAccount([addr1, addr2, addr3]);

// Manually set account state
fork.setAccount(address, {
  owner: programId,
  lamports: 1_000_000_000n,
  data: new Uint8Array([...]),
  executable: false,
  space: 100n,
});

// Airdrop SOL (creates account if needed)
await fork.airdrop(address, lamports);

// Load a program from bytes or file
fork.addProgram(programId, programBytes);
fork.addProgramFromFile(programId, "./target/deploy/my_program.so");

Clock

// Get full clock state
const clock = fork.getClock();
// => { timestamp, slot, epoch, epochStartTimestamp, leaderScheduleEpoch }

// Set any subset of clock fields
fork.setClock({ timestamp: new Date("2025-06-01") });
fork.setClock({ slot: 300_000_000n });
fork.setClock({ timestamp: new Date("2025-06-01"), slot: 300_000_000n });

// Convenience shortcuts
fork.latestTimestamp(); // Date
fork.latestSlot();      // bigint

// Sync to current network time (requires RPC)
await fork.advanceToNow();

// Blockhash management
fork.latestBlockhash(); // current blockhash
fork.expireBlockhash(); // expire current, generate new

Snapshots

// Save current state
const snapshot = fork.save();

// Restore state
fork.load(snapshot);

// Create new fork from snapshot
const newFork = ForkSvm.load(snapshot);

Disk Persistence

Persist snapshots to disk for reproducible tests that don't depend on network state:

import { writeToDisc, readFromDisc } from "@xlabs-xyz/fork-svm";

// Save a snapshot after fetching the accounts you need
await writeToDisc("./fixtures/my-scenario", fork.save());

// Later (or in CI), load from disk - no RPC calls needed
const snapshot = await readFromDisc("./fixtures/my-scenario");
const fork = ForkSvm.load(snapshot);

This is invaluable for:

  • Reproducible CI - tests run against a fixed snapshot, not the live network
  • Offline development - work without network access once you've captured state
  • Debugging - save state at a specific point and replay transactions against it

Drop-in RPC

For code that expects a standard Solana RPC, create a compatible interface:

const rpc = fork.createForkRpc();

// Works with existing RPC-based code
const balance = await rpc.getBalance(address).send();
const account = await rpc.getAccountInfo(address, { encoding: "base64" }).send();
const blockhash = await rpc.getLatestBlockhash().send();

// Transactions go through the fork
await rpc.sendTransaction(wireTransaction, { encoding: "base64" }).send();

Supported methods: getAccountInfo, getMultipleAccounts, getBalance, getLatestBlockhash, sendTransaction, simulateTransaction, getTransaction.

Utilities

createCurried

Creates a set of convenience functions that work with the fork's RPC and optionally integrate with @xlabs-xyz/amount for type-safe, human-readable amounts (see @xlabs-xyz/common for Sol, sol, and usdc):

import { createCurried } from "@xlabs-xyz/fork-svm";
import { Sol, sol, usdc } from "@xlabs-xyz/common";

const {
  minimumBalanceForRentExemption,
  getAccountInfo,
  getDeserializedAccount,
  getMint,
  getTokenAccount,
  getBalance,
  getTokenBalance,
  airdrop,
  createAccount,
  createAta,
  createTx,
  sendTx,
  createAndSendTx,
} = createCurried(fork, Sol); // Sol kind for typed amounts (optional)

// Now with nicer ergonomics
await airdrop(wallet, sol(0.1));
const balance = await getBalance(wallet); // Amount<Sol>
const ata = createAta(wallet, usdcMint, usdc(100));
await createAndSendTx(instructions, feePayer, additionalSigners, alts);

assertTxSuccess

Unwraps a transaction result, failing with a clear message if it errors:

import { assertTxSuccess } from "@xlabs-xyz/fork-svm";

const meta = await assertTxSuccess(fork.sendTransaction(tx));
// throws with logs if tx fails

Comparison

| Feature | ForkSvm | solana-test-validator | LiteSVM (Node) | |--------------------|---------|-----------------------|----------------| | In-process | ✅ | ❌ | ✅ | | Lazy forking | ✅ | ❌ (explicit --clone) | ❌ | | State manipulation | ✅ | ❌ | ✅ | | Snapshots | ✅ | ❌ | ❌ | | Drop-in RPC | ✅ | ✅ | ❌ | | @solana/kit | ✅ | N/A | ❌ (web3.js) |

Notes

LiteSVM

This package includes a modified copy of LiteSVM's Node.js bindings (in src/liteSvm/). The upstream npm package was both outdated (crashes on newer Solana programs) and built on the legacy @solana/web3.js. The version here uses freshly built binaries and has been ported to @solana/kit.

Binaries for macOS (ARM64) and Linux (x64) are included. For other platforms, run scripts/build.sh (requires yarn and Rust).