Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Tevm Architecture Overview

Tevm's architecture is modular, extensible, and compatible with the broader JavaScript ecosystem.

For the native ZEVM client's ownership boundary between ZEVM, Voltaire, and Guillotine Mini, see ZEVM's Architecture and Upstream Ownership docs. Tevm's boundary is different: Tevm owns the JavaScript APIs, transports, package facades, and integration behavior described below while using ZEVM-backed primitives where noted.

Design Philosophy: Objects and Actions

Tevm separates Objects (stateful components) from Actions (pure functions over them). This viem-inspired pattern enables tree-shaking and a composable API, and Tevm follows it internally for maximum viem compatibility.

Objects — stateful components that encapsulate data:

  • TevmNode — the core Node interface
  • Evm — the Ethereum Virtual Machine
  • StateManager — manages blockchain state
  • Blockchain — handles blocks and chain state

Actions — pure functions taking an object as their first parameter:

  • Tree-shakable for minimal bundle size
  • Single-purpose with clear input/output
  • Composable for complex operations
  • Can be imported individually

Example: Using an Action

import { createTevmNode, PREFUNDED_ACCOUNTS } from "tevm";
import { getAccountHandler } from "tevm/actions"; 
 
const node = createTevmNode(); 
 
const getAccount = getAccountHandler(node); 
 
const account = await getAccount({

  address: PREFUNDED_ACCOUNTS[0].address,
});
 
console.log(account.balance);

This lets you import only the actions you need, create specialized handlers, and follow a consistent interface.

Client Options: Convenience vs. Tree Shaking

Tevm offers two main approaches:

MemoryClient: All-in-One
import { createMemoryClient, http } from 'tevm'
 
// Create a client with all actions pre-attached
const client = createMemoryClient({
  fork: {
    transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
  }
})
 
// Standard viem actions
const code = await client.getContractCode({
  address: '0x1234567890123456789012345678901234567890'
})
 
// Tevm-specific actions (prefixed with 'tevm')
const state = await client.tevmDumpState()
const balance = await client.getBalance({
  address: '0x1234567890123456789012345678901234567890'
})

MemoryClient: ✅ easy to start · ✅ all methods available · ✅ less code · ❌ larger bundle

Tree-Shakable Actions: ✅ smallest bundle · ✅ only what you use · ✅ works with code-splitting · ❌ more verbose imports

For more on tree-shakable actions, see the viem docs on tree-shaking.

Core Architecture Components

VM

The most important component, made up of the ZEVM-backed interpreter facade, StateManager, Blockchain, Common, TxPool, and ReceiptsManager. The VM runs transactions and builds blocks, coordinating state through its subcomponents.

It is NOT recommended you use the VM directly except for advanced use cases — use the viem API instead.

Interpreter (EVM)

Runs EVM bytecode with full support for active hardfork opcodes, built-in precompiles, and Tevm custom precompiles. Tevm's public EVM facade is backed by @evmts/zevm/evm, wrapped with Tevm-specific state, tracing, predeploy, logging, and profiling hooks.

// Access via
const evm = (await node.getVm()).evm;
 
// Features
evm.events.on("step", (data, next) => {
  console.log(`Opcode: ${data.opcode.name}`);
  next();
});
 
// Control execution gas limits, precompiles, etc.

For the most minimal way to execute bytecode, the EVM facade with the common and stateManager modules can be a good fit. For most use cases, prefer the viem API or MemoryClient.

The EVM wraps ZEVM's JavaScript/Wasm-compatible EVM primitives rather than the older pre-ZEVM implementation, keeping Tevm's low-level types aligned with @evmts/zevm while preserving the higher-level viem-style API.

State Manager

Maintains account balances, contract code, and storage state with forking capability from live networks. Tevm's custom state layer built around ZEVM-compatible account, trie, and utility primitives.

// Access via
const stateManager = (await node.getVm()).stateManager;
 
// Features
await stateManager.putAccount(address, account);
await stateManager.getAccount(address);
await stateManager.getStorage(address, key);
await stateManager.checkpoint(); // Create state snapshot
await stateManager.revert(); // Revert to the latest checkpoint

The StateManager keeps low-level methods Tevm's VM needs while adding Tevm-specific behavior: fork-backed reads, snapshots, and state dumping.

Blockchain

