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 interfaceEvm
- The Ethereum Virtual MachineStateManager
- Manages blockchain stateBlockchain
- 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 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 |
📁 API Layers
📁 Viem Client API (high-level)
📁 JSON-RPC API
📁 TevmNode API
📁 Low-Level Components
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
Learn how to create and configure a Tevm Node for your project.
See how to use Tevm with viem for a complete Ethereum development experience.
Explore real-world examples and use cases for Tevm Node.
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 |