Open-source project
ethereumjs/ethereumjs-monorepo avatar
ethereumjs/ethereumjs-monorepo

EthereumJS Monorepo: TypeScript Building Blocks for the Ethereum Execution Layer

Ethereum L1 Protocol Stack and EVM in TypeScript

2,756 stars853 forksTypeScriptLicense varies

At a glance

What is it?
The EthereumJS monorepo ships the execution layer as separate npm packages: VM, EVM, tx, block, mpt, statemanager, common. Here is what each one does, how to run a signed transaction, and where the modular approach costs you.
Who is it for?
Adopt EthereumJS when you need execution-layer primitives inside a JavaScript or TypeScript process: a wallet backend, a simulation service, a test harness that replays blocks, or tooling that has to run the same code in Node.js and the browser. Do not adopt it as a replacement for a full node if your requirement is peer-to-peer networking, RPC serving or archive-scale chain data; that is a different package set and a different operational burden.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 23 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the EthereumJS monorepo actually provides

The repository describes itself as "Modular TypeScript libraries for Ethereum execution-layer protocol work." That sentence is the whole product. Rather than one large client, the monorepo publishes a set of npm packages that each own one slice of the execution layer, and the README maps goals to packages directly: run a signed transaction, replay a block or build a block with @ethereumjs/vm; execute bytecode, custom opcodes or precompiles with @ethereumjs/evm; create, sign or parse transactions with @ethereumjs/tx; handle blocks, headers and withdrawals with @ethereumjs/block; keep persistent chain storage with @ethereumjs/blockchain; work with Merkle proofs and trie tooling through @ethereumjs/mpt; and read chain configuration and hardfork parameters from @ethereumjs/common.

The intended audience is developers writing software that has to reason about Ethereum state and rules without running a node. A wallet that needs to construct and sign a transaction, a service that simulates what a transaction would do before broadcasting it, a test suite that replays historical blocks, a browser application that has to verify a Merkle proof: these are the shapes of problem the package map is drawn around. The README also states that the libraries run in Node.js or the browser, are tree-shakeable, use Noble crypto by default, and are WASM-free unless you opt in. That last point matters for bundle size and for environments where loading a WASM binary is awkward.

The project is maintained by former members of the Ethereum Foundation JavaScript team and the broader Ethereum community, according to the README. The repository is not archived, and the last push was on 2026-09-18. Recent releases include @ethereumjs/[email protected], @ethereumjs/[email protected] and @ethereumjs/[email protected], all published on 2026-08-21, and the README notes that the active packages are published in sync on v10.

How the package layers fit together

The README includes a layered diagram of the packages, and the layering is the architecture. At the bottom sit codec and primitive packages: @ethereumjs/rlp for RLP encode and decode, and @ethereumjs/util for bytes, accounts, addresses and signatures. Above those, @ethereumjs/common holds chains, hardforks and EIP parameters, with @ethereumjs/genesis supplying genesis state for known chains. Then come the protocol types, @ethereumjs/tx and @ethereumjs/block, which know the shape of a transaction or a block and how to serialize it. State handling is a third band: @ethereumjs/mpt for the Merkle Patricia Trie and proofs, @ethereumjs/binarytree for the EIP-7864 binary tree, and @ethereumjs/statemanager for Merkle, RPC and binary-tree state backends. Execution sits on top, with @ethereumjs/evm as the interpreter and @ethereumjs/vm wiring execution to state and block rules.

This is why the README's decision flow matters more than any single package. If you only need to execute bytecode, the README points you at @ethereumjs/evm and its examples directory, and you never pull in block or blockchain machinery. If you need transaction and block rules, you start at @ethereumjs/vm. If you need chain storage or types, you start at @ethereumjs/block or @ethereumjs/blockchain. The dependency graph is meant to be entered at the shallowest point that solves your problem.

One consequence is that the state backend is a choice, not a given. The statemanager package exposes Merkle, RPC and binary-tree backends, which means the same VM code can run against an in-process trie or against a remote node over RPC. That is a real design decision with a real cost: an RPC-backed state manager turns every state read into a network round trip, so the execution speed you get depends on where the state lives, not on the interpreter.

Installing EthereumJS and running a signed transfer

The README's quick start installs the VM together with the packages it needs at the edges: common for chain and hardfork selection, tx for transaction construction, and util for byte and address helpers.

bash
npm install @ethereumjs/vm @ethereumjs/common @ethereumjs/tx @ethereumjs/util

The example that follows is adapted from packages/vm/examples/runTx.ts. It builds a Common instance for Mainnet at the Prague hardfork, creates a VM, funds an account directly in the state manager, signs a legacy transaction and runs it. The printed value should be 21000n, the intrinsic gas of a plain transfer.

ts
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
import { createLegacyTx } from '@ethereumjs/tx'
import {
  createAccount,
  createAddressFromPrivateKey,
  createZeroAddress,
  hexToBytes,
} from '@ethereumjs/util'
import { createVM, runTx } from '@ethereumjs/vm'

const common = new Common({ chain: Mainnet, hardfork: Hardfork.Prague })
const vm = await createVM({ common })

const senderKey = hexToBytes(`0x${'20'.repeat(32)}`)
const sender = createAddressFromPrivateKey(senderKey)
await vm.stateManager.putAccount(sender, createAccount({ nonce: 0n, balance: BigInt(1e18) }))

const tx = createLegacyTx({
  gasLimit: 21000n,
  gasPrice: 1_000_000_000n,
  value: 1n,
  to: createZeroAddress(),
}).sign(senderKey)

const res = await runTx(vm, { tx })
console.log(res.totalGasSpent) // 21000n

