title: VM & Submodules description: Overview of the internal EVM, blockchain, state, receipts, and more
VM & Submodules
Tevm Node is built on a modular architecture that separates concerns into distinct submodules. This guide covers the internal components and their APIs.
Overview
The main submodules are:
1. EVM (Ethereum Virtual Machine) - Core execution engine 2. Blockchain - Block and chain state management 3. StateManager - Account and storage state management 4. TxPool - Transaction mempool 5. ReceiptsManager - Transaction receipts and logs
EVM Module
The EVM module handles bytecode execution and state transitions. It is based on @ethereumjs/evm
.
const vm = await node.getVm()
const evm = await vm.evm
// Direct EVM execution
const result = await evm.runCall({
to: '0x...',
data: '0x...',
value: 0n,
caller: '0x...',
})
// Full transaction execution (recommended)
const txResult = await vm.runTx({
tx: transaction,
block: block,
})
Key Features
- State Management: Handles account state, storage, and code execution - Gas Metering: Tracks gas consumption during execution - Precompiles: Built-in contract implementations - EIP Support: Implements various Ethereum Improvement Proposals
Common Operations
// Check execution result
if (result.execResult.exceptionError) {
console.error('Execution failed:', result.execResult.exceptionError)
} else {
console.log('Return value:', result.execResult.returnValue)
console.log('Gas used:', result.execResult.executionGasUsed)
}
// Access logs from execution
for (const log of result.execResult.logs) {
console.log('Event:', {
address: log.address,
topics: log.topics,
data: log.data
})
}
Blockchain Module
The blockchain module manages blocks and chain state. It is based on @ethereumjs/blockchain
.
const chain = (await node.getVm()).blockchain
// Get block by number or hash
const block = await chain.getBlock('latest')
const blockByHash = await chain.getBlock(blockHash)
// Add new block
await chain.putBlock(block)
// Delete block
await chain.delBlock(blockHash)
// Get block details
console.log({
number: block.header.number,
hash: block.hash(),
parentHash: block.header.parentHash,
stateRoot: block.header.stateRoot
})
Fork Support
// When forking, blocks are fetched from the parent chain
const forkedBlock = await chain.getBlock(blockNumber)
// Local blocks override forked blocks
await chain.putBlock(localBlock)
StateManager
The StateManager handles account state and storage. It is based on @ethereumjs/statemanager
.
const state = (await node.getVm()).stateManager
// Account operations
const account = await state.getAccount(address)
await state.putAccount(address, account)
// Contract operations
await state.putContractCode(address, bytecode)
const code = await state.getContractCode(address)
// Storage operations
await state.putContractStorage(address, key, value)
const value = await state.getContractStorage(address, key)
// State snapshots
const snapshot = await state.dumpCanonicalGenesis()
await state.commit()
await state.revert()
Working with Accounts
// Create or update account
const account = {
nonce: 1n,
balance: 100n,
storageRoot: EMPTY_ROOT,
codeHash: EMPTY_HASH
}
await state.putAccount(address, account)
// Delete account
await state.deleteAccount(address)
Transaction Pool
The TxPool manages pending transactions. It is based on @ethereumjs/tx-pool
.
const pool = await node.getTxPool()
// Add transaction
await pool.add(transaction)
// Get pending transactions
const pending = await pool.getPendingTransactions()
// Remove transaction
await pool.removeByHash(txHash)
// Get transactions ordered by price
const ordered = await pool.txsByPriceAndNonce({
baseFee: 10n
})
Transaction Validation
// Validate transaction before adding
try {
await pool.validateTx(transaction)
await pool.add(transaction)
} catch (error) {
console.error('Invalid transaction:', error)
}
ReceiptsManager
The ReceiptsManager handles transaction receipts and event logs.
const receipts = await node.getReceiptsManager()
// Get receipts for block
const blockReceipts = await receipts.getReceipts(blockHash)
// Get receipt by transaction hash
const txReceipt = await receipts.getReceiptByTxHash(txHash)
// Query logs
const logs = await receipts.getLogs({
fromBlock: 0n,
toBlock: 'latest',
address: contractAddress,
topics: [eventSignature]
})
Working with Logs
// Process event logs
for (const log of logs) {
console.log({
address: log.address,
topics: log.topics,
data: log.data,
blockNumber: log.blockNumber,
transactionHash: log.transactionHash,
logIndex: log.logIndex
})
}
Best Practices
1. Use High-Level APIs: Prefer vm.runTx()
over direct EVM execution for full transaction support.
2. State Management: - Use checkpoints for atomic operations - Clean up state after tests - Handle reverts properly
3. Gas Optimization: - Estimate gas before execution - Monitor gas usage in hot paths - Use appropriate gas limits
4. Error Handling: - Check execution results - Handle exceptions gracefully - Validate inputs
Related Topics
- JSON-RPC Support - Managing State - Transaction Pool - Receipts & Logs - EVM Opcodes Reference - Ethereum Yellow Paper - ethereumjs/ethereumjs-monorepo
Up Next
- JSON-RPC Guide
- Advanced Usage