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 interfaceEvm— the Ethereum Virtual MachineStateManager— manages blockchain stateBlockchain— 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:
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 checkpointThe 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 Level | Description | Best For |
|---|---|---|
| Viem Client API | Standard viem actions plus Tevm-specific actions | Most application development |
| JSON-RPC API | Standard Ethereum RPC methods plus Anvil and Tevm-specific methods | Direct RPC integration, tooling |
| Low-Level TevmNode | Direct access to EVM, StateManager, Blockchain, etc. | Tool developers, deep customization |
- 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()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
| Resource | Description |
|---|---|
| TevmNode Interface Reference | Detailed API reference for the core node interface |
| GitHub Repository | Source code and contributions |
| Custom Precompiles Guide | Learn how to extend the EVM |
| Performance Profiling | Optimize your Ethereum applications |
Next: Create a Tevm Node · Viem Integration · Practical Examples