Two things about this snippet are worth noting before you copy it. The account is written straight into the state manager with putAccount, which works because the default VM state is in-memory; there is no funding step from a real chain. And the private key is a literal repeated byte pattern, which is fine for a local example and obviously not for anything else. The README also directs readers who only want bytecode execution to @ethereumjs/evm and its examples, and states that blob (EIP-4844) transactions need a separate KZG library, with setup covered in the tx README under KZG setup.

Where the modular design costs you

The trade-off is explicit in the package map: you assemble the stack yourself. A single install of @ethereumjs/vm does not give you persistent chain storage, peer networking or an RPC server. If your application needs to follow the chain, you are responsible for feeding it blocks and for choosing a state backend that can keep up. The statemanager README is where that decision lives, and the choice between an in-memory Merkle trie and an RPC backend changes the performance profile of everything above it.

Hardfork coverage is the second constraint, and the README states it plainly in a status table. Osaka is listed as mainnet today and stable on npm. Amsterdam is listed as in development, with a pointer to packages/vm/README.md#amsterdam-hardfork-experimental. If your work depends on Amsterdam-era behaviour, you are reading an experimental section of a package README, not a stable release note. That is a normal state of affairs for a library that tracks a moving protocol, but it means the hardfork you target determines whether you are on supported ground.

The third limitation is scope. This is an execution-layer library set. It does not present itself as a full node implementation for operators, and nothing in the README describes running a validator, serving public RPC or syncing mainnet as an end-user product. Teams that need a node should be looking at node software, not at a package that executes transactions inside their own process.

EthereumJS compared with other EVM implementations

The related searches around this project include Revm, Py evm and Yellowpaper, which is a fair signal of what people compare it against. The difference is language and integration surface rather than protocol behaviour. Revm is a Rust EVM; Py evm is a Python one. Both are execution engines you embed in a host program written in that language. EthereumJS is the same idea expressed in TypeScript, with the addition that the monorepo also publishes the surrounding protocol types (tx, block), the trie and state layers, and chain configuration as separate installable packages.

That packaging difference is the practical one. If your service is a Node.js process, pulling in a Rust engine means an FFI or subprocess boundary and a build step for a native artifact. EthereumJS stays inside the same runtime and the same type system as the rest of your code. The cost is that you inherit JavaScript's performance envelope for state-heavy work, and the README's own framing (tree-shakeable, WASM-free unless you opt in, browser-ready) is aimed at integration convenience rather than raw throughput. A team choosing between them is really choosing which side of that boundary they want their complexity on.

The Yellowpaper is not an alternative implementation at all; it is the specification these libraries are tested against. The README's phrase "spec-tested execution" points at that relationship, and it is the reason the hardfork status table exists: the specification moves, and the libraries track it.

Versioning, releases and licence questions

The README states that active packages are published in sync on v10, and points at scripts/release-npm.ts for the release process. The recent release list bears that out: @ethereumjs/[email protected], @ethereumjs/[email protected] and @ethereumjs/[email protected] were all published on 2026-08-21 within seconds of each other. For a consumer, synchronized versioning is convenient because you do not have to reason about which combination of package versions is compatible. It also means an upgrade touches several packages at once, and a fix in one package arrives with whatever else moved in the same release.

The root package.json is private and declares workspaces over packages/*, so the repository is a workspace root rather than a publishable artifact. It runs a preinstall check on the npm version and a postinstall that builds all workspaces, which means a fresh clone builds the whole monorepo before you can use it locally. The Dockerfile takes a different route: it copies prebuilt dist directories and package.json files for each package under packages/ into the image, rather than building inside it. That is a hint about how the project itself ships container images, and it implies the build happens before the image is assembled.

On licensing, the repository metadata does not state a licence, and the README does not name one either. Each package on npm carries its own terms, so the package you install is the place to look. I cannot tell you what those terms are from this material, and it would be wrong to guess. If your organisation has licence review requirements, resolve this before you build on the stack rather than after.

Editorial conclusion

Adopt EthereumJS when you need execution-layer primitives inside a JavaScript or TypeScript process: a wallet backend, a simulation service, a test harness that replays blocks, or tooling that has to run the same code in Node.js and the browser. Do not adopt it as a replacement for a full node if your requirement is peer-to-peer networking, RPC serving or archive-scale chain data; that is a different package set and a different operational burden. Before committing, verify the hardfork you target is listed as stable in the README table, check whether your transaction type pulls in a separate KZG library, and read the licence terms in the individual package you install, since the repository metadata does not state one.

Frequently asked questions

Is the Ethereum Virtual Machine the same as an Ethereum wallet?

No. The EVM is the execution component that runs bytecode, and in this project it is the @ethereumjs/evm package, the interpreter with opcodes and precompiles. A wallet is software that manages keys and builds transactions, and it might use @ethereumjs/tx and @ethereumjs/vm internally, but the EVM itself does not hold funds or keys.

Can you explain how the Ethereum Virtual Machine works in EthereumJS?

The README separates the interpreter from the surrounding machinery: @ethereumjs/evm is the EVM interpreter with opcodes and precompiles, while @ethereumjs/vm wires execution to state and block rules. The VM runs against a state backend chosen from the statemanager package, which offers Merkle, RPC and binary-tree backends.

What is Ethereum programmed in?

This project is written in TypeScript, as the repository's primary language and its npm package set indicate. The README describes the libraries as modular TypeScript packages that run in Node.js or the browser, using Noble crypto by default and no WASM unless you opt in. That says nothing about the Ethereum protocol itself, only about this implementation.

Official sources

  1. ethereumjs/ethereumjs-monorepo on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ethereumjs-ethereumjs-monorepo.svg)](https://hysenlabs.com/projects/ethereumjs-ethereumjs-monorepo)