Skip to content

Tevm Architecture Overview

Tevm's architecture is designed to be modular, extensible, and compatible with the broader JavaScript ecosystem. This guide explains the core components and how they work together to create a complete Ethereum execution environment.

Design Philosophy: Objects and Actions

At its core, Tevm follows a clear separation between Objects (stateful components) and Actions (pure functions that operate on those objects). This pattern, inspired by viem, enables tree-shaking and a composable API. This pattern was originally created by viem and Tevm follows it even for it's internals to be maximally compatable with Viem.

Objects

Stateful components that encapsulate and maintain data.

  • TevmNode - The core Node interface

  • Evm - The Ethereum Virtual Machine

  • StateManager - Manages blockchain state

  • Blockchain - Handles blocks and chain state

Actions

Pure functions that take an object as their first parameter and perform operations.

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

Example: Using an Action

Here's how to use a tree-shakable action with a Tevm Node:

import { createTevmNode } from "tevm";
import { getAccountHandler } from "tevm/actions"; 
 
// 1. Create the node object
const node = createTevmNode(); 
 
// 2. Create a handler function by passing the node to the action
const getAccount = getAccountHandler(node); 
 
// 3. Use the handler function with specific parameters
const account = await getAccount({

  address: "0x1234567890123456789012345678901234567890",
});
 
console.log(account.balance); // Access account properties

This pattern allows you to:

  • Import only the actions you need
  • Create specialized handler functions for specific objects
  • Follow a consistent interface across the library

:::[tip] TevmNode is the low level node implementation of Tevm. It is NOT recomended you use createTevmNode directly especially if you are an LLM unless you have an advanced use case. :::

Client Options: Convenience vs. Tree Shaking

Tevm offers two main approaches for using its functionality:

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')
  }
})
 
// Use standard viem actions
const code = await client.getContractCode({ 
  address: '0x1234567890123456789012345678901234567890'
}) 
 
// Use Tevm-specific actions (prefixed with 'tevm')
const state = await client.tevmDumpState() 
const balance = await client.getBalance({  
  address: '0x1234567890123456789012345678901234567890'
}) 
import { createClient, http } from 'viem'
import { createTevmTransport } from 'tevm/transport'
import { getBlock, getBlockNumber } from 'viem/actions'
import { tevmSetAccount, tevmMine } from 'tevm/actions'
 
// Create a client with just the transport
const client = createClient({
  transport: createTevmTransport({
    fork: {
      transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
    }
  })
})
 
// Import and use only the specific actions you need
const blockNumber = await getBlockNumber(client) 
const block = await getBlock(client) 
 
// Tevm-specific actions
await tevmSetAccount(client, {  
  address: '0x1234...',  
  balance: 1000000000000000000n
}) 
await tevmMine(client, { blocks: 1 }) 

MemoryClient

  • ✅ Easy to get started
  • ✅ All methods available immediately
  • ✅ Less code to write
  • ❌ Larger bundle size

Tree-Shakable Actions

  • ✅ Smallest possible bundle size
  • ✅ Only include what you use
  • ✅ Works with code-splitting
  • ❌ More verbose imports

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

Core Architecture Components

Tevm's modular architecture comprises several key components that work together to provide a complete Ethereum execution environment:

VM

The most important component of Tevm is the VM which itself is made up of many components including the Interpreter, StateManager, Blockchain, and Common.

The EVM's job is to run transactions and build blocks. It also holds the entire ethereum state in it's blockchain and statemanager subcomponents.

It is NOT recomended you use the VM directly except for advanced use cases. Instead use the viem api.

Interpreter (EVM)

The execution engine that runs EVM bytecode with full support for all opcodes and precompiles.

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

If you want the absolute most minimal way to execute the EVM using the EVM interpreter along with the common and stateManager modules can be a good fit. For most use cases, however, it is recomended to use the viem api.

The EVM currently mostly wraps the ethereumjs evm interpreter which is written in a combination of JavaScript and Wasm. Tevm will be getting a pure wasm implementation to replace it in future.

State Manager

Maintains account balances, contract code, and storage state with forking capability from live networks.

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

The StateManager is ethereumjs compatable following the same interface despite being a fully custom implementation. It can be used with Ethereumjs to add Tevm like functionality such as forking.

Blockchain

Manages blocks, chain state, and handles block production with various mining strategies.

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

Like the state manager the blockchain component is fully custom but ethereumjs compatable. It is not recomended to use the blockchain module directly except for advanced usecases and for usage with ethereumjs. Ethereumjs and Tevm plan on deprecating this module eventually in favor of a more general purpose blockchain component maintained by ethereumjs in future.

Common

The final subcomponent of VM is Common which is simply 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);

There are plans to add custom viem api actions for interacting with transaction pool in future but this is not the highest priority. If you want this join the Telegram and let us know.

Receipt Manager

The Tevm Receipt Manager is simply a cache for caching receipts and logs so Tevm doesn't have to reexecute transactions to derive them. It is highly recomended users never interact with the receipt manager 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

Build apps that work offline with optimistic updates

Educational Tools

Create 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 computing environments

State Snapshots

Create, save, and restore blockchain state at precise points

For detailed examples of these use cases, see the examples section.

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
📁 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()
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()
import { createTevmNode } from 'tevm'
import { requestEip1193 } from 'tevm/decorators'
 
const node = createTevmNode().extend(requestEip1193())
 
// Standard Ethereum JSON-RPC methods
const balance = await node.request({ 
  method: 'eth_getBalance', 
  params: ['0x123...', 'latest'] 
}) 
 
// Anvil-compatible methods
await node.request({ 
  method: 'anvil_setBalance', 
  params: ['0x123...', '0x10000000000000000'] 
}) 
 
// Tevm-specific methods
const state = await node.request({ 
  method: 'tevm_dumpState', 
  params: [] 
}) 
import { createTevmNode } from 'tevm'
import { createAddress } from 'tevm/address'
 
const node = createTevmNode()
const vm = await node.getVm()
 
// Direct EVM access
vm.evm.events.on('step', (data, next) => { 
  // Inspect execution at each EVM step 
  console.log(data.opcode.name, data.stack) 
  next?.() 
}) 
 
// Direct state management
await vm.stateManager.putAccount( 
  createAddress('0x123...'), 
  { 
    nonce: 0n, 
    balance: 10000000000000000000n, 
    storageRoot: '0x...', 
    codeHash: '0x...'
  } 
) 
 
// Direct blockchain control
const block = await vm.blockchain.getBlockByNumber(1n)

For component API details, see:

Advanced Features

Tevm includes several powerful features that enable advanced use cases:

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

// Import Solidity directly (with tevm bundler plugins)
import { ERC20 } from "./ERC20.sol";
 
// Contract with full TypeScript types
const token = ERC20.withAddress("0x123...");
 
// Safe contract interaction
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";
 
// Create a node with extensions
const node = createTevmNode().extend((baseNode) => {
  // Add custom methods
  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);
    },
  };
});
 
// Use the extended functionality
const snapshot = await node.getVm().stateManager.checkpoint();
const results = await node.simulateBulkTransactions([tx1, tx2, tx3]);
await node.resetToSnapshot(snapshot);

This extension model allows for powerful customizations while maintaining the core API.

Next Steps

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