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

@johnhenry/raijin-mempool

v0.1.1

Published

Transaction mempool with fee-based ordering for the Raijin mesh rollup

Readme

@johnhenry/raijin-mempool

npm version license

Transaction mempool with fee-based ordering and eviction for the Raijin mesh rollup — a browser-native rollup framework where the users ARE the validators.

Accepts transactions, verifies signatures through an injected verifier, deduplicates by sender+nonce, orders by fee for block building, evicts the lowest-fee transaction when full, and optionally gossips accepted transactions to peers.

Install

npm install @johnhenry/raijin-mempool

The fee convention — read this first

defaultFeeExtractor reads the first 8 bytes of tx.data as a big-endian uint64 fee (shorter data ⇒ fee 0n). Two traps hide in that sentence:

  1. It collides with the state machine's convention. @johnhenry/raijin-core's StateMachine treats tx.data[0] as the transaction type. A data layout can't serve both defaults at once — if your transactions carry typed data, supply your own FeeExtractor (e.g. (tx) => tx.value for tip-style fees).
  2. The config doc-comment in older builds says the default is tx.value. It is not. The default is the 8-byte data prefix; verify with defaultFeeExtractor(tx) if in doubt.

This pool is the one @johnhenry/raijin-validator runs. It used to ship a second, simpler class of the same name; it no longer does, and Mempool imported from either package is now this implementation — see "Which mempool am I holding?" below.

Quick start

import { Mempool, defaultFeeExtractor } from '@johnhenry/raijin-mempool'

const pool = new Mempool({
  verifier: async (tx) => myVerifySignature(tx), // required
  maxSize: 4096,                                 // default 4096
  feeExtractor: (tx) => tx.value,                // optional — see trap above
  gossip: { broadcast: (tx) => channel.send(tx) }, // optional
})

pool.onDropped((tx, reason) => console.log('dropped:', reason))
// reasons: 'duplicate' | 'invalid-signature' | 'pool-full' | 'evicted'

await pool.submit(tx)          // true if accepted (also gossiped), false if dropped
const forBlock = pool.pendingForProposer(100) // top-100 by fee

API

Mempool

new Mempool(config: MempoolConfig) — config.verifier is required; maxSize, feeExtractor, gossip optional.

| Member | Purpose | | --- | --- | | submit(tx): Promise<boolean> | Dedup (sender+nonce) → verify signature → evict-or-reject if full → accept + gossip. | | remove(tx): boolean / removeBatch(txs): number | Drop by sender+nonce identity (e.g. after block inclusion). | | pending(): Transaction[] | All transactions, fee-descending, nonce-ascending on ties. | | pendingForProposer(limit?): Transaction[] | The same ordering, truncated to limit. | | has(tx) / hasNonce(sender, nonce) | Membership checks by sender+nonce. | | hasCapacityFor(tx): boolean | Cheap backpressure check, no signature verification: true while there is room, or when tx out-bids the lowest pending fee (so submit would evict). Added in 0.1.0 for ValidatorNode gossip. | | size | Current count. | | onAccepted(handler) / onDropped(handler) | Event hooks; onDropped receives a reason string. |

Eviction rule: when the pool is full, an incoming transaction must have a strictly higher fee than the current lowest to displace it. Equal fee ⇒ dropped as 'pool-full'. The displaced transaction is emitted as 'evicted'.

Identity is sender+nonce, not content: a second transaction from the same sender with the same nonce is a 'duplicate' even if its fee is higher — there is no replace-by-fee.

orderByFee(txs, feeExtractor): Transaction[]

Pure sorting helper (fee descending, nonce ascending on ties). Returns a new array; usable without a Mempool.

defaultFeeExtractor(tx): bigint

The 8-byte big-endian data prefix described above.

Types

MempoolConfig, FeeExtractor ((tx) => bigint), TransactionVerifier ((tx) => Promise<boolean>), GossipTransport ({ broadcast(tx) }), MempoolEvents.

Using it as a block builder's source

The intended consumption pattern — take the top of the pool, build, then prune exactly what was included:

const txs = pool.pendingForProposer(maxTxPerBlock) // top-N by fee
const block = await buildBlock(txs)                // your producer
// …after the block finalizes:
pool.removeBatch(block.transactions)               // prune by sender+nonce

pending() returns a fresh array each call, so it's safe to build from while submit()s keep arriving — but the pool contents can change between pendingForProposer() and removeBatch(), which is fine: removeBatch returns how many were actually removed, and transactions that arrived meanwhile simply wait for the next block.

Which mempool am I holding?

One implementation, two import paths. @johnhenry/raijin-validator re-exports this class:

import { Mempool } from '@johnhenry/raijin-mempool'   // same class
import { Mempool } from '@johnhenry/raijin-validator' // as this one

This was not always true, and older documentation says so: raijin-validator used to define its own FIFO Mempool with no signature verification that threw when full, and ValidatorNode used that one. It doesn't exist any more. ValidatorNode now constructs this pool, with maxSize: maxMempoolSize and a verifier built from the node's identity.verify — the same SignatureVerifier the state machine uses — so a transaction with a bad signature is rejected at submitTransaction() rather than becoming a revert receipt one block later.

Fee ordering is therefore active inside a validator by default. The one thing to watch is the fee convention above: defaultFeeExtractor reads tx.data's first 8 bytes, which collide with the state machine's type byte, so supply your own FeeExtractor if your transactions carry typed data.

Provenance

Previously published unscoped as raijin-mempool: 0.0.1 (initial release, 2026-03-15), then 0.0.2 (2026-07-16 — published with a broken build, because the release workflow used plain npm publish, which does not rewrite workspace:* internal dependency specifiers into real resolved versions; that tarball was unpublished), then 0.0.3 (2026-07-16, same day — republished correctly via pnpm publish, the last unscoped version, live until this move).

Moved into the @johnhenry npm scope and restarted at 0.0.0 — no functional changes in the move. The workspace:* publish bug can no longer recur: the monorepo has since converted to npm workspaces with real semver ranges, and releases go out via npm publish --workspaces. See the root CHANGELOG for the full project history.

License

MIT