Manages blocks, chain state, forked block lookup, and block production (auto, interval, manual mining).

// Access via
const blockchain = (await node.getVm()).blockchain;
 
// Features
await blockchain.getBlock(blockHash);
await blockchain.getBlockByNumber(blockNumber);
await blockchain.putBlock(block);
await blockchain.getLatestBlock();

A Tevm implementation backed by Tevm block types and ZEVM-compatible primitives. Not recommended to use directly — prefer viem actions, MemoryClient methods, or JSON-RPC unless you're building Tevm extensions.

Common

A config object holding chain-specific information such as hardfork and EIP info.

Transaction Pool

Manages pending transactions, orders them by gas price, and handles transaction validation.

// Access via
const txPool = await node.getTxPool();
 
// Features
await txPool.add(transaction);
await txPool.getTransactions();
const pendingTxs = txPool.getPendingTransactions();
const pendingNonces = txPool.getPendingNonce(address);

Custom viem API actions for the transaction pool are planned but not high priority. Join the Telegram if you want this.

Receipt Manager

A cache for receipts and logs so Tevm doesn't have to re-execute transactions to derive them. Don't interact with it directly except for very advanced use cases (of which there may be none).

Custom Tool Opportunities

  • Transaction Simulators — Preview transaction outcomes before sending to mainnet
  • EVM Debuggers — Step through transactions with full state visibility
  • Local-first dApps — Apps that work offline with optimistic updates
  • Educational Tools — Interactive EVM learning experiences
  • CI/CD Integration — Test smart contracts in continuous integration pipelines
  • Gas Optimization — Analyze contract gas usage patterns with precision
  • Serverless Execution — Run Ethereum nodes in serverless or edge environments
  • State Snapshots — Create, save, and restore blockchain state at precise points

For examples, see examples.

API Interfaces

API LevelDescriptionBest For
Viem Client APIStandard viem actions plus Tevm-specific actionsMost application development
JSON-RPC APIStandard Ethereum RPC methods plus Anvil and Tevm-specific methodsDirect RPC integration, tooling
Low-Level TevmNodeDirect access to EVM, StateManager, Blockchain, etc.Tool developers, deep customization
API layers at a glance:
  • Viem Client API (high-level): client.getBalance(), client.tevmMine(), client.sendTransaction()
  • JSON-RPC API: eth_getBalance, anvil_mine, tevm_dumpState
  • TevmNode API: node.getVm(), node.getTxPool(), node.extend()
  • Low-Level Components: vm.evm.runCall(), stateManager.getAccount(), blockchain.putBlock()
Client API
import { createMemoryClient } from 'tevm'
 
const client = createMemoryClient()
 
// Standard viem actions
const balance = await client.getBalance({ address: '0x123...' })
const blockNumber = await client.getBlockNumber()
 
// Tevm-specific actions
await client.tevmSetAccount({
  address: '0x123...',
  balance: 1000000000000000000n
})
await client.tevmMine()

For component API details, see:

Advanced Features

For Tevm Bundler users, import Solidity directly with full type safety:

import { ERC20 } from "./ERC20.sol";
 
const token = ERC20.withAddress("0x123...");
 
const decimals = await token.read.decimals();

Extensibility Model

Node Extension API

Tevm's plugin system allows adding new functionality to nodes:

import { createTevmNode } from "tevm";
 
const node = createTevmNode().extend((baseNode) => {
  return {
    async simulateBulkTransactions(txs) {
      const results = [];
      for (const tx of txs) {
        const vm = await baseNode.getVm();
        results.push(await vm.runTx({ tx }));
      }
      return results;
    },
 
    async resetToSnapshot(snapshot) {
      const vm = await baseNode.getVm();
      return vm.stateManager.revert(snapshot);
    },
  };
});
 
const snapshot = await node.getVm().stateManager.checkpoint();
const results = await node.simulateBulkTransactions([tx1, tx2, tx3]);
await node.resetToSnapshot(snapshot);

This model allows powerful customizations while preserving the core API.

Further Resources

ResourceDescription
TevmNode Interface ReferenceDetailed API reference for the core node interface
GitHub RepositorySource code and contributions
Custom Precompiles GuideLearn how to extend the EVM
Performance ProfilingOptimize your Ethereum applications

Next: Create a Tevm Node · Viem Integration · Practical Examples