```typescript showLineNumbers {2-8,11-18} filename="gas-analysis.ts"
// Track gas usage over time
const gasTimeline = logs // [!code focus]
.filter(log => log.gasUsed !== undefined) // [!code focus]
.map(log => ({ // [!code focus]
timestamp: log.startTime, // [!code focus]
gasUsed: log.gasUsed, // [!code focus]
type: log.type // [!code focus]
})) // [!code focus]
// Calculate gas efficiency by operation type
const gasEfficiencyByType = gasTimeline.reduce((acc, log) => { // [!code focus]
acc[log.type] = acc[log.type] || { totalGas: 0n, totalTime: 0, count: 0 } // [!code focus]
acc[log.type].totalGas += log.gasUsed // [!code focus]
acc[log.type].totalTime += log.executionTime // [!code focus]
acc[log.type].count++ // [!code focus]
return acc // [!code focus]
}, {}) // [!code focus]
// Calculate gas per millisecond for each operation type
Object.entries(gasEfficiencyByType).forEach(([type, stats]) => {
const gasPerMs = Number(stats.totalGas) / stats.totalTime
console.log(`${type}: ${gasPerMs.toFixed(2)} gas/ms (${stats.count} operations)`)
})
```
### Use Cases
#### Smart Contract Optimization
Profile your contract to identify performance bottlenecks and optimize the most expensive operations.
```typescript showLineNumbers {1-18} filename="contract-optimization.ts"
// Deploy the contract first
const vm = await node.getVm()
const deployTx = createTx({ data: contractBytecode })
await vm.runTx({ tx: deployTx })
const contractAddress = deployTx.createdAddress
// Clear logs before profiling the specific function
vm.evm.clearPerformanceLogs()
// Call the function you want to optimize
const functionCallTx = createTx({
to: contractAddress,
data: encodeFunctionData({
abi,
functionName: 'expensiveFunction',
args: [param1, param2]
})
})
await vm.runTx({ tx: functionCallTx })
const logs = vm.evm.getPerformanceLogs()
// Analyze to find optimization opportunities
const hotspots = identifyHotspots(logs)
console.log('Optimization targets:', hotspots)
```
#### Compare Different Implementations
Compare different approaches to solve the same problem and choose the most efficient one.
```typescript showLineNumbers {1-21} filename="implementation-comparison.ts"
async function compareImplementations(implementations) {
const results = []
const vm = await node.getVm()
for (const impl of implementations) {
// Clear previous logs
vm.evm.clearPerformanceLogs()
// Deploy this implementation
const deployTx = createTx({ data: impl.bytecode })
await vm.runTx({ tx: deployTx })
// Call with standard test case
const callTx = createTx({
to: deployTx.createdAddress,
data: impl.encodedFunctionCall
})
await vm.runTx({ tx: callTx })
const logs = vm.evm.getPerformanceLogs()
// Collect metrics
results.push(analyzePerformance(logs, impl.name))
}
return compareResults(results)
}
// Example output:
// Implementation A: 1.2ms, 45,000 gas
// Implementation B: 0.8ms, 32,000 gas (29% improvement)
```
#### Gas Optimization
Identify and optimize operations that consume the most gas to make your contracts cheaper to use.
```typescript showLineNumbers {1-17} filename="gas-optimization.ts"
// Find gas-intensive parts of your contract
const contractGasUsage = logs
.filter(log => log.type === 'opcode' && log.gasUsed)
.reduce((acc, log) => {
// Group by program counter to identify code locations
const pcKey = log.pc.toString().padStart(4, '0')
acc[pcKey] = acc[pcKey] || {
opcode: log.opcode,
count: 0,
totalGas: 0n
}
acc[pcKey].count++
acc[pcKey].totalGas += log.gasUsed
return acc
}, {})
// Identify gas hotspots
const gasHotspots = Object.entries(contractGasUsage)
.sort(([, a], [, b]) => Number(b.totalGas - a.totalGas))
.slice(0, 10)
```
### Best Practices
#### Targeted Profiling
For most accurate results, profile specific operations in isolation rather than entire sessions.
```typescript showLineNumbers {1-10} filename="targeted-profiling.ts"
// Clear logs before the specific operation you want to profile
vm.evm.clearPerformanceLogs()
// Run only the operation you want to profile
await vm.runTx({ tx: specificOperationTx })
// Analyze just that operation
const logs = vm.evm.getPerformanceLogs()
const analysis = analyzePerformance(logs)
// Clear logs when done to prevent memory build-up
vm.evm.clearPerformanceLogs()
```
#### Memory Management
Profiling generates a lot of data. Implement strategies to manage memory usage.
```typescript showLineNumbers {1-21} filename="memory-management.ts"
// For long-running sessions, clear logs periodically
async function profileWithMemoryManagement(operations) {
const results = []
for (const op of operations) {
// Clear before each operation
vm.evm.clearPerformanceLogs()
// Run the operation
await vm.runTx({ tx: op.tx })
// Process logs immediately
const logs = vm.evm.getPerformanceLogs()
const summary = summarizeLogs(logs) // Extract just what you need
// Save only the summary
results.push({
operation: op.name,
summary
})
// Clear immediately to free memory
vm.evm.clearPerformanceLogs()
}
return results
}
```
#### Comparative Analysis
Always measure performance before and after optimization to verify improvements.
```typescript showLineNumbers {1-14} filename="comparative-analysis.ts"
// Compare before and after optimization
async function measureOptimizationImpact(originalCode, optimizedCode) {
// Profile original implementation
vm.evm.clearPerformanceLogs()
await runImplementation(originalCode)
const beforeLogs = vm.evm.getPerformanceLogs()
const beforeMetrics = analyzePerformance(beforeLogs)
// Profile optimized implementation
vm.evm.clearPerformanceLogs()
await runImplementation(optimizedCode)
const afterLogs = vm.evm.getPerformanceLogs()
const afterMetrics = analyzePerformance(afterLogs)
// Calculate improvements
return {
timeImprovement: (1 - afterMetrics.totalTime / beforeMetrics.totalTime) * 100,
gasImprovement: (1 - Number(afterMetrics.totalGas) / Number(beforeMetrics.totalGas)) * 100,
details: compareMetrics(beforeMetrics, afterMetrics)
}
}
```
#### Production Use
The profiler itself adds some overhead. Consider these guidelines for production use.
```typescript showLineNumbers {1-15} filename="production-profiling.ts"
// For production environments
const node = createTevmNode({
profiler: {
// Only enable in specific environments
enabled: process.env.ENABLE_PROFILING === 'true',
// Use selective profiling to reduce overhead
includeOpcodes: false, // Disable full opcode tracking
samplingRate: 0.01, // Only profile 1% of operations
includeMemory: false, // Skip memory tracking
includeStorage: false, // Skip storage tracking
// Only track high-level calls for a system overview
includeHighLevelCalls: true
}
})
```
### Related Resources
import { Callout, Steps, Step, Button } from "vocs/components";
import { Card, CardGrid, Tab, TabGroup } from "../../components";
## Receipts & Logs
Tevm Node provides robust support for managing transaction receipts and event
logs through the ReceiptsManager module and filter system.
Transaction receipts and event logs are essential for tracking state changes and application events in Ethereum. Tevm's ReceiptsManager gives you powerful tools to work with them, enabling features like event listening, transaction status tracking, and log filtering.
### Quick Start
```typescript showLineNumbers {1-4,6-7,10-16,19-20,23-31} filename="basic-receipt-usage.ts"
import { createTevmNode } from 'tevm'
import { createImpersonatedTx } from 'tevm/tx'
import { runTx } from 'tevm/vm'
import { createAddress } from 'tevm/utils'
const node = createTevmNode() // [!code focus]
const receiptsManager = await node.getReceiptsManager() // [!code focus]
// Execute a transaction
const vm = await node.getVm() // [!code focus]
const tx = createImpersonatedTx({ // [!code focus]
impersonatedAddress: createAddress('0x1234567890123456789012345678901234567890'), // [!code focus]
to: createAddress('0x2345678901234567890123456789012345678901'), // [!code focus]
value: 1000000000000000000n, // 1 ETH // [!code focus]
gasLimit: 21000n, // [!code focus]
}) // [!code focus]
// Run the transaction
const result = await runTx(vm)({ tx }) // [!code focus]
const txHash = tx.hash() // [!code focus]
// Get the receipt
const receiptResult = await receiptsManager.getReceiptByTxHash(txHash) // [!code focus]
if (receiptResult) { // [!code focus]
const [receipt, blockHash, txIndex, logIndex] = receiptResult // [!code focus]
// Access receipt data
console.log({ // [!code focus]
status: 'status' in receipt ? receipt.status : undefined, // [!code focus]
gasUsed: receipt.cumulativeBlockGasUsed, // [!code focus]
logs: receipt.logs // [!code focus]
}) // [!code focus]
} // [!code focus]
```
```typescript showLineNumbers {1-4,6-7,10-14,17-23,26-32} filename="log-filtering.ts"
import { createTevmNode } from 'tevm'
import { createImpersonatedTx } from 'tevm/tx'
import { runTx } from 'tevm/vm'
import { createAddress, hexToBytes } from 'tevm/utils'
const node = createTevmNode() // [!code focus]
const receiptsManager = await node.getReceiptsManager() // [!code focus]
// Get blocks for filtering
const vm = await node.getVm() // [!code focus]
const fromBlock = await vm.blockchain.getBlockByTag('earliest') // [!code focus]
const toBlock = await vm.blockchain.getBlockByTag('latest') // [!code focus]
const contractAddress = createAddress('0x1234567890123456789012345678901234567890') // [!code focus]
// Filter by contract address
const addressLogs = await receiptsManager.getLogs( // [!code focus]
fromBlock, // [!code focus]
toBlock, // [!code focus]
[contractAddress.toBytes()], // Filter by this address // [!code focus]
undefined // No topic filter // [!code focus]
) // [!code focus]
// Filter by event topic (event signature hash)
const eventTopic = hexToBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef') // Transfer event // [!code focus]
const topicLogs = await receiptsManager.getLogs( // [!code focus]
fromBlock, // [!code focus]
toBlock, // [!code focus]
undefined, // Any address // [!code focus]
[eventTopic] // Filter by this topic // [!code focus]
) // [!code focus]
// Print results
console.log(`Found ${addressLogs.length} logs from the contract`)
console.log(`Found ${topicLogs.length} Transfer events`)
```
### Receipt Types
Tevm supports different receipt types across all Ethereum hard forks.
Uses state root for transaction results
Uses status codes (success/failure)
Includes blob gas information
```typescript showLineNumbers {1-6,8-12,14-17} filename="receipt-types.ts"
interface PreByzantiumReceipt {
stateRoot: Uint8Array // Merkle root after transaction
cumulativeBlockGasUsed: bigint
logs: Log[] // Event logs
// No status field
}
interface PostByzantiumReceipt {
status: number // 1 for success, 0 for failure
cumulativeBlockGasUsed: bigint
logs: Log[] // Event logs
}
interface EIP4844Receipt extends PostByzantiumReceipt {
blobGasUsed: bigint // Gas used for blob data
blobGasPrice: bigint // Price paid for blob data
}
```
```typescript showLineNumbers {1-12} filename="receipt-handling.ts"
// Function to handle different receipt types
function processReceipt(receiptResult) {
if (!receiptResult) return 'Receipt not found'
const [receipt] = receiptResult
if ('status' in receipt) {
// Post-Byzantium receipt
return `Transaction ${receipt.status === 1 ? 'succeeded' : 'failed'}`
} else {
// Pre-Byzantium receipt
return `Transaction included with state root: 0x${Buffer.from(receipt.stateRoot).toString('hex')}`
}
}
```
Always check the receipt structure before accessing properties, as the type depends on the Ethereum hard fork configuration.
### Working with Event Logs
#### Contract Deployment
First, deploy a contract that emits events:
```typescript showLineNumbers {1-14} filename="deploy-contract.ts"
// Deploy a contract that emits events
const deployTx = createImpersonatedTx({
impersonatedAddress: createAddress(
"0x1234567890123456789012345678901234567890",
),
data: CONTRACT_BYTECODE, // Your contract bytecode
gasLimit: 1000000n,
});
const vm = await node.getVm();
const deployResult = await runTx(vm)({ tx: deployTx });
// Check deployment success
const contractAddress = deployResult.createdAddress;
if (!contractAddress) {
throw new Error("Contract deployment failed");
}
```
#### Emit Events
Next, interact with the contract to emit events:
```typescript showLineNumbers {1-10} filename="emit-events.ts"
// Call a function that emits events
const interactTx = createImpersonatedTx({
impersonatedAddress: createAddress(
"0x1234567890123456789012345678901234567890",
),
to: contractAddress,
// Function selector + parameters (e.g., transfer function on an ERC-20)
data: "0xa9059cbb000000000000000000000000123456789012345678901234567890123456789000000000000000000000000000000000000000000000008ac7230489e80000",
gasLimit: 100000n,
});
await runTx(vm)({ tx: interactTx });
```
#### Query Logs
Then, query the logs using various filters:
```typescript showLineNumbers {1-25} filename="query-logs.ts"
// Get the block range for filtering
const fromBlock = await vm.blockchain.getBlockByTag("earliest");
const toBlock = await vm.blockchain.getBlockByTag("latest");
// 1. Get all logs from the contract
const contractLogs = await receiptsManager.getLogs(
fromBlock,
toBlock,
[contractAddress.toBytes()],
undefined,
);
// 2. Get logs for a specific event (e.g., Transfer event)
const transferEventSignature =
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
const transferLogs = await receiptsManager.getLogs(
fromBlock,
toBlock,
[contractAddress.toBytes()],
[hexToBytes(transferEventSignature)],
);
// 3. Get logs with specific parameters (e.g., transfers to a specific address)
const receiverAddress = "0x1234567890123456789012345678901234567890";
const paddedAddress = "0x000000000000000000000000" + receiverAddress.slice(2);
const topicForReceiver = hexToBytes(paddedAddress);
const transfersToReceiver = await receiptsManager.getLogs(
fromBlock,
toBlock,
[contractAddress.toBytes()],
[hexToBytes(transferEventSignature), undefined, topicForReceiver],
);
```
#### Process Log Data
Finally, decode and process the log data:
```typescript showLineNumbers {1-19} filename="decode-logs.ts"
// Process Transfer event logs
for (const log of transferLogs) {
// A Transfer event typically has this structure:
// topic[0]: event signature
// topic[1]: from address (padded to 32 bytes)
// topic[2]: to address (padded to 32 bytes)
// data: amount (as a 32-byte value)
const from = "0x" + Buffer.from(log.topics[1]).toString("hex").slice(24);
const to = "0x" + Buffer.from(log.topics[2]).toString("hex").slice(24);
// Convert the data to a BigInt (amount)
const amount = BigInt("0x" + Buffer.from(log.data).toString("hex"));
console.log(`Transfer: ${from} โ ${to}: ${amount} tokens`);
// You can also access other metadata
console.log(
`Block: ${log.blockNumber}, TxIndex: ${log.txIndex}, LogIndex: ${log.logIndex}`,
);
}
```
### Advanced Features
Ethereum logs support filtering on up to 4 topics, allowing for complex queries.
```typescript showLineNumbers {1-22} filename="complex-filtering.ts"
// Get logs with complex filters:
// 1. From a specific contract
// 2. With Transfer event signature
// 3. From a specific sender
// 4. To a specific receiver
const fromAddress = '0x1234567890123456789012345678901234567890'
const toAddress = '0x2345678901234567890123456789012345678901'
// Event topics with wildcards (undefined means "any value")
const topics = [
hexToBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), // Transfer event
hexToBytes('0x000000000000000000000000' + fromAddress.slice(2)), // From address (padded)
hexToBytes('0x000000000000000000000000' + toAddress.slice(2)) // To address (padded)
]
const filteredLogs = await receiptsManager.getLogs(
fromBlock,
toBlock,
[contractAddress.toBytes()], // Contract address
topics // Topic filters
)
```
Filter logs from multiple contracts at once.
```typescript showLineNumbers {1-21} filename="multi-contract-filtering.ts"
// Track events across multiple contracts (e.g., a token and a marketplace)
const tokenAddress = createAddress('0x1234567890123456789012345678901234567890')
const marketplaceAddress = createAddress('0x2345678901234567890123456789012345678901')
// Get Transfer events from either contract
const transferEvent = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
const transfers = await receiptsManager.getLogs(
fromBlock,
toBlock,
[tokenAddress.toBytes(), marketplaceAddress.toBytes()], // Multiple addresses
[hexToBytes(transferEvent)]
)
console.log('Events by contract:')
const tokenEvents = transfers.filter(log =>
Buffer.from(log.address).toString('hex') ===
Buffer.from(tokenAddress.toBytes()).toString('hex')
)
const marketplaceEvents = transfers.filter(log =>
Buffer.from(log.address).toString('hex') ===
Buffer.from(marketplaceAddress.toBytes()).toString('hex')
)
```
ReceiptsManager maintains indexes for efficient queries.
```typescript showLineNumbers {1-24} filename="receipt-indexing.ts"
// The ReceiptsManager maintains indexes for:
// 1. Transaction hash โ receipt
// 2. Block hash โ receipts
// 3. Address โ logs
// 4. Topics โ logs
// You can directly get a receipt by transaction hash
const receipt = await receiptsManager.getReceiptByTxHash(txHash)
// Or get all receipts in a block
const block = await vm.blockchain.getLatestBlock()
const blockHash = block.hash()
const receipts = await receiptsManager.getBlockReceipts(blockHash)
// Performance considerations:
// - Receipt storage grows with blockchain size
// - ReceiptsManager implements limits to prevent excessive resource usage
// Built-in limits
const GET_LOGS_LIMIT = 10000 // Maximum number of logs returned
const GET_LOGS_LIMIT_MEGABYTES = 150 // Maximum response size
const GET_LOGS_BLOCK_RANGE_LIMIT = 2500 // Maximum block range for queries
// For large-scale applications, implement pagination or additional filtering
```
### Best Practices
Use specific filters and limit block ranges for better performance
Always check for null/undefined results when working with receipts
Check receipt types before accessing properties
Implement pagination for large log queries
#### Efficient Log Queries
```typescript showLineNumbers {1-12} filename="efficient-queries.ts"
// Instead of querying the entire chain
// Focus on specific block ranges when possible
const latestBlock = await vm.blockchain.getLatestBlock();
const blockNumber = latestBlock.header.number;
// Get logs from the last 100 blocks
const fromBlock = await vm.blockchain.getBlock(
blockNumber - 100n > 0n ? blockNumber - 100n : 0n,
);
// Use specific filters (address + topics)
const logs = await receiptsManager.getLogs(
fromBlock,
latestBlock,
[contractAddress.toBytes()],
[eventTopic],
);
```
#### Proper Error Handling
```typescript showLineNumbers {1-18} filename="error-handling.ts"
// Handle missing receipts gracefully
async function safeGetReceipt(txHash) {
try {
const receiptResult = await receiptsManager.getReceiptByTxHash(txHash);
if (receiptResult === null) {
console.log(
"Receipt not found - transaction may be pending or not exist",
);
return null;
}
const [receipt] = receiptResult;
return receipt;
} catch (error) {
console.error("Error retrieving receipt:", error.message);
// Handle specific error types if needed
return null;
}
}
```
#### Working with Receipt Types
```typescript showLineNumbers {1-18} filename="type-safety.ts"
// Safely work with different receipt types
function getTransactionStatus(receipt) {
if (!receipt) return "Unknown";
if ("status" in receipt) {
// Post-Byzantium receipt
return receipt.status === 1 ? "Success" : "Failed";
} else if ("stateRoot" in receipt) {
// Pre-Byzantium receipt - check if it's the empty root
const emptyRoot =
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421";
const actualRoot = "0x" + Buffer.from(receipt.stateRoot).toString("hex");
return actualRoot === emptyRoot ? "Likely Failed" : "Likely Success";
}
return "Unknown Format";
}
```
### Related Resources
## Transaction Pool
The Transaction Pool (TxPool) is a crucial component that manages pending transactions before they're included in blocks. It handles transaction ordering, replacement, validation, and lifecycle management.
### Quick Start
```ts
import { createTevmNode } from 'tevm'
import { createAddress } from 'tevm/address'
import { createImpersonatedTx } from 'tevm/tx'
// Initialize node and get txpool
const node = createTevmNode()
const txPool = await node.getTxPool()
// Create and add a transaction
const tx = createImpersonatedTx({
impersonatedAddress: createAddress('0x1234...'),
to: createAddress('0x2345...'),
value: 1000000000000000000n, // 1 ETH
gasLimit: 21000n,
maxFeePerGas: 20000000000n,
maxPriorityFeePerGas: 20000000000n,
nonce: 0n,
})
await txPool.addUnverified(tx)
```
### Key Features
* ๐ **Transaction Validation** - Comprehensive validation including nonce, balance, and gas checks
* ๐ **Transaction Replacement** - Replace pending transactions with higher gas price versions
* ๐ **Nonce Ordering** - Maintains correct transaction sequence per account
* ๐งน **Automatic Pruning** - Removes old transactions to prevent memory bloat
* โก **Performance Optimized** - Efficient handling of large transaction volumes
### Core Concepts
#### Pool Limits
The TxPool enforces several limits to ensure stable operation:
```ts
const LIMITS = {
MAX_POOL_SIZE: 5000, // Maximum total transactions
MAX_TXS_PER_ACCOUNT: 100, // Maximum per account
MIN_GAS_PRICE: 100000000n, // 0.1 GWei minimum
TX_MAX_DATA_SIZE: 128 * 1024, // 128KB max transaction size
}
```
#### Transaction Lifecycle
1. **Addition** - Transactions enter the pool via `add()` or `addUnverified()`
2. **Validation** - Optional checks for nonce, balance, and gas parameters
3. **Storage** - Valid transactions are stored and ordered by nonce
4. **Pruning** - Old transactions are removed after `POOLED_STORAGE_TIME_LIMIT` (20 minutes)
### Detailed Usage
#### Adding Transactions
Two methods for adding transactions, each with different validation levels:
```ts
// Method 1: With full validation
try {
await txPool.add(tx)
} catch (error) {
if (error.message.includes('insufficient balance')) {
console.error('Account has insufficient funds')
}
}
// Method 2: Without validation (faster)
await txPool.addUnverified(tx)
```
#### Transaction Replacement
Replace a pending transaction by submitting a new one with the same nonce and higher gas price:
```ts
const originalTx = createImpersonatedTx({
// ... base transaction params ...
maxFeePerGas: 20000000000n,
nonce: 0n,
})
const replacementTx = createImpersonatedTx({
// ... same params as original ...
maxFeePerGas: 30000000000n, // At least 10% higher
nonce: 0n, // Same nonce as original
})
await txPool.addUnverified(originalTx)
await txPool.addUnverified(replacementTx) // Replaces originalTx
```
> **Note**: Replacement transactions must increase gas price by at least `MIN_GAS_PRICE_BUMP_PERCENT` (10%)
#### Querying Transactions
```ts
// Get transactions by sender
const senderTxs = await txPool.getBySenderAddress(senderAddress)
// Get transactions by hash
const txHashes = [hash1, hash2]
const specificTxs = txPool.getByHash(txHashes)
// Get ordered transactions for mining
const orderedTxs = await txPool.txsByPriceAndNonce({
baseFee: currentBaseFee,
allowedBlobs: 6, // For EIP-4844
})
```
#### Block Processing
When new blocks are added, update the pool:
```ts
import { mineHandler } from 'tevm/actions'
// Mine new blocks
await mineHandler(node)()
// Remove included transactions
txPool.removeNewBlockTxs(newBlocks)
```
### Advanced Features
#### Pool Management
```ts
// Start transaction processing
txPool.start()
// Stop processing (but keep transactions)
txPool.stop()
// Clear all transactions
txPool.close()
// Manual cleanup of old transactions
txPool.cleanup()
```
#### Transaction Types
The pool supports all Ethereum transaction types:
* Legacy Transactions
* EIP-2930 (Access Lists)
* EIP-1559 (Fee Market)
* EIP-4844 (Blob Transactions)
* Tevm Impersonated Transactions
### Best Practices
#### 1. Transaction Creation
Always use `createImpersonatedTx` with proper types:
```ts
const tx = createImpersonatedTx({
impersonatedAddress: createAddress(address),
to: createAddress(recipient),
value: parseEther('1'), // Use helper functions for values
gasLimit: 21000n,
maxFeePerGas: gweiToWei('20'),
maxPriorityFeePerGas: gweiToWei('2'),
nonce: 0n,
})
```
#### 2. Error Handling
Implement comprehensive error handling:
```ts
try {
await txPool.add(tx)
} catch (error) {
switch (true) {
case error.message.includes('insufficient balance'):
// Handle balance error
break
case error.message.includes('nonce too low'):
// Handle nonce error
break
case error.message.includes('gas price too low'):
// Handle gas price error
break
default:
// Handle unknown errors
}
}
```
#### 3. Performance Optimization
* Use `addUnverified` for bulk operations
* Implement proper cleanup cycles
* Monitor pool size and transaction age
#### 4. Memory Management
```ts
// Regular cleanup cycle
setInterval(() => {
txPool.cleanup()
}, 5 * 60 * 1000) // Every 5 minutes
// Monitor pool size
const poolSize = txPool.txsInPool
if (poolSize > MAX_POOL_SIZE * 0.8) {
console.warn('Pool approaching capacity')
}
```
### API Reference
#### TxPool Class
```ts
class TxPool {
constructor(options: { vm: Vm })
async add(tx: Transaction): Promise
async addUnverified(tx: Transaction): Promise
async getBySenderAddress(address: Address): Promise
getByHash(hashes: Uint8Array[]): Transaction[]
removeByHash(hash: string): void
removeNewBlockTxs(blocks: Block[]): void
start(): boolean
stop(): boolean
cleanup(): void
close(): void
}
```
### Related Topics
* [JSON-RPC Support](../api/json-rpc)
* [VM & Submodules](../api/vm-and-submodules)
* [Receipts & Logs](./receipts-and-logs)
import { Callout, Steps, Step } from "vocs/components";
## Creating a MemoryClient
`createMemoryClient` is the foundation of Tevm - a powerful function that
bootstraps a complete Ethereum execution environment in JavaScript.
Instantiate a client with default configuration:
#### TypeScript
```ts
import { createMemoryClient } from "tevm";
const client = createMemoryClient();
// Optionally wait for the client to be ready before using it
// This is not required but useful if profiling performance or debugging an issue
await client.ready();
// Your client is now ready to use!
```
Tailor the node with powerful configuration options:
```ts
import { createMemoryClient, http } from "tevm";
const client = createMemoryClient({
// Fork from a live network
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY"),
},
// Configure automatic mining
miningConfig: { type: "auto" },
// Set logging verbosity
loggingLevel: "debug",
});
await client.ready();
```
### Configuration Options
Tevm Node offers extensive configuration options to adapt to different use cases. Here's a complete breakdown:
All configuration options for `createMemoryClient` apply to `createTevmNode`
and `createTevmTransport`
#### Fork Configuration
Forking allows you to create a local copy of any EVM chain for testing and
development.
The `fork` option creates a node that connects to an existing network:
```ts
import { createMemoryClient, http } from "tevm";
const node = createMemoryClient({
fork: {
// Use any EIP-1193 compatible provider
transport: http("https://mainnet.infura.io/v3/YOUR-KEY"),
// Optional: Fork from a specific block
blockTag: 17_000_000n,
},
});
await node.ready();
```
When forking, state is fetched lazily from the remote provider and cached
locally. This means your first access to any account or storage slot will be
slower, but subsequent accesses will be fast.
#### Mining Configuration
Mining configuration determines how transactions are processed and blocks are
produced.
Control how and when blocks are produced with various mining modes:
```ts
// Auto-mining: Mine a block for every transaction
const node = createMemoryClient({
miningConfig: {
type: "auto",
},
});
// Interval-based mining: Mine at regular intervals
const intervalNode = createMemoryClient({
miningConfig: {
type: "interval",
interval: 12_000, // Mine every 12 seconds
},
});
await node.ready();
await intervalNode.ready();
```
#### Chain Configuration
Chain configuration defines the rules and parameters of the blockchain.
Customize the chain parameters or use pre-configured chains:
```ts
import { createMemoryClient } from "tevm";
import { Common } from "tevm/common";
// Custom chain configuration
const customNode = createMemoryClient({
common: Common.custom({
chainId: 1337,
networkId: 1337,
// Other chain parameters
}),
});
await customNode.ready();
```
Or use one of the pre-configured chains:
```ts
import { createMemoryClient } from "tevm";
import { mainnet, optimism, arbitrum, base } from "tevm/common";
// Create a node with Optimism chain configuration
const optimismNode = createMemoryClient({
common: optimism,
});
await optimismNode.ready();
```
Want to add your own network?
If you need support for a network not included in Tevm, first add it to
`viem/chains` and then open an issue on the Tevm repository to request the
network to be added.
#### Logging Configuration
Logging helps debug issues and understand what's happening inside your node.
Configure the internal logger to match your needs:
```ts
const node = createMemoryClient({
loggingLevel: "debug", // 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
});
// Later use the logger directly
node.logger.debug("Detailed debugging information");
node.logger.info("Informational message");
node.logger.warn("Warning!");
node.logger.error("Error encountered", { details: "Something went wrong" });
await node.ready();
```
#### Custom Precompiles
Custom precompiles allow you to extend the EVM with your own functions.
Add your own precompiled contracts to unlock powerful capabilities:
```ts
import { definePrecompile, createContract, parseAbi } from "tevm";
const calculatorPrecompile = definePrecompile({
// Define contract interface
contract: createContract({
abi: parseAbi([
"function add(uint256 a, uint256 b) returns (uint256)",
"function subtract(uint256 a, uint256 b) returns (uint256)",
]),
address: "0xf2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2",
}),
// Implement the precompile logic
call: async ({ data, gasLimit }) => {
// Precompile implementation goes here
console.log("Precompile called with data:", data, "gas limit:", gasLimit);
return {
returnValue: new Uint8Array([0x01]), // Example return value
executionGasUsed: 200n,
};
},
});
// Register the precompile with the node
const node = createMemoryClient({
customPrecompiles: [calculatorPrecompile.precompile()],
});
await node.ready();
```
#### Performance Profiling
Performance profiling helps identify bottlenecks in your smart contracts.
Enable the built-in profiler for detailed execution metrics:
```ts
const node = createMemoryClient({
profiler: true,
});
await node.ready();
// Run a transaction or call
// ...
// Access profiling data
const vm = await node.getVm();
const performanceLogs = vm.evm.getPerformanceLogs();
console.log("Performance data:", performanceLogs);
```
### Complete Configuration Reference
| Property | Type | Default | Description |
| ---------------------------- | -------------------------------------------------------------- | ------------------ | ------------------------------------------------------------ |
| `fork` | `{ transport: EIP1193RequestFn; blockTag?: BlockTag; }` | - | Enables forking from a live network or another Tevm instance |
| `common` | `Common` | `tevmDevnet` | Chain configuration object |
| `loggingLevel` | `"fatal" \| "error" \| "warn" \| "info" \| "debug" \| "trace"` | `"info"` | Logging verbosity level |
| `miningConfig` | `{ type: 'auto' } \| { type: 'interval', interval: number }` | `{ type: 'auto' }` | Block mining behavior |
| `customPrecompiles` | `Precompile[]` | `[]` | Additional precompiled contracts |
| `allowUnlimitedContractSize` | `boolean` | `false` | Disables EIP-170 contract size checks |
### Best Practices
Following these best practices will help you avoid common pitfalls when
working with Tevm Node.
#### Always pass in a common when forking
Though a `common` object is not required it is highly recomended for following reasons
* Tevm will initialize faster if a common is provided via not needing to fetch a chainId up front
* Chain specific information such as EIP and hardfork info will help Tevm provide a closer experience to the real chain.
When a common is not provided `tevmDefault` common is used
```typescript
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/common";
const client = createMemoryClient({
// always pass in a common
common: optimism,
fork: { transport: http() },
});
// Because tevm knows this is an op stack chain it will operate closer to an l1 chain including allowing you to calculate l1 data fee
const { l1DataFee } = await client.call({ data });
```
#### Choose the Right Mining Configuration
By Default Tevm uses manual mining. We believe explicit mining is the correct configuration for most to all use cases which is why it's the default. As a convenience we do offer automining which will mine a new block everytime a tx enters mempool. We also offer interval mining and gas mining to more closely replicate a real ethereum node.
```ts
// For testing: Mine after each transaction
const testNode = createMemoryClient({
miningConfig: { type: "auto" },
});
// For simulation: Mine at intervals to mimic real networks
const simulationNode = createMemoryClient({
miningConfig: { type: "interval", interval: 12_000 }, // 12 seconds like Ethereum
});
```
#### Utilize debug logging when something goes wrong
When you have issues with Tevm, `debug logging` is useful. Tevm will produce a lot of logs so you should use an LLM to help comb through them.
```ts
const client = createMemoryClient({
loggingLevel: "debug",
});
```
#### Call `client.ready()` if profiling
Anytime you are profiling tevm you will want to call `client.ready()` before running the profiler or else you will be measuring the initializion time in addition to the action. When you don't call `client.ready()` all tevm actions implicitly wait for the client to be ready before executing. Tevm initializes very fast though as it doesn't need to do a `sync`.
### Next Steps
* [Node Interface](./tevm-node-interface) - Explore the complete TevmNode interface and its capabilities
* [Forking & Reforking](./forking) - Learn how to fork from live networks and efficiently manage forks
* [State Management](./managing-state) - Understand how to manipulate blockchain state
* [Custom Precompiles](../advanced/custom-precompiles) - Create your own precompiled contracts to extend EVM functionality
import { Callout } from "vocs/components";
## Forking & Reforking
Tevm's forking capability creates a local blockchain that mirrors the state of
any EVM network, allowing you to work with real-world data in an isolated
environment.
Forking allows you to create a local copy of an Ethereum network (or any EVM-compatible chain) at a specific point in time. This local copy can be modified without affecting the original network, making it perfect for:
* Testing against production smart contracts and state
* Debugging complex transactions
* Developing with real-world data
* Simulating DeFi protocols and interactions
* Performing "what-if" analysis and scenario testing
### Basic Forking
#### Fork from Mainnet
```ts
import { createTevmNode, http } from "tevm";
// Create a node that forks from Ethereum mainnet
const node = createTevmNode({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY")({}),
blockTag: "latest", // Fork from the latest block
},
});
// Always wait for the node to be ready
await node.ready();
// The node now contains a copy of the entire Ethereum mainnet state
console.log("Forked node ready!");
```
#### Fork from Optimism
```ts
import { createTevmNode, http } from "tevm";
import { optimism } from "tevm/common";
// Create a node that forks from Optimism
const node = createTevmNode({
fork: {
transport: http("https://mainnet.optimism.io")({}),
blockTag: "latest",
},
common: optimism, // Use Optimism chain configuration
});
await node.ready();
console.log("Optimism fork ready!");
```
#### Fork from Specific Block
```ts
import { createTevmNode, http } from "tevm";
// Create a node that forks from a specific block
const node = createTevmNode({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY")({}),
blockTag: 17_500_000n, // Specific block number
},
});
await node.ready();
console.log("Forked from block 17,500,000");
```
Remember to call the transport function with an empty object `({})` as shown
in the examples above. This is required for the viem transport to be
initialized correctly.
### Fork Transport Options
Tevm can fork from any EIP-1193 compatible provider, giving you flexibility in
where you source your blockchain data.
#### Provider Types
Tevm supports multiple transport types for forking:
```ts
import { createTevmNode, http } from "tevm";
import { createPublicClient, http as viemHttp } from "viem";
import { BrowserProvider } from "ethers";
// 1. Using Tevm's http transport (recommended)
const tevmNode1 = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
// 2. Using viem's PublicClient
const publicClient = createPublicClient({
transport: viemHttp("https://mainnet.infura.io/v3/YOUR-KEY"),
});
const tevmNode2 = createTevmNode({
fork: {
transport: publicClient,
},
});
// 3. Using Ethers.js Provider (must be wrapped to match EIP-1193)
const ethersProvider = new BrowserProvider(window.ethereum);
const tevmNode3 = createTevmNode({
fork: {
transport: {
request: async ({ method, params }) => {
return await ethersProvider.send(method, params || []);
},
},
},
});
```
#### Provider Selection
Each provider type has different characteristics:
| Provider | Pros | Cons |
| --------------------- | ---------------------------------------- | -------------------------------- |
| `http` from Tevm | Optimized for Tevm, minimal dependencies | Limited middleware support |
| Public RPC nodes | Free, easy to use | Rate limits, may be slow |
| Alchemy/Infura/etc | Fast, reliable, archive data | Requires API key, may have costs |
| Local Ethereum node | Full control, no rate limits | Resource intensive to run |
| Another Tevm instance | Memory efficient | Adds complexity |
For best performance with large-scale testing, consider using a dedicated RPC
provider like Alchemy or Infura, or running your own Ethereum node.
### How Forking Works
Understanding how forking works internally helps you optimize your usage of
Tevm.
Tevm implements **lazy loading with local caching** for forked state:
1. **Initial Fork**: When you create a forked node, Tevm doesn't immediately copy the entire blockchain state.
2. **Lazy Loading**: When your code accesses specific accounts or storage slots, Tevm fetches only that data from the remote provider.
3. **Local Cache**: Fetched data is stored in a local cache, so subsequent reads are fast and don't require network requests.
4. **Local Modifications**: Any changes you make (e.g., account balance changes, contract deployments) are stored locally and take precedence over the forked state.
This approach provides the perfect balance between having a complete blockchain copy and efficient memory usage.
```ts
import { createTevmNode, http } from "tevm";
import { createAddress } from "tevm/address";
import { performance } from "node:perf_hooks";
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await node.ready();
const vm = await node.getVm();
const daiAddress = createAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F");
// First access - fetches from remote provider
console.log("Fetching DAI contract from mainnet...");
const t0 = performance.now();
const daiContract = await vm.stateManager.getAccount(daiAddress);
console.log(`First access: ${performance.now() - t0}ms`);
// Second access - uses local cache
const t1 = performance.now();
await vm.stateManager.getAccount(daiAddress);
console.log(`Cached access: ${performance.now() - t1}ms`);
// The difference in timing demonstrates the caching in action
```
### Reforking Strategies
Reforking allows you to create multiple nodes based on the same initial state,
perfect for testing different scenarios.
There are two primary approaches to reforking in Tevm:
#### 1. Using a Node as Transport
This is the recommended approach for most scenarios as it's more memory efficient:
```ts
import { createTevmNode, http, hexToBigInt } from "tevm";
import { requestEip1193 } from "tevm/decorators";
// Step 1: Create the source node with EIP-1193 support
const sourceNode = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
blockTag: 17_000_000n,
},
}).extend(requestEip1193());
await sourceNode.ready();
// Step 2: Get the current block number
const currentBlock = await sourceNode.request({
method: "eth_blockNumber",
});
// Step 3: Create a new node that forks from the source node
const forkNode = createTevmNode({
fork: {
transport: sourceNode, // Use the source node as a transport
blockTag: hexToBigInt(currentBlock),
},
});
await forkNode.ready();
// Now forkNode is a fork of sourceNode
// Changes in forkNode won't affect sourceNode
```
This approach has several advantages:
* **Memory Efficiency**: Reuses the cached state from the source node
* **Performance**: Avoids duplicate network requests for the same data
* **Isolation**: Changes in the new fork don't affect the source node
* **Flexibility**: Can create multiple forks from the same source
#### 2. Using Deep Copy
For complete isolation, you can create an independent copy:
```ts
import { createTevmNode, http } from "tevm";
// Step 1: Create the original node
const originalNode = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await originalNode.ready();
// Let the node fetch some state
// ... perform operations ...
// Step 2: Create a completely independent copy
const independentNode = await originalNode.deepCopy();
// Now independentNode is a complete copy of originalNode with its own state
// Changes to either node won't affect the other
```
Deep copying duplicates the entire state tree, which can be memory intensive.
Use this approach sparingly and prefer using the node as a transport when
possible.
### Working with Forked State
Once you have a forked node, you can read from and write to its state just
like a regular Ethereum node.
#### Reading State
Access the state of accounts, contracts, and storage:
```ts
import { createTevmNode, http } from "tevm";
import { createAddress } from "tevm/address";
import { formatEther, hexToBytes } from "viem";
// Create a node forked from mainnet
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await node.ready();
const vm = await node.getVm();
// 1. Read an EOA (externally owned account) state
const vitalikAddress = createAddress(
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
);
const vitalikAccount = await vm.stateManager.getAccount(vitalikAddress);
if (vitalikAccount) {
console.log(`Vitalik's balance: ${formatEther(vitalikAccount.balance)} ETH`);
console.log(`Nonce: ${vitalikAccount.nonce}`);
}
// 2. Read a contract's code
const uniswapV2Router = createAddress(
"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
);
const routerAccount = await vm.stateManager.getAccount(uniswapV2Router);
const routerCode = await vm.stateManager.getContractCode(uniswapV2Router);
console.log(`Uniswap Router code size: ${routerCode.length} bytes`);
// 3. Read contract storage
const slot0 = await vm.stateManager.getContractStorage(
uniswapV2Router,
hexToBytes(
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
);
console.log(`Storage slot 0: ${slot0}`);
// 4. Execute view function via low-level call
const result = await vm.evm.runCall({
to: uniswapV2Router,
data: hexToBytes("0xc45a0155"), // factory() function selector
gasLimit: 100000n,
});
console.log("Factory address:", result.execResult.returnValue);
```
#### Modifying State
Make changes to the forked state:
```ts
import { createTevmNode, http } from "tevm";
import { createAddress } from "tevm/address";
import { parseEther, formatEther } from "viem";
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await node.ready();
const vm = await node.getVm();
// 1. Modify account balance
const testAddress = createAddress("0x1234567890123456789012345678901234567890");
let account = await vm.stateManager.getAccount(testAddress);
// If account doesn't exist yet, create it
if (!account) {
account = {
nonce: 0n,
balance: 0n,
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
};
}
// Add 100 ETH to the account
account.balance += parseEther("100");
await vm.stateManager.putAccount(testAddress, account);
// Verify the change
const updatedAccount = await vm.stateManager.getAccount(testAddress);
console.log(`New balance: ${formatEther(updatedAccount.balance)} ETH`);
// 2. Impersonate an account
node.setImpersonatedAccount("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); // Vitalik
// Now you can send transactions as this account
import { createImpersonatedTx } from "tevm/tx";
const tx = createImpersonatedTx({
from: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
to: testAddress,
value: parseEther("1"),
gasLimit: 21000n,
});
const txResult = await vm.runTx({ tx });
console.log(`Transaction success: ${!txResult.execResult.exceptionError}`);
console.log(`Gas used: ${txResult.gasUsed}`);
// 3. Modify contract storage directly
const uniswapV3Factory = createAddress(
"0x1F98431c8aD98523631AE4a59f267346ea31F984",
);
await vm.stateManager.putContractStorage(
uniswapV3Factory,
hexToBytes(
"0x0000000000000000000000000000000000000000000000000000000000000000",
),
hexToBytes(
"0x0000000000000000000000001234567890123456789012345678901234567890",
),
);
```
### Advanced Use Cases
* **๐ DeFi Protocol Testing** - Test complex DeFi interactions like flash loans, liquidations, and arbitrage
* **๐ Vulnerability Analysis** - Analyze smart contract security by manipulating state to trigger edge cases
* **๐ณ๏ธ Governance Simulation** - Simulate DAO votes and governance proposals
* **๐ฐ MEV Strategy Testing** - Develop and test MEV strategies without risking real capital
#### DeFi Protocol Example
Simulate a complex DeFi interaction:
```ts
import { createTevmNode, http } from "tevm";
import { createAddress } from "tevm/address";
import { hexToBytes, parseEther } from "viem";
async function simulateFlashLoan() {
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await node.ready();
// Setup our test account
const trader = createAddress("0x1234567890123456789012345678901234567890");
const vm = await node.getVm();
// Give account some ETH
await vm.stateManager.putAccount(trader, {
nonce: 0n,
balance: parseEther("10"),
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
});
// AAVE V2 lending pool address
const aaveLendingPool = createAddress(
"0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9",
);
// Encode a flashLoan call (simplified)
// In reality you'd need to encode the parameters correctly
const flashLoanCalldata = hexToBytes("0xab9c4b5d...");
// Execute the flash loan
import { createImpersonatedTx } from "tevm/tx";
const tx = createImpersonatedTx({
from: trader,
to: aaveLendingPool,
data: flashLoanCalldata,
gasLimit: 5000000n,
});
const result = await vm.runTx({ tx });
console.log("Flash loan simulation result:");
console.log(`Success: ${!result.execResult.exceptionError}`);
if (result.execResult.exceptionError) {
console.log(`Error: ${result.execResult.exceptionError}`);
} else {
console.log(`Gas used: ${result.gasUsed}`);
}
// Check ending balance
const finalAccount = await vm.stateManager.getAccount(trader);
console.log(`Final balance: ${finalAccount.balance}`);
}
// Run the simulation
simulateFlashLoan();
```
### Performance Optimization
Optimizing your forking setup can significantly improve performance,
especially for complex test suites.
#### Efficient State Access
```ts
import { createTevmNode, http } from "tevm";
import { createAddress } from "tevm/address";
// Create a forked node
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
// Specify a block to avoid moving target issues
blockTag: 17_000_000n,
},
});
await node.ready();
// Access pattern optimization
async function optimizedStateAccess() {
const vm = await node.getVm();
const stateManager = vm.stateManager;
// โ Batch related state requests
const addresses = [
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
"0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT
"0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
].map(createAddress);
// Fetch all in parallel
const accounts = await Promise.all(
addresses.map((addr) => stateManager.getAccount(addr)),
);
// โ Reuse state manager for multiple operations
const usdcAddress = addresses[0];
// Access code and storage from the same contract
const [code, slot0, slot1] = await Promise.all([
stateManager.getContractCode(usdcAddress),
stateManager.getContractStorage(
usdcAddress,
Buffer.from("0".padStart(64, "0"), "hex"),
),
stateManager.getContractStorage(
usdcAddress,
Buffer.from("1".padStart(64, "0"), "hex"),
),
]);
return { accounts, code, storage: [slot0, slot1] };
}
```
#### Selective Forking
For some tests, you might not need the entire state of mainnet:
```ts
// Specify chainId for minimal setup when you don't need a full fork
const lightNode = createTevmNode({
// No fork specified - creates a minimal local node
common: { chainId: 1 },
});
// Only fork when you need production data
const conditionalFork =
process.env.USE_FORK === "true"
? {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
blockTag: "latest",
}
: undefined;
const node = createTevmNode({
fork: conditionalFork,
});
```
#### Cache Warmer
For critical paths, pre-warm the cache:
```ts
// Pre-warm the cache for frequently accessed contracts
async function warmCache(node) {
const vm = await node.getVm();
const contracts = [
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
"0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", // UniswapV2Router
].map(createAddress);
console.log("Warming cache...");
await Promise.all(
contracts.map((addr) =>
vm.stateManager
.getAccount(addr)
.then((account) => vm.stateManager.getContractCode(addr)),
),
);
console.log("Cache warmed!");
}
```
### Best Practices
Following these best practices will help you avoid common pitfalls when
working with forked nodes.
#### 1. RPC Provider Setup
```ts
// โ Always call http transport with empty object
const node = createTevmNode({
fork: {
transport: http("https://ethereum.quicknode.com/YOUR-API-KEY")({}),
},
});
// โ Specify a block number for deterministic tests
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
blockTag: 17_000_000n, // Fixed block for reproducibility
},
});
```
#### 2. Error Handling
```ts
// โ Add proper error handling for RPC failures
try {
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
},
});
await node.ready();
} catch (error) {
if (error.message.includes("rate limit")) {
console.error(
"RPC rate limit exceeded. Consider using a different provider.",
);
} else if (error.message.includes("network")) {
console.error("Network error. Check your internet connection.");
} else {
console.error("Fork initialization failed:", error);
}
}
```
#### 3. State Handling
```ts
// โ Use proper null checks
const account = await vm.stateManager.getAccount(address);
if (account) {
// Account exists, safe to use
account.balance += parseEther("1");
await vm.stateManager.putAccount(address, account);
}
// โ Handle potential RPC failures in state access
try {
const code = await vm.stateManager.getContractCode(contractAddress);
// Use code
} catch (error) {
console.error("Failed to fetch contract code:", error);
// Implement fallback or retry logic
}
```
#### 4. Performance Considerations
```ts
// โ Choose the right block for your needs
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
// For recent transactions, use 'latest'
// For reproducible tests, use a specific number
// For historical analysis, use a past block
blockTag: process.env.NODE_ENV === "test" ? 17_000_000n : "latest",
},
});
// โ Remember that first access is slower (RPC call)
// but subsequent accesses are fast (from cache)
```
#### 5. Testing Setup
```ts
// โ For test isolation, use a fresh fork for each test
beforeEach(async () => {
node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}),
blockTag: 17_000_000n,
},
});
await node.ready();
});
// โ For test efficiency, reuse the same fork but reset state
let baseNode;
before(async () => {
baseNode = createTevmNode({
fork: {
/* ... */
},
});
await baseNode.ready();
});
beforeEach(async () => {
// Clone the base node for each test
node = await baseNode.deepCopy();
});
```
### Next Steps
* [State Management](/core/managing-state) - Learn more about manipulating blockchain state
* [Mining Modes](/core/mining-modes) - Configure how transactions are mined in your forked node
* [Transaction Pool](/advanced/txpool) - Understand how to work with pending transactions
* [Forking Example](/examples/forking-mainnet) - See a complete example of forking mainnet
import { Callout, Steps, Button } from 'vocs/components'
## Managing State
Tevm provides powerful state management capabilities through multiple APIs to read, write, and manipulate blockchain state.
Tevm offers various approaches to manage Ethereum state, from low-level direct state access to high-level client APIs. Each approach has its benefits and use cases.
### State Management Approaches
:::code-group
```typescript [Raw API] showLineNumbers {1-2,4-6,9,10-15,18-26,29} filename="raw-state-manager.ts"
import { createTevmNode } from 'tevm'
import { EthjsAccount } from 'tevm/utils'
const node = createTevmNode()
const vm = await node.getVm()
const stateManager = vm.stateManager // [!code focus]
// Read account state
const address = '0x1234567890123456789012345678901234567890'
const account = await stateManager.getAccount(address) // [!code focus]
console.log({
balance: account.balance, // [!code focus]
nonce: account.nonce, // [!code focus]
codeHash: account.codeHash, // [!code focus]
storageRoot: account.storageRoot // [!code focus]
})
// Create or update an account
await stateManager.putAccount( // [!code focus]
address,
new EthjsAccount({
nonce: 0n,
balance: 10_000_000n,
storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
})
)
// Delete an account
await stateManager.deleteAccount(address) // [!code focus]
```
The most direct way to manage state is through the StateManager interface.
The raw StateManager API gives you maximum control but requires more detailed knowledge of Ethereum's state structure.
The `createMemoryClient` function creates a client that implements viem's public actions, making it compatible with viem's ecosystem.
```typescript [Ethers] showLineNumbers {1-3,5-6,9,12-13} filename="ethers-integration.ts"
import { createTevmNode } from 'tevm'
import { requestEip1193 } from 'tevm/decorators'
import { BrowserProvider } from 'ethers'
const node = createTevmNode().extend(requestEip1193()) // [!code focus]
const provider = new BrowserProvider(node) // [!code focus]
// Use Ethers API for standard operations
const balance = await provider.getBalance('0x1234...') // [!code focus]
// Access raw state manager for advanced operations
const vm = await node.getVm() // [!code focus]
const stateManager = vm.stateManager // [!code focus]
// Now you can use both Ethers and low-level StateManager APIs
```
Using the `requestEip1193` decorator makes your Tevm node compatible with Ethers.js Provider interface.
:::
### Advanced Features
#### State Checkpoints
Create atomic state changes that can be committed or reverted:
```typescript showLineNumbers {1,3,4-10,12-13} filename="checkpoints.ts"
const stateManager = (await node.getVm()).stateManager
await stateManager.checkpoint() // [!code focus]
try { // [!code focus]
// Batch multiple state changes
await Promise.all([
stateManager.putAccount(address, account),
stateManager.putContractStorage(address, key, value),
])
await stateManager.commit() // [!code focus]
} catch (error) { // [!code focus]
await stateManager.revert() // [!code focus]
console.error('State changes reverted:', error) // [!code focus]
}
```
Checkpoints provide transaction-like semantics, allowing you to revert changes if something goes wrong.
#### State Persistence
Save and load blockchain state:
```typescript showLineNumbers {2,5,8-9} filename="persistence.ts"
// Dump complete state
const state = await stateManager.dumpCanonicalGenesis() // [!code focus]
// Save state (example with localStorage)
localStorage.setItem('tevmState', JSON.stringify(state)) // [!code focus]
// Load saved state
const savedState = JSON.parse(localStorage.getItem('tevmState')) // [!code focus]
await stateManager.generateCanonicalGenesis(savedState) // [!code focus]
```
You can save state to any storage system - from localStorage in browsers to database systems in server environments.
#### Fork Mode
Tevm supports lazy loading with caching when forking from another network:
```typescript showLineNumbers {1-5,7,10,13} filename="fork-mode.ts"
const node = createTevmNode({
fork: {
transport: http('https://mainnet.optimism.io')
}
})
const stateManager = (await node.getVm()).stateManager // [!code focus]
// First access fetches from remote
const account = await stateManager.getAccount('0x1234...') // [!code focus]
// Subsequent access uses cache
const cachedAccount = await stateManager.getAccount('0x1234...') // [!code focus]
```
When forking, Tevm only loads the state it needs from the remote provider and caches it locally. This provides an efficient balance between performance and state availability.
### Best Practices
โ ๏ธ Error Handling
Properly handle errors from state operations
๐ก๏ธ State Isolation
Create isolated copies of state for testing
โ๏ธ Atomic Operations
Group related state changes with checkpoints
:::code-group
```typescript [Error Handling] showLineNumbers {1-14} filename="error-handling.ts"
try {
const account = await stateManager.getAccount('0x1234...')
if (!account) {
throw new Error('Account not found')
}
// Work with account
} catch (error) {
if (error instanceof MissingAccountError) {
// Handle specific error types
console.log('Account does not exist yet')
} else {
// Handle generic errors
console.error('State operation failed:', error)
}
}
```
Tevm provides specific error types for different state operations. Catch and handle these appropriately.
```typescript [State Isolation] showLineNumbers {2,5} filename="state-isolation.ts"
// Create isolated copy for testing
const isolatedState = await stateManager.deepCopy() // [!code focus]
// Make changes without affecting original
await isolatedState.putAccount(address, account) // [!code focus]
// Changes to isolatedState don't affect the original stateManager
```
State isolation is particularly useful for testing scenarios where you want to try operations without affecting your main state.
```typescript [Atomic Operations] showLineNumbers {1-12} filename="atomic-operations.ts"
await stateManager.checkpoint()
try {
// Group related changes
await Promise.all([
stateManager.putAccount(address, account),
stateManager.putContractStorage(address, key, value),
])
await stateManager.commit()
} catch (error) {
// Revert all changes if any operation fails
await stateManager.revert()
console.error('Transaction reverted:', error)
}
```
:::
### Related Resources
import { Callout, Steps, Button } from "vocs/components";
import { Card, CardGrid, FileTree } from "../../components";
## Mining Modes
Tevm Node provides several mining modes to control how and when blocks are
produced, allowing you to simulate different network conditions.
Mining modes determine when transactions are included in blocks and confirmed. By choosing the right mining mode, you can accurately simulate real blockchain networks or optimize for specific testing scenarios.
### Available Mining Modes
Mines a block after each transaction
Mines blocks at fixed time intervals
Only mines when explicitly requested
Mines when gas threshold is reached
:::code-group
```typescript [Auto Mining] showLineNumbers {2-4} filename="auto-mining.ts"
const node = createTevmNode({
miningConfig: {
// [!code focus]
type: "auto", // [!code focus]
}, // [!code focus]
});
// Send a transaction - it will be mined immediately
const txHash = await node.sendTransaction({
// transaction details
});
// Transaction is already confirmed in a block
const receipt = await node.getTransactionReceipt({ hash: txHash });
console.log("Block number:", receipt.blockNumber);
```
Automatically mines a new block after each transaction.
Quick testing and development
Immediate transaction confirmation
Simulating instant finality
```typescript [Interval Mining] showLineNumbers {2-5} filename="interval-mining.ts"
const node = createTevmNode({
miningConfig: {
// [!code focus]
type: "interval", // [!code focus]
interval: 12000, // 12 seconds, similar to Ethereum // [!code focus]
}, // [!code focus]
});
// Transaction will remain pending until the next mining interval
const txHash = await node.sendTransaction({
// transaction details
});
// Wait for the next interval (or use a Promise to be notified)
setTimeout(async () => {
const receipt = await node.getTransactionReceipt({ hash: txHash });
console.log("Block number:", receipt.blockNumber);
}, 12000);
```
Mines blocks at fixed time intervals, simulating real network block times.
Only mines blocks when explicitly requested, giving you complete control over block production.
Complex test scenarios
Precise control over block timing
Testing mempool behavior
```typescript [Gas-Based Mining] showLineNumbers {2-5} filename="gas-based-mining.ts"
const node = createTevmNode({
miningConfig: {
// [!code focus]
type: "gas", // [!code focus]
gasLimit: 15000000, // Similar to Ethereum block gas limit // [!code focus]
}, // [!code focus]
});
// Transactions accumulate in the mempool until gas threshold is reached
// A block is mined when total gas exceeds gasLimit
// Small transaction (won't trigger mining by itself)
await node.sendTransaction({
/* details for small tx */
});
// Large transaction that pushes total over threshold will trigger mining
await node.sendTransaction({
/* details for large tx */
});
```
Mines a new block when the accumulated gas usage reaches a specified threshold.
Testing gas-dependent behavior
Simulating block fullness scenarios
Performance testing with varying load
:::
### Changing Mining Modes
You can change the mining mode at any time during the node's lifecycle to
adapt to different testing phases.
```typescript showLineNumbers {2-5,8-10} filename="changing-mining-modes.ts"
// Switch to interval mining
await node.setMiningConfig({
// [!code focus]
type: "interval", // [!code focus]
interval: 5000, // 5 seconds // [!code focus]
}); // [!code focus]
// Switch to manual mining
await node.setMiningConfig({
// [!code focus]
type: "manual", // [!code focus]
}); // [!code focus]
```
### Event Handlers
Tevm provides event handlers for mining operations, allowing you to monitor blocks, receipts, and logs in real-time:
```typescript showLineNumbers {8-25} filename="mine-with-events.ts"
import { createMemoryClient } from "tevm";
const client = createMemoryClient();
// Mine with event handlers
const result = await client.mine({
blockCount: 2,
// Monitor each block as it's mined
onBlock: (block, next) => {
console.log(`Block #${block.header.number} mined:`, {
hash: block.hash().toString("hex"),
timestamp: block.header.timestamp,
gasUsed: block.header.gasUsed,
});
next?.(); // Must call next to continue processing
},
// Monitor transaction receipts
onReceipt: (receipt, blockHash, next) => {
console.log(`Receipt for tx ${receipt.transactionHash}:`, {
blockHash,
gasUsed: receipt.gasUsed,
});
next?.();
},
// Monitor logs from transactions
onLog: (log, receipt, next) => {
console.log(`Log from ${log.address}:`, {
topics: log.topics,
data: log.data,
});
next?.();
},
});
```
Event handlers are called synchronously by default. Always call the `next()`
function to continue processing, similar to middleware patterns.
### Best Practices
#### Choose the Right Mode for Your Use Case
Use auto mining for the fastest feedback loop during
development
Use manual mining for deterministic tests with precise control
Use interval mining to simulate real-world network conditions
Use gas mining to test application behavior under congestion
#### Consider Performance Implications
Different mining modes have varying performance characteristics:
* Auto mining can be resource-intensive with many transactions
* Interval mining might delay transaction processing
* Gas mining helps simulate network congestion but requires careful configuration
#### Testing Best Practices
```typescript filename="testing-strategies.ts"
// For time-sensitive logic testing
const timeNode = createTevmNode({
miningConfig: { type: "interval", interval: 10000 },
});
// For deterministic test cases
const deterministicNode = createTevmNode({
miningConfig: { type: "manual" },
});
// For gas-sensitive contract testing
const gasNode = createTevmNode({
miningConfig: { type: "gas", gasLimit: 8000000 },
});
```
### Example: Testing Different Mining Modes
:::code-group
```typescript [Comparative Example] showLineNumbers {4-6,9-13,16-18,21-24} filename="mining-mode-comparison.ts"
import { createTevmNode } from 'tevm'
// Auto mining for quick tests
const autoNode = createTevmNode({ // [!code focus]
miningConfig: { type: 'auto' } // [!code focus]
}) // [!code focus]
// Interval mining for realistic scenarios
const intervalNode = createTevmNode({ // [!code focus]
miningConfig: { // [!code focus]
type: 'interval', // [!code focus]
interval: 12000 // [!code focus]
} // [!code focus]
})
// Manual mining for controlled tests
const manualNode = createTevmNode({ // [!code focus]
miningConfig: { type: 'manual' } // [!code focus]
}) // [!code focus]
// Test transaction processing
await autoNode.sendTransaction({...}) // Mines immediately // [!code focus]
await intervalNode.sendTransaction({...}) // Mines after interval // [!code focus]
await manualNode.sendTransaction({...}) // Stays pending until manual mine // [!code focus]
await manualNode.mine() // Now the transaction is mined // [!code focus]
```
```typescript [Real-world Simulation] filename="real-world-simulation.ts"
import { createTevmNode, http } from "tevm";
import { mainnet } from "tevm/chains";
// Create a node that simulates mainnet conditions
const node = createTevmNode({
// Fork from mainnet
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
common: mainnet,
},
// Use Ethereum's ~12 second block time
miningConfig: {
type: "interval",
interval: 12000,
},
});
// Transaction will stay in mempool until next block
const txHash = await node.sendTransaction({
from: "0x123...",
to: "0x456...",
value: 1000000000000000000n,
});
console.log("Transaction is pending in the mempool...");
// You can monitor pending transactions
const pending = await node.getTxPoolContent();
console.log("Pending transactions:", pending);
// Wait for the block to be mined
setTimeout(async () => {
const receipt = await node.getTransactionReceipt({
hash: txHash,
});
console.log("Transaction mined in block:", receipt.blockNumber);
}, 12000);
```
:::
### Related Resources
import { Callout } from "vocs/components";
import { Card, CardGrid } from "../../components";
## TevmNode Interface
The TevmNode interface is the core API of Tevm, providing direct access to all
Ethereum execution capabilities without abstraction layers.
The `TevmNode` interface represents the foundational layer of Tevm's architecture. It exposes powerful low-level access to all the essential components that make up an Ethereum node - from the EVM execution engine to transaction processing and state management.
### Interface Overview
```ts
export type TevmNode<
TMode extends "fork" | "normal" = "fork" | "normal",
TExtended = {},
> = {
// Logging & status
readonly logger: Logger;
status: "INITIALIZING" | "READY" | "SYNCING" | "MINING" | "STOPPED";
readonly ready: () => Promise;
// Core components
readonly getVm: () => Promise;
readonly getTxPool: () => Promise;
readonly getReceiptsManager: () => Promise;
readonly miningConfig: MiningConfig;
// Forking support
readonly mode: TMode;
readonly forkTransport?: { request: EIP1193RequestFn };
// Account management
readonly getImpersonatedAccount: () => Address | undefined;
readonly setImpersonatedAccount: (address: Address | undefined) => void;
// Event filtering
readonly setFilter: (filter: Filter) => void;
readonly getFilters: () => Map;
readonly removeFilter: (id: Hex) => void;
// Extensibility
readonly extend: (
decorator: (client: TevmNode) => TExtension,
) => TevmNode;
// State management
readonly deepCopy: () => Promise>;
} & EIP1193EventEmitter &
TExtended;
```
When using TypeScript, the interface is fully typed, providing excellent
autocomplete and type checking as you develop.
### Key Capabilities
Direct access to the Ethereum Virtual Machine for executing code
Working with pending transactions
Advanced account management and impersonation
Creating and managing event filters and subscriptions
Adding custom functionality through decorators
### Initialization & Status
Always wait for node initialization using the `ready()` method before
interacting with other node components.
```ts
import { createTevmNode } from "tevm";
// Create a node instance
const node = createTevmNode();
// Wait for initialization to complete
await node.ready();
// Check the current status
console.log(`Node status: ${node.status}`); // 'READY'
// Access the logger for debugging
node.logger.debug("Node successfully initialized");
```
The node status can be one of the following values:
* `INITIALIZING`: Node is starting up and components are being created
* `READY`: Node is fully initialized and ready for use
* `SYNCING`: Node is synchronizing state (usually in fork mode)
* `MINING`: Node is currently mining a block
* `STOPPED`: Node has been stopped or encountered a fatal error
### Virtual Machine Access
The VM (Virtual Machine) is the heart of Tevm, providing direct access to EVM
execution capabilities.
```ts
import { createTevmNode } from "tevm";
import { createAddress } from "tevm/address";
import { hexToBytes } from "viem";
const node = createTevmNode();
await node.ready();
// Get access to the VM
const vm = await node.getVm();
// Execute a transaction directly
import { createImpersonatedTx } from "tevm/tx";
const tx = createImpersonatedTx({
to: "0x1234567890123456789012345678901234567890",
value: 1000000000000000000n, // 1 ETH
nonce: 0n,
gasLimit: 21000n,
gasPrice: 10000000000n,
});
const txResult = await vm.runTx({ tx });
console.log(
"Transaction executed:",
txResult.execResult.exceptionError
? `Failed: ${txResult.execResult.exceptionError}`
: "Success!",
);
// Execute EVM bytecode directly
const evmResult = await vm.evm.runCall({
to: createAddress("0x1234567890123456789012345678901234567890"),
caller: createAddress("0x5678901234567890123456789012345678901234"),
data: hexToBytes(
"0xa9059cbb000000000000000000000000abcdef0123456789abcdef0123456789abcdef0000000000000000000000000000000000000000000000008ac7230489e80000",
), // transfer(address,uint256)
gasLimit: 100000n,
});
console.log("EVM call result:", evmResult.execResult);
// Hook into EVM execution for debugging
vm.evm.events.on("step", (data, next) => {
console.log(`${data.pc}: ${data.opcode.name}`);
next?.(); // Continue to next step
});
```
### Transaction Pool Management
The Transaction Pool (TxPool) keeps track of pending transactions before they
are included in blocks.
```ts
import { createTevmNode } from "tevm";
import { parseEther } from "viem";
const node = createTevmNode();
await node.ready();
// Get the transaction pool
const txPool = await node.getTxPool();
// Add transactions to the pool
await txPool.add({
from: "0x1234567890123456789012345678901234567890",
to: "0x5678901234567890123456789012345678901234",
value: parseEther("1.5"),
gasLimit: 21000n,
maxFeePerGas: 30000000000n,
});
// Check pool content
const pendingTxs = await txPool.content();
console.log("Pending transactions:", pendingTxs.pending);
// Get ordered transactions (by price and nonce)
const orderedTxs = await txPool.txsByPriceAndNonce();
console.log("Ordered transactions:", orderedTxs);
// Get pending transactions for a specific address
const txsForAddress = await txPool.contentFrom(
"0x1234567890123456789012345678901234567890",
);
console.log("Transactions for address:", txsForAddress);
```
### Receipt & Log Management
The ReceiptsManager tracks transaction receipts and logs, allowing you to
query and filter events.
```ts
import { createTevmNode } from "tevm";
const node = createTevmNode();
await node.ready();
// Get the receipts manager
const receiptsManager = await node.getReceiptsManager();
// After a transaction is executed, get its receipt
const receipt = await receiptsManager.getReceiptByTxHash(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
);
// Query logs with filters
const transferLogs = await receiptsManager.getLogs({
fromBlock: 0n,
toBlock: "latest",
address: "0x1234567890123456789012345678901234567890", // Optional: contract address
topics: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer event signature
null, // Any from address
"0x0000000000000000000000005678901234567890123456789012345678901234", // Filter by to address
],
});
console.log("Transfer logs:", transferLogs);
// Create a subscription for new logs
const subId = await receiptsManager.newLogSubscription({
address: "0x1234567890123456789012345678901234567890",
topics: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
],
});
// Handle new matching logs
receiptsManager.on("log", (log) => {
if (log.subscriptionId === subId) {
console.log("New transfer detected:", log);
}
});
```
### Account Impersonation
Account impersonation lets you send transactions as any address in fork mode,
without needing the private key.
```ts
import { createTevmNode, http } from "tevm";
import { parseEther } from "viem";
// Create a forked node
const node = createTevmNode({
fork: {
transport: http("https://mainnet.infura.io/v3/YOUR-KEY"),
},
});
await node.ready();
// Impersonate a known address (like a whale or contract owner)
node.setImpersonatedAccount("0x28C6c06298d514Db089934071355E5743bf21d60"); // Example whale address
// Get the VM
const vm = await node.getVm();
// Send a transaction as the impersonated account
import { createImpersonatedTx } from "tevm/tx";
const tx = createImpersonatedTx({
from: "0x28C6c06298d514Db089934071355E5743bf21d60", // Impersonated address
to: "0x1234567890123456789012345678901234567890",
value: parseEther("10"),
gasLimit: 21000n,
});
const txResult = await vm.runTx({ tx });
console.log("Transaction result:", txResult.execResult);
// Check if an address is being impersonated
const currentImpersonated = node.getImpersonatedAccount();
console.log("Currently impersonating:", currentImpersonated);
// Stop impersonating
node.setImpersonatedAccount(undefined);
```
### Event Filtering
Event filters allow you to track and query blockchain events based on various
criteria.
```ts
import { createTevmNode } from "tevm";
import { formatEther, parseEther } from "viem";
const node = createTevmNode();
await node.ready();
// Create a filter for all "Transfer" events
node.setFilter({
id: "0x1", // Custom ID
fromBlock: 0n,
toBlock: "latest",
address: "0x1234567890123456789012345678901234567890", // Optional: Filter by contract
topics: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer event signature
],
});
// Create a filter for a specific address receiving tokens
node.setFilter({
id: "0x2",
fromBlock: 0n,
toBlock: "latest",
topics: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer
null, // Any sender
"0x0000000000000000000000001234567890123456789012345678901234567890", // Specific recipient (padded)
],
});
// Get all active filters
const filters = node.getFilters();
console.log(`Active filters: ${filters.size}`);
// After executing transactions, get the logs from a filter
const receiptManager = await node.getReceiptsManager();
const logs = await receiptManager.getFilterLogs("0x1");
// Format and display the transfer logs
logs.forEach((log) => {
// Decode Transfer(address,address,uint256)
const from = "0x" + log.topics[1].slice(26);
const to = "0x" + log.topics[2].slice(26);
const value = BigInt(log.data);
console.log(`Transfer: ${from} -> ${to}: ${formatEther(value)} ETH`);
});
// Remove a filter when done
node.removeFilter("0x1");
```
### Extensibility
The extend method allows you to add custom functionality to any TevmNode
instance, creating powerful abstractions.
```ts
import { createTevmNode } from "tevm";
import { parseEther, formatEther } from "viem";
const node = createTevmNode();
await node.ready();
// Extend the node with custom methods
const enhancedNode = node.extend((baseNode) => ({
// Add a method to get an account's balance
async getBalance(address) {
const vm = await baseNode.getVm();
const account = await vm.stateManager.getAccount(address);
return account.balance;
},
// Add a method to transfer ETH between accounts
async transferETH(from, to, amount) {
const vm = await baseNode.getVm();
import { createImpersonatedTx } from "tevm/tx";
// Create the impersonated transaction
const tx = createImpersonatedTx({
from,
to,
value: amount,
gasLimit: 21000n,
});
// Execute the transfer
const result = await vm.runTx({ tx });
return {
success: !result.execResult.exceptionError,
gasUsed: result.gasUsed,
};
},
// Add a method to get all account balances
async getAllBalances(addresses) {
const results = {};
for (const addr of addresses) {
results[addr] = formatEther(await this.getBalance(addr));
}
return results;
},
}));
// Use the extended methods
const balance = await enhancedNode.getBalance(
"0x1234567890123456789012345678901234567890",
);
console.log(`Balance: ${formatEther(balance)} ETH`);
// Transfer ETH
const transfer = await enhancedNode.transferETH(
"0x1234567890123456789012345678901234567890",
"0x5678901234567890123456789012345678901234",
parseEther("1.5"),
);
console.log(
`Transfer ${transfer.success ? "succeeded" : "failed"}, gas used: ${transfer.gasUsed}`,
);
// Get multiple balances at once
const balances = await enhancedNode.getAllBalances([
"0x1234567890123456789012345678901234567890",
"0x5678901234567890123456789012345678901234",
]);
console.log("Account balances:", balances);
```
### State Management
The deepCopy method creates fully independent node instances with identical
state, perfect for testing different scenarios.
```ts
import { createTevmNode } from "tevm";
import { parseEther } from "viem";
// Create a base node and perform initial setup
const baseNode = createTevmNode();
await baseNode.ready();
// Set up initial state
const vm = await baseNode.getVm();
await vm.stateManager.putAccount("0x1234567890123456789012345678901234567890", {
nonce: 0n,
balance: parseEther("100"),
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
});
// Create an independent copy for a specific test scenario
const scenarioNode = await baseNode.deepCopy();
// Modify state in the copy (without affecting the original)
const scenarioVm = await scenarioNode.getVm();
await scenarioVm.stateManager.putAccount(
"0x1234567890123456789012345678901234567890",
{
nonce: 0n,
balance: parseEther("200"), // Different balance in this scenario
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
},
);
// Both nodes now have independent state
const originalAccount = await (
await baseNode.getVm()
).stateManager.getAccount("0x1234567890123456789012345678901234567890");
const modifiedAccount = await (
await scenarioNode.getVm()
).stateManager.getAccount("0x1234567890123456789012345678901234567890");
console.log("Original balance:", originalAccount.balance); // 100 ETH
console.log("Scenario balance:", modifiedAccount.balance); // 200 ETH
```
### Practical Examples
:::code-group
```ts [Contract Deployment]
import { createTevmNode } from "tevm";
import { hexToBytes } from "viem";
const node = createTevmNode();
await node.ready();
// ERC20 contract bytecode (simplified for example)
const bytecode = "0x60806040..."; // Contract bytecode (truncated)
// Deploy the contract
const vm = await node.getVm();
const deployResult = await vm.runTx({
tx: {
nonce: 0n,
gasLimit: 2000000n,
gasPrice: 10000000000n,
data: hexToBytes(bytecode),
},
});
if (deployResult.execResult.exceptionError) {
throw new Error(
`Deployment failed: ${deployResult.execResult.exceptionError}`,
);
}
// Get the created contract address
const contractAddress = deployResult.createdAddress;
console.log(`Contract deployed at: ${contractAddress}`);
// Now you can interact with the contract
const callResult = await vm.runTx({
tx: {
to: contractAddress,
data: hexToBytes(
"0x70a08231000000000000000000000000" +
"1234567890123456789012345678901234567890".slice(2),
), // balanceOf(address)
gasLimit: 100000n,
},
});
console.log("Call result:", callResult.execResult.returnValue);
```
```ts [Contract Debugging]
import { createTevmNode } from "tevm";
import { hexToBytes } from "viem";
const node = createTevmNode();
await node.ready();
const vm = await node.getVm();
// Set up debugging hooks
const opcodeCounts = {};
const memoryAccesses = [];
const stackChanges = [];
vm.evm.events.on("step", (data, next) => {
// Count opcodes
opcodeCounts[data.opcode.name] = (opcodeCounts[data.opcode.name] || 0) + 1;
// Track memory operations
if (["MLOAD", "MSTORE", "MSTORE8"].includes(data.opcode.name)) {
memoryAccesses.push({
op: data.opcode.name,
offset: data.stack[data.stack.length - 1]?.toString(16),
});
}
// Track stack changes
if (data.opcode.name === "PUSH1") {
stackChanges.push({
pc: data.pc,
op: data.opcode.name,
value: data.opcode.pushValue?.toString(16),
});
}
// Continue execution
next?.();
});
// Execute a transaction with the debugging hooks active
const result = await vm.runTx({
tx: {
to: "0x1234567890123456789012345678901234567890", // Contract address
data: hexToBytes(
"0xa9059cbb000000000000000000000000abcdef0123456789abcdef0123456789abcdef0000000000000000000000000000000000000000000000008ac7230489e80000",
), // transfer(address,uint256)
gasLimit: 100000n,
},
});
// Output debugging information
console.log("Execution complete");
console.log(`Gas used: ${result.gasUsed}`);
console.log(
"Most common opcodes:",
Object.entries(opcodeCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 5),
);
console.log("Memory accesses:", memoryAccesses.length);
console.log("PUSH operations:", stackChanges.length);
// Clean up
vm.evm.events.removeAllListeners("step");
```
```ts [Chain Simulation]
import { createTevmNode } from "tevm";
import { parseEther } from "viem";
async function simulateICO() {
// Create a fresh node for the simulation
const node = createTevmNode({
miningConfig: { type: "auto" }, // Auto-mine transactions
});
await node.ready();
const vm = await node.getVm();
// Setup accounts
const deployer = "0x1111111111111111111111111111111111111111";
const investors = [
"0x2222222222222222222222222222222222222222",
"0x3333333333333333333333333333333333333333",
"0x4444444444444444444444444444444444444444",
];
// Fund accounts
await vm.stateManager.putAccount(deployer, {
nonce: 0n,
balance: parseEther("100"),
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
});
for (const investor of investors) {
await vm.stateManager.putAccount(investor, {
nonce: 0n,
balance: parseEther("10"),
codeHash:
"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
storageRoot:
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
});
}
// Deploy ICO contract (simplified)
// In reality, you would deploy a real contract here
console.log("Deploying ICO contract...");
// Simulate investors participating
for (const investor of investors) {
console.log(`Investor ${investor} participating...`);
// In a real simulation, you would call the contract's participate method
// This is a simplified example
await vm.runTx({
tx: {
from: investor,
to: deployer, // In reality, this would be the contract address
value: parseEther("5"),
gasLimit: 21000n,
},
});
}
// Check final state
const deployerAccount = await vm.stateManager.getAccount(deployer);
console.log(`Deployer final balance: ${deployerAccount.balance}`);
for (const investor of investors) {
const investorAccount = await vm.stateManager.getAccount(investor);
console.log(
`Investor ${investor} final balance: ${investorAccount.balance}`,
);
}
// In a real simulation, you would also check token balances
}
// Run the simulation
simulateICO().catch(console.error);
```
:::
### Best Practices
Following these best practices will help you avoid common pitfalls when
working with the TevmNode interface.
#### 1. Initialization Flow
Always wait for the node to be fully initialized before accessing components:
```ts
const node = createTevmNode();
await node.ready(); // Essential: Ensures all components are initialized
// Now safe to use the node
const vm = await node.getVm();
```
#### 2. Error Handling
Implement robust error handling for EVM operations:
```ts
try {
const vm = await node.getVm();
const result = await vm.runTx({
tx: {
/* ... */
},
});
if (result.execResult.exceptionError) {
console.error(`Execution failed: ${result.execResult.exceptionError}`);
console.error(`At PC: ${result.execResult.exceptionError.pc}`);
// Handle the specific error
}
} catch (error) {
// Handle unexpected errors
console.error("Unexpected error:", error.message);
}
```
#### 3. Resource Management
Clean up resources when they're no longer needed:
```ts
// Remove filters when done
node.getFilters().forEach((_, id) => node.removeFilter(id));
// Remove event listeners
const vm = await node.getVm();
vm.evm.events.removeAllListeners("step");
// For subscriptions
const receiptsManager = await node.getReceiptsManager();
receiptsManager.removeAllListeners("log");
```
#### 4. State Isolation
Use `deepCopy` for testing different scenarios:
```ts
const baseNode = createTevmNode();
await baseNode.ready();
// Set up initial state
// ...
// For each test case, create an independent copy
async function runTestCase(scenario) {
const testNode = await baseNode.deepCopy();
// Modify state for this specific test
// ...
// Run the test
// ...
// Each test has isolated state that doesn't affect other tests
}
```
#### 5. Optimizing Performance
For heavy workloads, consider these optimizations:
```ts
// Disable profiling when not needed
const node = createTevmNode({
profiler: false,
});
// Use direct VM access for bulk operations
const vm = await node.getVm();
const stateManager = vm.stateManager;
// Batch state changes
const addresses = ["0x1111...", "0x2222...", "0x3333..."];
for (const address of addresses) {
await stateManager.putAccount(address, {
// Account data
});
}
// Only mine when necessary (if using manual mining)
await node.mine({ blocks: 1 });
```
### Type Safety
The `TevmNode` interface is fully typed with TypeScript, providing excellent development-time safety:
```ts
import type { TevmNode } from "tevm/node";
// Function that works with any TevmNode
function setupNode(node: TevmNode) {
return async () => {
await node.ready();
// Fork-specific operations with type checking
if (node.mode === "fork") {
node.setImpersonatedAccount("0x...");
return {
mode: "fork",
impersonatedAccount: node.getImpersonatedAccount(),
};
}
return { mode: "normal" };
};
}
// With extension types
function createEnhancedNode() {
const baseNode = createTevmNode();
const enhancedNode = baseNode.extend((base) => ({
async getBalance(address: string): Promise {
const vm = await base.getVm();
const account = await vm.stateManager.getAccount(address);
return account.balance;
},
}));
// TypeScript knows enhancedNode has getBalance method
return enhancedNode;
}
```
### Next Steps
Learn how to create and manage forks from live Ethereum networks
Understand the state management capabilities of Tevm
Explore the different block mining strategies
Use Tevm's Ethereum-compatible JSON-RPC interface
Work with low-level EVM execution events
Add custom precompiled contracts to extend EVM functionality
## Account Management
Tevm provides two key actions for managing account state: `getAccountHandler` and `setAccountHandler`.
### getAccountHandler
The `getAccountHandler` action allows you to retrieve the current state of an account.
#### Parameters
```ts
type GetAccountParams = {
// Required address of the account
address: Address
// Optional block tag to query state from
blockTag?: 'latest' | 'pending' | 'earliest' | number
// Whether to return storage (can be expensive)
returnStorage?: boolean
}
```
#### Return Type
```ts
type GetAccountResult = {
// Address of the account
address: Address
// Current nonce
nonce: bigint
// Balance in wei
balance: bigint
// Deployed bytecode (if contract)
deployedBytecode: Hex
// Storage root
storageRoot: Hex
// Code hash
codeHash: Hex
// Whether this is a contract
isContract: boolean
// Whether account is empty
isEmpty: boolean
// Storage (if returnStorage=true)
storage?: { [key: Hex]: Hex }
// Any errors that occurred
errors?: TevmGetAccountError[]
}
```
#### Example
```ts
import { createTevmNode } from 'tevm'
import { getAccountHandler } from 'tevm/actions'
const node = createTevmNode()
const account = await getAccountHandler(node)({
address: '0x...',
blockTag: 'latest',
returnStorage: true
})
console.log('Balance:', account.balance)
console.log('Nonce:', account.nonce)
if (account.isContract) {
console.log('Code:', account.deployedBytecode)
console.log('Storage:', account.storage)
}
```
### setAccountHandler
The `setAccountHandler` action allows you to modify account state directly.
#### Parameters
```ts
type SetAccountParams = {
// Required address to modify
address: Address
// New nonce value
nonce?: bigint
// New balance in wei
balance?: bigint
// New deployed bytecode
deployedBytecode?: Hex
// New storage values
state?: { [key: Hex]: Hex }
}
```
#### Return Type
```ts
type SetAccountResult = {
// Any errors that occurred
errors?: TevmSetAccountError[]
}
```
#### Examples
##### 1. Setting Account Balance
```ts
import { setAccountHandler } from 'tevm/actions'
await setAccountHandler(node)({
address: '0x...',
balance: parseEther('100')
})
```
##### 2. Deploying Contract Code
```ts
await setAccountHandler(node)({
address: contractAddress,
deployedBytecode: '0x...',
state: {
// Initial storage values
'0x0000...': '0x0000...'
}
})
```
##### 3. Modifying Multiple Properties
```ts
await setAccountHandler(node)({
address: '0x...',
nonce: 5n,
balance: parseEther('10'),
state: {
[slot1]: value1,
[slot2]: value2
}
})
```
### Best Practices
1. **Storage Management**:
```ts
// Avoid fetching storage unless needed
const account = await getAccountHandler(node)({
address: '0x...',
returnStorage: false // default
})
```
2. **State Consistency**:
```ts
// Check account exists before modifying
const account = await getAccountHandler(node)({ address })
if (!account.isEmpty) {
await setAccountHandler(node)({
address,
balance: account.balance + amount
})
}
```
3. **Error Handling**:
```ts
const result = await setAccountHandler(node)({
address: '0x...',
balance: newBalance,
throwOnFail: false
})
if (result.errors) {
console.error('Failed to set account:', result.errors)
}
```
### Related Topics
* [State Management](../core/managing-state)
* [Call API](./tevm-call)
* [JSON-RPC Support](./json-rpc)
### See Also
* [JSON-RPC API](/api/json-rpc)
* [Client Types](#TODO)
* [Actions Reference](/reference/actions)
* [EIP-1193 Specification](https://eips.ethereum.org/EIPS/eip-1193)
* [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/)
## EVM Events
Tevm Node provides access to low-level EVM events, allowing you to monitor and debug contract execution at a granular level.
### Available Events
```ts
type EVMEvent = {
// Emitted when a new contract is created
newContract: (data: {
address: Address, // Contract address
code: Uint8Array // Contract bytecode
}, next?: () => void) => void
// Emitted before a message (call) is processed
beforeMessage: (data: Message, next?: () => void) => void
// Emitted after a message (call) is processed
afterMessage: (data: EVMResult, next?: () => void) => void
// Emitted on each EVM step (instruction execution)
step: (data: InterpreterStep, next?: () => void) => void
}
```
#### The InterpreterStep Object
The `step` event handler receives a detailed `InterpreterStep` object with the following properties:
```ts
interface InterpreterStep {
// Program counter - current position in the bytecode
pc: number
// Current opcode information
opcode: {
name: string // Opcode name (e.g., 'SSTORE', 'CALL')
fee: number // Base gas fee of the opcode
dynamicFee?: bigint // Additional dynamic gas fee (if applicable)
isAsync: boolean // Whether opcode is asynchronous
}
// Gas information
gasLeft: bigint // Remaining gas
gasRefund: bigint // Gas refund accumulator
// EVM state
stateManager: StateManager // Reference to the StateManager instance
stack: Uint8Array[] // Current EVM stack contents
returnStack: bigint[] // Return stack for RETURNSUB operations
// Account information
account: Account // Account which owns the code running
address: Address // Address of the current account
// Execution context
depth: number // Current call depth (starts at 0 for top-level call)
memory: Uint8Array // Current EVM memory contents
memoryWordCount: bigint // Current size of memory in words (32 bytes)
codeAddress: Address // Address of the code being executed
// (differs from address in DELEGATECALL/CALLCODE)
}
```
Where:
* [`Address`](/reference/address) is Tevm's address type (from `tevm/address`)
* [`StateManager`](/reference/state) is the state manager interface (from `tevm/state`)
* [`Account`](/reference/node) is Tevm's account representation
#### The NewContractEvent Object
The `onNewContract` event handler receives a `NewContractEvent` object with the following properties:
```ts
interface NewContractEvent {
// Contract information
address: Address // Address of the newly created contract
code: Uint8Array // Deployed contract bytecode
}
```
#### The Message Object
The `beforeMessage` event handler receives a `Message` object with the following properties:
```ts
interface Message {
// Call information
to?: Address // Target address (undefined for contract creation)
value: bigint // Value sent with the call (in wei)
caller: Address // Address of the account that initiated this call
// Gas information
gasLimit: bigint // Gas limit for this call
// Data and code
data: Uint8Array // Input data to the call
code?: Uint8Array // Contract code for the call
codeAddress?: Address // Address of contract code
// Call type
depth: number // Call depth
isStatic: boolean // Whether the call is static (view)
isCompiled: boolean // Whether this is precompiled contract code
delegatecall: boolean // Whether this is a DELEGATECALL
callcode: boolean // Whether this is a CALLCODE
// Other
salt?: Uint8Array // Salt for CREATE2 calls
authcallOrigin?: Address // Origin address for AUTH calls
}
```
#### The EVMResult Object
The `afterMessage` event handler receives an `EVMResult` object with the following properties:
```ts
interface EVMResult {
execResult: {
// Return information
returnValue: Uint8Array // Return data from the call
executionGasUsed: bigint // Gas used in execution
gasRefund?: bigint // Gas refunded
// Error information
exceptionError?: { // Error encountered during execution
error: string // Error type (e.g., 'revert', 'out of gas')
errorType?: string // Additional error type information
}
// State
logs?: Log[] // Logs emitted during execution
selfdestruct?: Record // Self-destructed addresses
gas?: bigint // Remaining gas
}
// Other information
gasUsed: bigint // Total gas used (including intrinsic costs)
createdAddress?: Address // Address of created contract (if any)
gasRefund?: bigint // Total gas refunded
}
```
### Using with tevmCall Family
The recommended way to access EVM events is through the tevmCall family of methods. Tevm offers two API styles for this:
#### Client-based API (batteries included)
```ts
import { createMemoryClient } from 'tevm'
import { encodeFunctionData } from 'viem'
const client = createMemoryClient()
// Listen for EVM steps and other events during execution
const result = await client.tevmCall({
to: contractAddress,
data: encodeFunctionData({
abi,
functionName: 'myFunction',
args: [arg1, arg2]
}),
// Listen for EVM steps
onStep: (step, next) => {
console.log('EVM Step:', {
pc: step.pc, // Program counter
opcode: step.opcode, // Current opcode
gasLeft: step.gasLeft, // Remaining gas
stack: step.stack, // Stack contents
depth: step.depth, // Call depth
})
next?.()
},
// Listen for contract creation
onNewContract: (data, next) => {
console.log('New contract deployed:', {
address: data.address.toString(),
codeSize: data.code.length,
})
next?.()
},
// Listen for message execution
onBeforeMessage: (message, next) => {
console.log('Executing message:', {
to: message.to?.toString(),
value: message.value.toString(),
delegatecall: message.delegatecall,
})
next?.()
},
onAfterMessage: (result, next) => {
console.log('Message result:', {
gasUsed: result.execResult.executionGasUsed.toString(),
returnValue: result.execResult.returnValue.toString('hex'),
error: result.execResult.exceptionError?.error,
})
next?.()
}
})
```
#### Tree-shakable API
```ts
import { tevmCall } from 'tevm/actions'
import { createClient } from 'viem'
import { createTevmNode } from 'tevm/node'
import { requestEip1193 } from 'tevm/decorators'
import { encodeFunctionData } from 'viem'
// Create Tevm Node as a viem transport
const tevmTransport = createTevmTransport()
// Create Viem client
const client = createClient({
transport: tevmTransport,
})
// Listen for EVM steps and other events during execution
const result = await tevmCall(client, {
to: contractAddress,
data: encodeFunctionData({
abi,
functionName: 'myFunction',
args: [arg1, arg2]
}),
// Event handlers work the same way with both API styles
onStep: (step, next) => {
console.log('EVM Step:', {
pc: step.pc,
opcode: step.opcode,
gasLeft: step.gasLeft,
stack: step.stack,
depth: step.depth,
})
next?.()
},
onNewContract: (data, next) => {
console.log('New contract deployed:', {
address: data.address.toString(),
codeSize: data.code.length,
})
next?.()
},
onBeforeMessage: (message, next) => {
console.log('Executing message:', {
to: message.to?.toString(),
value: message.value.toString(),
delegatecall: message.delegatecall,
})
next?.()
},
onAfterMessage: (result, next) => {
console.log('Message result:', {
gasUsed: result.execResult.executionGasUsed.toString(),
returnValue: result.execResult.returnValue.toString('hex'),
error: result.execResult.exceptionError?.error,
})
next?.()
}
})
```
### Advanced Examples
#### Debugging
You can create a debug tracer to collect comprehensive execution data:
```ts
import { createMemoryClient } from 'tevm'
import { tevmCall } from 'tevm/actions'
const client = createMemoryClient()
// Create a debug tracer
const trace = {
steps: [],
contracts: new Set(),
errors: [],
}
// Execute with tracing
const result = await tevmCall(client, {
to: contractAddress,
data: '0x...',
// Track each EVM step
onStep: (step, next) => {
trace.steps.push({
pc: step.pc,
opcode: step.opcode.name,
gasCost: step.opcode.fee,
stack: step.stack.map(item => item.toString(16)),
})
next?.()
},
// Track contract creation
onNewContract: (data, next) => {
trace.contracts.add(data.address.toString())
next?.()
},
// Track errors
onAfterMessage: (result, next) => {
if (result.execResult.exceptionError) {
trace.errors.push({
error: result.execResult.exceptionError.error,
returnData: result.execResult.returnValue.toString('hex'),
})
}
next?.()
}
})
console.log('Execution trace:', {
stepCount: trace.steps.length,
contracts: Array.from(trace.contracts),
errors: trace.errors,
})
```
#### Gas Profiling
Create a gas profiler to analyze opcode costs:
```ts
import { createMemoryClient } from 'tevm'
import { tevmCall } from 'tevm/actions'
const client = createMemoryClient()
// Create a gas profiler
const profile = {
opcodes: new Map(),
totalGas: 0n,
}
// Execute with profiling
const result = await tevmCall(client, {
to: contractAddress,
data: '0x...',
onStep: (step, next) => {
const opName = step.opcode.name
const gasCost = BigInt(step.opcode.fee)
const stats = profile.opcodes.get(opName) || {
count: 0,
totalGas: 0n
}
stats.count++
stats.totalGas += gasCost
profile.totalGas += gasCost
profile.opcodes.set(opName, stats)
next?.()
}
})
// Get gas usage by opcode
for (const [opcode, stats] of profile.opcodes) {
console.log(`${opcode}:`, {
count: stats.count,
totalGas: stats.totalGas.toString(),
percentageOfTotal: Number(stats.totalGas * 100n / profile.totalGas),
})
}
```
#### Error Handling
Handle errors in execution:
```ts
import { createMemoryClient } from 'tevm'
import { tevmCall } from 'tevm/actions'
const client = createMemoryClient()
const result = await tevmCall(client, {
to: contractAddress,
data: '0x...',
onAfterMessage: (result, next) => {
if (result.execResult.exceptionError) {
const error = result.execResult.exceptionError
switch (error.error) {
case 'out of gas':
console.error('Transaction ran out of gas')
break
case 'revert':
console.error('Transaction reverted:',
result.execResult.returnValue.toString('hex'))
break
case 'invalid opcode':
console.error('Invalid opcode encountered')
break
default:
console.error('Unknown error:', error)
}
}
next?.()
}
})
```
### Best Practices
1. **Always Call Next**
```ts
// Important: Call next to continue execution
onStep: (step, next) => {
// Process step...
next?.()
}
```
2. **Handle Errors Gracefully**
```ts
onStep: (step, next) => {
try {
// Process step...
} catch (error) {
console.error('Error processing step:', error)
}
next?.()
}
```
3. **Be Efficient**
```ts
// Focus on information you need
onStep: (step, next) => {
// Only log SSTORE operations
if (step.opcode.name === 'SSTORE') {
console.log('Storage write:', {
key: step.stack[step.stack.length - 1].toString(16),
value: step.stack[step.stack.length - 2].toString(16)
})
}
next?.()
}
```
### Mining Events
Tevm also provides event handlers for block mining operations. You can use these to monitor and respond to newly mined blocks, transaction receipts, and logs:
```ts
import { createMemoryClient } from 'tevm'
import { mine } from 'tevm/actions'
const client = createMemoryClient()
// Mine blocks with event handlers
const result = await client.mine({
blockCount: 1,
// Listen for new blocks
onBlock: (block, next) => {
console.log('New block mined:', {
number: block.header.number,
hash: block.hash().toString('hex'),
timestamp: block.header.timestamp
})
next?.()
},
// Listen for transaction receipts
onReceipt: (receipt, blockHash, next) => {
console.log('Transaction receipt:', {
txHash: receipt.transactionHash,
blockHash,
gasUsed: receipt.gasUsed
})
next?.()
},
// Listen for logs emitted
onLog: (log, receipt, next) => {
console.log('Log emitted:', {
address: log.address,
topics: log.topics,
data: log.data
})
next?.()
}
})
```
These event handlers follow the same pattern as EVM execution events, requiring a `next()` call to continue processing.
### Related Topics
* [Tevm Call API](../api/tevm-call)
* [Methods](../api/methods)
* [Mining Modes](../core/mining-modes)
* [Performance Profiler](../advanced/performance-profiler)
* [Gas Estimation](../api/methods)
## JSON-RPC Support
Tevm Node provides comprehensive [JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) support through an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible interface. This allows seamless integration with popular Ethereum libraries and tools.
### EIP-1193 Provider
The node can be extended to expose an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible request interface:
```ts
import { createTevmNode } from 'tevm'
import { requestEip1193 } from 'tevm/decorators'
const node = createTevmNode().extend(requestEip1193())
// Use standard JSON-RPC methods
const blockNum = await node.request({
method: 'eth_blockNumber',
params: [],
})
```
### Supported Methods
#### Core Ethereum Methods
* **Block & Chain**
* [`eth_blockNumber`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/blockNumberHandler.md) - Get current block number
* [`eth_getBlockByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetBlockByHashProcedure.md) - Get block by hash
* [`eth_getBlockByNumber`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetBlockByNumberProcedure.md) - Get block by number
* [`eth_chainId`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/chainIdHandler.md) - Get current chain ID
* **State & Account**
* [`eth_getBalance`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getBalanceHandler.md) - Get account balance
* [`eth_getCode`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getCodeHandler.md) - Get contract code
* [`eth_getStorageAt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getStorageAtHandler.md) - Get storage value
* [`eth_getTransactionCount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionCountProcedure.md) - Get account nonce
* **Transaction**
* [`eth_call`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethCallHandler.md) - Execute contract call
* [`eth_estimateGas`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethEstimateGasProcedure.md) - Estimate gas usage
* [`eth_sendTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethSendTransactionHandler.md) - Send transaction
* [`eth_sendRawTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethSendRawTransaction.md) - Send signed transaction
* [`eth_getTransactionByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionByHashProcedure.md) - Get transaction details
* [`eth_getTransactionReceipt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionReceipt.md) - Get transaction receipt
* **Logs & Events**
* [`eth_getLogs`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetLogsHandler.md) - Get event logs
* [`eth_newFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethNewFilterProcedure.md) - Create new filter
* [`eth_newBlockFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethNewBlockFilterProcedure.md) - Create block filter
* [`eth_getFilterChanges`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetFilterChangesProcedure.md) - Get filter updates
* [`eth_getFilterLogs`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetFilterLogsProcedure.md) - Get all filter logs
* [`eth_uninstallFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethUninstallFilterProcedure.md) - Remove filter
#### Extended Methods
Tevm also supports additional methods commonly found in development environments:
* **Debug Methods**
* [`debug_traceTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/debugTraceTransactionProcedure.md) - Trace transaction execution
* [`debug_dumpState`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/debugDumpStateProcedure.md) - Dump current state
* **Anvil Methods** (For [Foundry](https://book.getfoundry.sh/reference/anvil/) compatibility)
* [`anvil_setCode`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetCodeProcedure.md) - Set contract code
* [`anvil_setBalance`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetBalance.md) - Set account balance
* [`anvil_setNonce`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetNonceProcedure.md) - Set account nonce
* [`anvil_setStorageAt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetStorageAtProcedure.md) - Set storage value
* [`anvil_deal`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilDealHandler.md) - Add native ETH or ERC20 tokens to an account
* [`anvil_impersonateAccount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilImpersonateAccount.md) - Impersonate account
* [`anvil_stopImpersonatingAccount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilStopImpersonatingAccountProcedure.md) - Stop impersonating
### Client Integration
#### Using with Viem
For more information, see the [Viem Documentation](https://viem.sh/docs/clients/custom.html).
```ts
import { createTevmTransport } from 'tevm/memory-client'
import { createPublicClient, custom } from 'viem'
import { requestEip1193 } from 'tevm/decorators'
const tevmTransport = createTevmTransport()
const client = createPublicClient({
chain: mainnet,
transport: tevmTransport,
})
```
#### Using with Ethers
For more information, see the [Ethers Documentation](https://docs.ethers.org/v6/api/providers/#Provider).
```ts
import { createTevmNode } from 'tevm'
import { BrowserProvider } from 'ethers'
import { requestEip1193 } from 'tevm/decorators'
const node = createTevmNode().extend(requestEip1193())
const provider = new BrowserProvider(node)
```
### Error Handling
JSON-RPC errors follow the [standard format](https://www.jsonrpc.org/specification#error_object) and are fully typed. See the [error types documentation](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/JsonRpcError.md) for more details:
```ts
interface JsonRpcError {
code: number
message: string
data?: unknown
}
```
Common error codes (see [Error Types](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorCodes.md)):
* `-32700`: Parse error ([`ParseError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/ParseError.md))
* `-32600`: Invalid request ([`InvalidRequest`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InvalidRequest.md))
* `-32601`: Method not found ([`MethodNotFound`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/MethodNotFound.md))
* `-32602`: Invalid params ([`InvalidParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InvalidParams.md))
* `-32603`: Internal error ([`InternalError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InternalError.md))
* `-32000` to `-32099`: Server error ([`ServerError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/ServerError.md))
For detailed error handling examples and best practices, see the [Error Handling Guide](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorHandling.md).
### Best Practices
1. **Error Handling**: Always wrap RPC calls in try-catch blocks to handle potential errors gracefully. See [Error Types](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorCodes.md) for all possible errors.
2. **Gas Estimation**: For transactions, use [`eth_estimateGas`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethEstimateGasProcedure.md) before sending to ensure sufficient gas:
```ts
const gasEstimate = await node.request({
method: 'eth_estimateGas',
params: [tx],
})
```
3. **Receipt Confirmation**: Wait for transaction receipts to confirm state changes using [`eth_getTransactionReceipt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionReceipt.md):
```ts
const txHash = await node.request({
method: 'eth_sendTransaction',
params: [tx],
})
const receipt = await node.request({
method: 'eth_getTransactionReceipt',
params: [txHash],
})
```
4. **Event Filtering**: Use filters efficiently by:
* Setting appropriate block ranges
* Using specific [topics](https://docs.soliditylang.org/en/latest/abi-spec.html#events)
* Cleaning up unused filters with [`eth_uninstallFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethUninstallFilterProcedure.md)
For more examples and detailed API documentation, see:
* [Complete Actions Documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs)
* [Type Definitions](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases)
* [Function Reference](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions)
* [Variables and Constants](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables)
### Related Topics
* [Using with Viem](../examples/viem)
* [Using with Ethers](../examples/ethers)
* [Managing State](../core/managing-state)
* [Receipts & Logs](../advanced/receipts-and-logs)
* [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/)
* [EIP-1193: Ethereum Provider JavaScript API](https://eips.ethereum.org/EIPS/eip-1193)
* [Tevm API Documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs)
### Using Tevm Actions
Tevm provides a set of high-level actions that can be imported from `tevm/actions`. See the [complete actions documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) for all available actions.
```ts
import {
tevmCall, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/callHandler.md
tevmMine, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/mineHandler.md
tevmGetAccount, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getAccountHandler.md
tevmSetAccount, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/setAccountHandler.md
tevmDeal // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilDealHandler.md
} from 'tevm/actions'
import { createTevmNode } from 'tevm'
const node = createTevmNode()
// Call a contract
const result = await tevmCall(node, {
to: '0x...',
data: '0x...',
value: 0n,
createTransaction: true
})
// Mine pending transactions
await tevmMine(node)
// Get account state
const account = await tevmGetAccount(node, {
address: '0x...',
blockTag: 'latest'
})
// Set account state
await tevmSetAccount(node, {
address: '0x...',
balance: 100n,
nonce: 0n,
deployedBytecode: '0x...'
})
// Deal tokens to an account
// For native ETH:
await tevmDeal(node, {
account: '0x...',
amount: 1000000000000000000n // 1 ETH
})
// For ERC20 tokens:
await tevmDeal(node, {
erc20: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
account: '0x...',
amount: 1000000n // 1 USDC (6 decimals)
})
```
For detailed type information, see:
* [`CallParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/CallParams.md)
* [`MineParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/MineParams.md)
* [`GetAccountParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/GetAccountParams.md)
* [`SetAccountParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/SetAccountParams.md)
* [`AnvilDealParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/AnvilDealParams.md)
Note: By default, tevm actions require manual mining via [`tevmMine()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/mineHandler.md). If you want transactions to be automatically applied, you can either:
1. Use the lower level API `vm.runCall`
2. Configure the client with `miningConfig: { type: 'auto' }`
### Optimistic Updates with Receipt Manager
For more information on transaction receipts and logs, see the [Ethereum Receipts Documentation](https://ethereum.org/en/developers/docs/transactions/transaction-receipts/).
```ts
import { createTevmNode } from 'tevm'
import { tevmCall, tevmMine } from 'tevm/actions'
const node = createTevmNode()
const receiptsManager = await node.getReceiptsManager()
// Submit transaction
const { txHash } = await tevmCall(node, {
method: 'eth_sendTransaction',
params: [tx],
createTransaction: true
})
// Get optimistic receipt
const pendingReceipt = await receiptsManager.getReceiptByTxHash(txHash)
// Update UI optimistically
updateUI(pendingReceipt)
// Wait for real receipt
const realReceipt = await node.request({
method: 'eth_getTransactionReceipt',
params: [txHash]
})
// Eject optimistic tx if real receipt differs
if (receiptsAreDifferent(pendingReceipt, realReceipt)) {
await receiptsManager.removeReceipt(txHash)
updateUI(realReceipt)
}
// Advanced: Rebase on new blocks
node.on('block', async (blockNumber) => {
// Get new block
const block = await node.request({
method: 'eth_getBlockByNumber',
params: [blockNumber, true]
})
// Get our pending transactions
const pendingTxs = await receiptsManager.getPendingTransactions()
// Rebase our transactions on top of new block
for (const tx of pendingTxs) {
const result = await tevmCall(node, {
...tx,
blockTag: 'pending'
})
// Update receipt
await receiptsManager.putReceipt(tx.hash, result)
}
// Mine rebased transactions
await tevmMine(node)
})
```
import { Callout, Steps, Button } from 'vocs/components'
## Tevm Node Methods
Tevm Node provides a comprehensive API for interacting with the Ethereum Virtual Machine. This reference covers all core methods and capabilities.
This reference documents the main API methods available on a Tevm Node instance. These methods allow you to interact with the Ethereum Virtual Machine, manage state, and control execution at various levels of abstraction.
### Core Methods
The VM interface provides the lowest level of control, allowing you to execute transactions with precise parameters and inspect every aspect of execution.
```ts [Transaction Pool] showLineNumbers {1,4-9,12} filename="txpool-management.ts"
const txPool = await node.getTxPool() // [!code focus]
// Add a transaction
await txPool.add({ // [!code focus]
from: '0x1234...', // [!code focus]
to: '0x5678...', // [!code focus]
value: 1000000000000000000n, // [!code focus]
}) // [!code focus]
// Get pending transactions
// Sorted by gas price and nonce
const pending = await txPool.txsByPriceAndNonce() // [!code focus]
// Get all pending transactions (raw access)
const allPending = txPool.getPendingTransactions()
// Get pending nonce for an address
const nextNonce = txPool.getPendingNonce('0x1234...')
```
Transactions added to the pool remain pending until they are mined into a block. The mining behavior depends on your node's mining configuration.
The logs API follows the same pattern as Ethereum's JSON-RPC, allowing you to filter by address, topics, and block range.
:::
### State Management
๐ค Account Impersonation
Act as another account in fork mode
๐ Event Filtering
Create and manage event filters
:::code-group
```ts [Account Impersonation] showLineNumbers {2,5,8} filename="account-impersonation.ts"
// Impersonate an account (fork mode only)
node.setImpersonatedAccount('0x1234...') // [!code focus]
// Get current impersonated account
const impersonated = node.getImpersonatedAccount() // [!code focus]
// Stop impersonating
node.setImpersonatedAccount(undefined) // [!code focus]
// Now you can send transactions as this account
// without having its private key
```
Account impersonation primarily affects the JSON-RPC layer, enabling methods like eth\_sendRawTransaction to execute as the impersonated account. It works best in fork mode.
```ts [Event Filtering] showLineNumbers {2-10,13,16} filename="event-filtering.ts"
// Create a filter for Transfer events
node.setFilter({ // [!code focus]
id: '0x1', // [!code focus]
fromBlock: 0n, // [!code focus]
toBlock: 'latest', // [!code focus]
address: '0x1234...', // [!code focus]
topics: [ // [!code focus]
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer event signature // [!code focus]
], // [!code focus]
}) // [!code focus]
// Get all filters
const filters = node.getFilters() // [!code focus]
// Remove a filter
node.removeFilter('0x1') // [!code focus]
// Use filters with eth_getLogs and eth_getFilterChanges
const logs = await node.request({
method: 'eth_getFilterChanges',
params: ['0x1']
})
```
The topic 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef is the keccak256 hash of the Transfer event signature:
Transfer(address,address,uint256)
:::
### Node Properties
๐ฆ Status
Current state of the node
๐ Mode
Fork or normal operation mode
๐ Logger
Built-in logging capabilities
Status
The status property indicates the current state of the node:
```ts showLineNumbers filename="node-status.ts"
console.log(node.status)
// One of: 'INITIALIZING' | 'READY' | 'SYNCING' | 'MINING' | 'STOPPED'
// Example: Wait until node is ready
const waitForReady = async () => {
while (node.status !== 'READY') {
await new Promise(resolve => setTimeout(resolve, 100))
}
console.log('Node is ready!')
}
```
Mode
The mode property indicates whether the node is running in fork or normal mode:
```ts showLineNumbers filename="node-mode.ts"
console.log(node.mode) // 'fork' or 'normal'
// Use mode to adapt behavior
if (node.mode === 'fork') {
console.log('Node is forking from a remote provider')
// Use fork-specific features like impersonation
} else {
console.log('Node is running in standalone mode')
// Use local-only features
}
```
Logger
Built-in logging capabilities with multiple levels:
Tevm's extension system allows you to add custom methods to the node instance. These methods have full access to the base node's functionality.
```ts [State Cloning] showLineNumbers {2,5-7} filename="state-copying.ts"
// Create a deep copy with independent state
const nodeCopy = await node.deepCopy() // [!code focus]
// Fork from another node
const forkedNode = createTevmNode({ // [!code focus]
fork: { transport: node } // [!code focus]
}) // [!code focus]
// Now you can experiment with different states
await nodeCopy.sendTransaction({ ... })
// Original node state remains unchanged
console.log('Original state intact')
```
State copying is useful for creating test scenarios, simulating alternative execution paths, or creating snapshots of state for later comparison.
:::
### JSON-RPC Support
Tevm Node implements standard Ethereum JSON-RPC methods, making it compatible with existing Ethereum tools and libraries.
:::code-group
```ts [EIP-1193 Interface] showLineNumbers {1,3,6-9} filename="eip1193.ts"
import { requestEip1193 } from 'tevm/decorators'
const node = createTevmNode().extend(requestEip1193())
// Standard JSON-RPC calls
const blockNumber = await node.request({ // [!code focus]
method: 'eth_blockNumber', // [!code focus]
params: [] // [!code focus]
}) // [!code focus]
// Get balance
const balance = await node.request({
method: 'eth_getBalance',
params: ['0x1234...', 'latest']
})
```
The EIP-1193 interface makes Tevm Node compatible with libraries like Ethers.js that expect a standard Ethereum provider.
Action methods provide a more ergonomic API than raw JSON-RPC calls, with proper TypeScript typing and simpler parameter handling.
:::
#### Supported Methods
JSON-RPC Methods
State Access
๐ eth\_getBalance
๐ eth\_getCode
๐ eth\_getStorageAt
๐ eth\_getTransactionCount
Block Methods
๐ eth\_blockNumber
๐ eth\_getBlockByHash
๐ eth\_getBlockByNumber
Transaction Methods
๐ eth\_sendTransaction
๐ eth\_sendRawTransaction
๐ eth\_getTransactionByHash
๐ eth\_getTransactionReceipt
Anvil Methods
๐ anvil\_impersonateAccount
๐ anvil\_stopImpersonatingAccount
๐ anvil\_mine
๐ anvil\_setBalance
Tevm Methods
๐ tevm\_snapshot
๐ tevm\_revert
๐ tevm\_mine
๐ tevm\_setAccount
Utility Actions
๐ whatsabi - \[Coming Soon] Analyze contract bytecode, discover function selectors, resolve proxy implementations, and determine ABIs even for unverified contracts
## Package Reference
Tevm Node is built as a collection of modular packages. You can install the complete suite with `npm install tevm` or individual packages based on your needs.
### Installation Options
#### Complete Package
```bash
npm install tevm
```
```typescript
// Import from the main package
import { createTevmNode } from 'tevm'
import { callHandler } from 'tevm/actions'
import { http } from 'tevm/transport'
```
#### Individual Packages
```bash
npm install @tevm/node @tevm/actions
```
```typescript
// Import directly from packages
import { createTevmNode } from '@tevm/node'
import { callHandler } from '@tevm/actions'
```
### Core Packages
#### Node & Client
* **@tevm/node** - Core node implementation and management
* Main entry point for creating and managing Tevm nodes
* Blockchain and state management
* Mining and block production
* **@tevm/memory-client** - In-memory Ethereum client
* Viem-compatible client interface
* Local state management
* Transaction handling
#### EVM & Execution
* **@tevm/vm** - Virtual Machine implementation
* EVM execution environment
* Opcode handling
* State transitions
* **@tevm/evm** - Low-level EVM operations
* Bytecode execution
* Gas calculation
* Stack management
#### State & Storage
* **@tevm/state** - State management
* Account state handling
* Storage manipulation
* State transitions
* **@tevm/blockchain** - Blockchain implementation
* Block management
* Chain reorganization
* Header validation
### Transaction Handling
#### Transaction Processing
* **@tevm/tx** - Transaction utilities
* Transaction signing
* Gas estimation
* Receipt generation
* **@tevm/txpool** - Transaction pool management
* Pending transaction queue
* Transaction ordering
* Replacement handling
#### Block & Receipt Management
* **@tevm/block** - Block utilities
* Block creation
* Block validation
* Chain management
* **@tevm/receipt-manager** - Transaction receipts
* Receipt storage
* Event logging
* Gas usage tracking
### Client Integration
#### Communication
* **@tevm/jsonrpc** - JSON-RPC implementation
* Standard Ethereum methods
* Custom Tevm methods
* Error handling
* **@tevm/http-client** - HTTP client
* Remote node communication
* Request batching
* Error handling
#### Actions & Procedures
* **@tevm/actions** - High-level actions
* Contract interaction
* Account management
* State queries
* **@tevm/procedures** - Common operations
* State manipulation
* Chain operations
* Utility functions
### Smart Contract Tools
#### Contract Interaction
* **@tevm/contract** - Contract utilities
* ABI handling
* Function encoding
* Event parsing
* **@tevm/precompiles** - Precompiled contracts
* Standard precompiles
* Custom implementations
* Gas calculation
#### Contract Management
* **@tevm/predeploys** - Pre-deployed contracts
* Standard contracts
* Network-specific contracts
* Contract deployment
### Utilities & Helpers
#### Core Utilities
* **@tevm/utils** - Common utilities
* Address handling
* Data encoding
* Type conversion
* **@tevm/common** - Shared constants
* Chain configurations
* Network parameters
* Common types
#### Development Tools
* **@tevm/decorators** - Function decorators
* Method extension
* Behavior modification
* Utility wrappers
* **@tevm/errors** - Error handling
* Error types
* Error messages
* Stack traces
#### Data Structures
* **@tevm/rlp** - RLP encoding/decoding
* Data serialization
* Network protocol
* Storage format
* **@tevm/trie** - Merkle Patricia Tree
* State storage
* Proof verification
* Tree manipulation
### Development & Debugging
#### Logging & Debugging
* **@tevm/logger** - Logging system
* Configurable levels
* Output formatting
* Debug information
* **@tevm/effect** - Effect system
* Side effect handling
* Async operations
* Error recovery
#### Storage & Types
* **@tevm/sync-storage-persister** - Storage persistence
* State synchronization
* Data persistence
* Cache management
* **@tevm/client-types** - Type definitions
* Interface definitions
* Type exports
* API documentation
### Best Practices
1. **Start Simple**
* Begin with the complete `tevm` package
* Only split into individual packages if you need to optimize bundle size
2. **Bundle Optimization**
* Import from specific subpaths: `tevm/actions` instead of `@tevm/actions`
* Use tree-shaking friendly imports
3. **Version Management**
* Keep all @tevm/\* packages on the same version
* Update packages together to avoid compatibility issues
4. **Development Workflow**
* Use TypeScript for better type safety
* Leverage provided type definitions
* Follow the documentation for each package
### Related Topics
* [Architecture Overview](../introduction/architecture-overview)
* [API Reference](./methods)
* [GitHub Repository](https://github.com/evmts/tevm-monorepo)
import { Callout } from 'vocs/components'
## Call API
The Call API is one of the most important APIs in Tevm, covering 90% of use cases along with mining. It provides a powerful interface for executing EVM calls with extensive configuration options.
Remember that Tevm defaults to manual mining mode, which means:
* When using `addToMempool: true` (or the deprecated `createTransaction: true`), you must manually call `client.tevmMine()` to include the transaction in a block
* Use `addToBlockchain: true` for immediate transaction inclusion (automatically mines the transaction)
* You cannot use both `addToMempool` and `addToBlockchain` simultaneously
### Basic Usage
Tevm offers two API styles for using the Call API:
#### Client-based API (batteries included)
```ts
import { createMemoryClient } from 'tevm'
const client = createMemoryClient()
// Use tevmCall directly on the client object
const result = await client.tevmCall({
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet
data: '0x' // Empty call
})
```
#### Tree-shakable API
```ts
import { tevmCall } from 'tevm/actions'
import { createTevmTransport } from 'tevm/memory-client'
import { createTevmNode } from 'tevm/node'
import { requestEip1193 } from 'tevm/decorators'
// Create Viem client with tevm transport
const client = createClient({
transport: createTevmTransport(),
})
// Use the tree-shakable action
const result = await tevmCall(client, {
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet
data: '0x' // Empty call
})
```
### Parameters
The `CallParams` type includes:
```ts
type CallParams = {
// Required for most calls (except contract deployment)
to?: Address
// Input data for the call
data?: Hex
// Value in wei to send
value?: bigint
// Gas limit for the call
gas?: bigint
// Block tag to execute against
blockTag?: 'latest' | 'pending' | 'earliest' | number
// DEPRECATED: Whether to create a transaction
createTransaction?: 'on-success' | 'always' | 'never' | boolean
// Whether to add transaction to the mempool (requires mining)
addToMempool?: 'on-success' | 'always' | 'never' | boolean
// Whether to add transaction to the blockchain (auto-mines)
addToBlockchain?: 'on-success' | 'always' | 'never' | boolean
// Whether to skip balance checks
skipBalance?: boolean
// Whether to create an access list
createAccessList?: boolean
// Whether to create a debug trace
createTrace?: boolean
// From address (defaults to first account)
from?: Address
// Gas price settings
maxFeePerGas?: bigint
maxPriorityFeePerGas?: bigint
// State overrides
stateOverrideSet?: StateOverrideSet
// Block overrides
blockOverrideSet?: BlockOverrideSet
// Event handlers for EVM execution
onStep?: (data: InterpreterStep, next?: () => void) => void
onNewContract?: (data: NewContractEvent, next?: () => void) => void
onBeforeMessage?: (data: Message, next?: () => void) => void
onAfterMessage?: (data: EVMResult, next?: () => void) => void
}
```
### Return Type
The `CallResult` includes:
```ts
type CallResult = {
// Return data from the call
rawData: Hex
// Gas used by the EVM
executionGasUsed: bigint
// Total gas including intrinsic costs
totalGasSpent?: bigint
// Transaction hash if created
txHash?: Hex
// Logs emitted
logs?: Log[]
// Created contract address
createdAddress?: Address
// Access list if requested
accessList?: Record>
// Debug trace if requested
trace?: TraceResult
// Any errors that occurred
errors?: TevmCallError[]
}
```
### Examples
#### 1. Simple Contract Call
Using client-based API:
```ts
import { createMemoryClient } from 'tevm'
import { encodeFunctionData, decodeFunctionResult, parseAbi } from 'viem'
// Example ERC20 ABI for 'balanceOf'
const abi = parseAbi([
'function balanceOf(address account) view returns (uint256 balance)'
])
const client = createMemoryClient()
const result = await client.tevmCall({
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
data: encodeFunctionData({
abi,
functionName: 'balanceOf',
args: ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266']
})
})
const balance = decodeFunctionResult({
abi,
functionName: 'balanceOf',
data: result.rawData
})
```
#### 2. Contract Deployment
Using client-based API:
```ts
import { createMemoryClient } from 'tevm'
// Simple contract bytecode (returns 42)
const bytecode = '0x6080604052348015600f57600080fd5b50602a60808190526040516100929190810190830190829052565b604051601f19601f830116810160405280815292829060208401853c80601f830112156100c057600080fd5b505b50505050610047806100d36000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632096525514602d575b600080fd5b60336047565b604051603e91906059565b60405180910390f35b602a81565b6000819050919050565b6053816040565b82525050565b6000602082019050606c6000830184604c565b9291505056fea2646970667358221220f1c69e125f1a9f0c5e22a6fb4f9cb134c5b43496922c563e13731844a6e4d12d64736f6c63430008130033'
const client = createMemoryClient()
const result = await client.tevmCall({
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
data: bytecode,
addToBlockchain: true // Automatically adds to mempool and mines
})
console.log('Contract deployed at:', result.createdAddress)
```
#### 3. State Override
Using client-based API:
```ts
import { createMemoryClient } from 'tevm'
const client = createMemoryClient()
const result = await client.tevmCall({
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
data: '0x',
stateOverrideSet: {
['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266']: {
balance: 4096n, // 0x1000 as bigint
nonce: 2n,
code: '0x',
state: {}
}
}
})
```
#### 4. Debug Trace
Using client-based API:
```ts
import { createMemoryClient } from 'tevm'
const client = createMemoryClient()
const result = await client.tevmCall({
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
data: '0x',
createTrace: true
})
// Analyze the execution trace
if (result.trace) {
result.trace.structLogs.forEach(log => {
console.log(log.op, log.stack, log.memory)
})
}
```
#### 5. EVM Event Handlers
You can attach event handlers to monitor EVM execution in real-time.
Using client-based API:
```ts
import { createMemoryClient } from 'tevm'
import { encodeFunctionData } from 'viem'
const client = createMemoryClient()
// Use EVM event handlers to monitor execution
const result = await client.tevmCall({
from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
to: contractAddress,
data: encodeFunctionData({
abi,
functionName: 'myFunction',
args: [arg1, arg2]
}),
// Monitor each EVM step
onStep: (step, next) => {
console.log(
`Executing ${step.opcode.name} at PC=${step.pc}`,
`Gas left: ${step.gasLeft.toString()}`,
`Stack depth: ${step.stack.length}`
)
// Important: Call next to continue execution
next?.()
},
// Monitor contract creation events
onNewContract: (data, next) => {
console.log(`New contract created at: ${data.address.toString()}`)
next?.()
},
// Monitor messages (calls) before execution
onBeforeMessage: (message, next) => {
console.log(`Call to: ${message.to?.toString()}`,
`Value: ${message.value.toString()}`,
`Gas limit: ${message.gasLimit.toString()}`)
next?.()
},
// Monitor messages (calls) after execution
onAfterMessage: (result, next) => {
console.log(`Call result: ${result.execResult.returnValue.toString('hex')}`,
`Gas used: ${result.execResult.executionGasUsed.toString()}`)
if (result.execResult.exceptionError) {
console.error(`Error: ${result.execResult.exceptionError.error}`)
}
next?.()
}
})
```
### Higher Level APIs
While `tevmCall`
***
title: VM & Submodules
description: Overview of the internal EVM, blockchain, state, receipts, and more
## VM & Submodules
[Tevm Node](https://github.com/evmts/tevm-monorepo) 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](https://ethereum.org/en/developers/docs/evm/)
2\. **Blockchain** - [Block and chain state](https://ethereum.org/en/developers/docs/blocks/) management
3\. **StateManager** - [Account and storage state](https://ethereum.org/en/developers/docs/accounts/) management
4\. **TxPool** - [Transaction mempool](https://ethereum.org/en/developers/docs/transactions/)
5\. **ReceiptsManager** - [Transaction receipts and logs](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt)
### EVM Module
The EVM module handles bytecode execution and state transitions. It is based on [`@ethereumjs/evm`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm).
```ts
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)
// Use createImpersonatedTx to create transactions
import { createImpersonatedTx } from "tevm/tx";
const transaction = createImpersonatedTx({
to: "0x1234567890123456789012345678901234567890",
data: "0x...",
});
const txResult = await vm.runTx({
tx: transaction,
block: block,
})
```
#### Key Features
\- **State Management**: Handles [account state](https://ethereum.org/en/developers/docs/accounts/), storage, and code execution
\- **Gas Metering**: Tracks [gas consumption](https://ethereum.org/en/developers/docs/gas/) during execution
\- **Precompiles**: [Built-in contract implementations](https://www.evm.codes/precompiled)
\- **EIP Support**: Implements various [Ethereum Improvement Proposals](https://eips.ethereum.org/)
#### Common Operations
```ts
// 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`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/blockchain).
```ts
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
```ts
// 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`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/statemanager).
```ts
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
```ts
// 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`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/tx-pool).
```ts
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
```ts
// 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](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt) and event logs.
```ts
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
```ts
// 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](https://ethereum.org/en/developers/docs/gas/#why-are-gas-fees-necessary) 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](./json-rpc)
\- [Managing State](../core/managing-state)
\- [Transaction Pool](../advanced/txpool)
\- [Receipts & Logs](../advanced/receipts-and-logs)
\- [EVM Opcodes Reference](https://www.evm.codes/)
\- [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)
\- [ethereumjs/ethereumjs-monorepo](https://github.com/ethereumjs/ethereumjs-monorepo)
**Up Next**\
\- [JSON-RPC Guide](./json-rpc)
\- [Advanced Usage](../advanced/txpool)
import { Callout, Steps } from "vocs/components";
import { TabGroup } from "../../components";
## Contract Loader
**Coming Soon**: The Contract Loader is currently under development and not
yet available in the latest released version. This documentation outlines the
planned API design for the upcoming feature.
The Contract Loader will provide a convenient way to analyze contract
bytecode, discover function selectors, and resolve proxy contracts - all
through the familiar Tevm API.
### Overview
Contract Loaders are powerful [actions](/reference/actions) that can extract ABI information from contract bytecode, even for unverified contracts. Powered by [WhatsABI](https://github.com/shazow/whatsabi), Tevm's Contract Loader can be used both as [runtime actions](#usage) and as [buildtime macros](#network-imports-via-macros), allowing you to:
* Discover function selectors from bytecode
* Look up function signatures from selectors
* Automatically resolve proxy contracts
* Access verified contract ABIs from Sourcify, Etherscan, and other sources
๐งฉ Works with Unverified Contracts
Extract function selectors directly from bytecode
๐ Resolves Proxy Implementations
Automatically detects and follows proxy patterns
๐ Uses Multiple Sources
Combines Sourcify, Etherscan and bytecode analysis
๐ ๏ธ Fully Typed API
Complete TypeScript support for parameters and results
### Usage
For security reasons, contract macros are disabled by default. To enable them,
add `"macros": true` to your `tevm.config.json` file.
```ts showLineNumbers {1,3-4,7-10,12-13,24-28} filename="contract-loader-example.ts"
import { loadContract, createMemoryClient, http } from 'tevm'
import { MultiABILoader, EtherscanABILoader, SourcifyABILoader } from 'tevm/whatsabi'
const client = createMemoryClient({ // [!code focus]
fork: { transport: http('https://mainnet.optimism.io') } // [!code focus]
})
// loadContract returns a fully typed Contract instance // [!code focus]
const contract = await loadContract(client, { // [!code focus]
address: '0x00000000006c3852cbEf3e08E8dF289169EdE581', // Seaport contract // [!code focus]
followProxies: true, // [!code focus]
// Use multiple loaders to find ABIs from different sources
loaders: [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: 'YOUR_ETHERSCAN_KEY' })
]
})
// Contract is a fully typed Tevm contract instance // [!code focus]
console.log(`Contract address: ${contract.address}`) // [!code focus]
console.log(`Contract has ${contract.abi.length} ABI entries`) // [!code focus]
// Access additional properties
console.log(`Human readable ABI: ${contract.humanReadableAbi}`)
console.log(`Deployed bytecode available: ${Boolean(contract.deployedBytecode)}`)
console.log(`Implementation address (if proxy): ${contract.proxyDetails?.[0]?.implementation || 'Not a proxy'}`)
// Use the contract for type-safe interactions
const owner = await client.readContract({
...contract.read.owner(),
address: contract.address
})
```
```ts showLineNumbers {1-2,4-10,13-14,17-22} filename="contract-loader-extension.ts"
import { createClient, contractLoaderExtension } from 'tevm' // [!code focus]
import { http } from 'viem' // [!code focus]
import { BlockscoutABILoader, SourcifyABILoader } from 'tevm/whatsabi' // [!code focus]
// Configure loaders and other options in the extension // [!code focus]
const client = createClient({ // [!code focus]
transport: http('https://mainnet.optimism.io') // [!code focus]
}).extend(contractLoaderExtension({ // [!code focus]
// Default options used for all contract loading // [!code focus]
followProxies: true, // [!code focus]
loaders: [ // [!code focus]
new SourcifyABILoader(), // [!code focus]
new BlockscoutABILoader({ apiKey: 'YOUR_BLOCKSCOUT_KEY' }) // [!code focus]
] // [!code focus]
})) // [!code focus]
// Now you can load contracts by just providing the address // [!code focus]
const contract = await client.loadContract({ // [!code focus]
address: '0x00000000006c3852cbEf3e08E8dF289169EdE581' // [!code focus]
}) // [!code focus]
// Contract is a Tevm Contract instance // [!code focus]
// Use it with client methods for type-safe interactions // [!code focus]
const balance = await client.readContract({ // [!code focus]
...contract.read.balanceOf('0x123...'), // [!code focus]
address: contract.address // [!code focus]
}) // [!code focus]
// Override default options when needed
const anotherContract = await client.loadContract({
address: '0x456...',
// Override default options
followProxies: false
})
```
### Network Imports via Macros
One of the most powerful features of Contract Loader in Tevm is the ability to import contracts from any network at build time using macros.
Build-time macros provide better performance and stronger type safety compared
to runtime imports, as they avoid network requests during application
execution. Tevm's macro system is modeled after [Bun's
macros](https://bun.sh/docs/bundler/macros) but works with all bundlers
supported by [Tevm's bundler plugins](/reference/bundler), including Webpack,
Rollup, Vite, ESBuild, and more.
Macros use the [Import Attributes syntax](https://github.com/tc39/proposal-import-attributes), a Stage 3 TC39 proposal that lets you attach metadata to import statements (`with {type: 'tevm'}`). For security reasons, macros are disabled by default and must be enabled by adding `"macros": true` to your `tevm.config.json` file.
#### Creating Contract Macros
First, create a file that exports functions using loadContract to resolve contract data:
```ts showLineNumbers filename="contract-macros.js"
import { createClient, createMemoryClient } from "tevm";
import { http } from "viem";
import { mainnet } from "viem/chains";
import { loadContract } from "tevm";
import { EtherscanABILoader, SourcifyABILoader } from "tevm/whatsabi";
// For hermetic builds, use a memory client with a fork at specific block
// This ensures deterministic builds with reproducible contract data
const client = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
// Pinning a block height ensures your builds are reproducible
blockNumber: 19000000n,
},
});
// Configure loaders
const loaders = [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: "YOUR_ETHERSCAN_KEY" }),
];
// Using top-level await to pre-load contracts
// Note: This requires Node.js 14.8+ or setting "module": "esnext" in tsconfig
// Directly export the contract instances
export const usdc = await loadContract(client, {
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
followProxies: true,
includeBytecode: true,
includeSourceCode: true,
loaders,
});
export const weth = await loadContract(client, {
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH on Ethereum
followProxies: true,
includeBytecode: true,
includeSourceCode: true,
loaders,
});
```
#### Using Contract Macros
Then import the contracts with `with { type: 'tevm' }` attribute:
```ts showLineNumbers filename="app.js"
// Import contracts using macros
import { usdc } from "./contract-macros.js" with { type: "tevm" };
import { weth } from "./contract-macros.js" with { type: "tevm" };
import { createMemoryClient } from "tevm";
// Create client for local interaction
const client = createMemoryClient();
// Use the contracts with full type safety
const usdcBalance = await client.readContract({
...usdc.read.balanceOf("0x123..."),
address: usdc.address,
});
// WETH contract methods are fully typed
const wethDeposit = await client.writeContract({
...weth.write.deposit(),
value: 1000000000000000000n,
});
```
For security reasons, macros in third-party packages (node\_modules) are
blocked. Package authors should pre-build their contracts before publishing.
#### How Macros Work
#### 1. Build-Time Execution
When your bundler encounters an import with `type: 'macro'`, it executes the imported function during the build process.
#### 2. Contract Resolution
The function uses WhatsABI to fetch and analyze the contract from the blockchain, resolving ABIs and following any proxies.
#### 3. Static Code Generation
The bundler replaces the import with statically generated code that includes the full contract ABI and metadata.
#### 4. Type Generation
TypeScript types are generated for all contract methods, events, and properties, ensuring full type safety.
#### Benefits of Macros
* **Build-time resolution** - No network requests during application runtime
* **Full type safety** - Complete TypeScript types for all contract methods
* **Proxy resolution** - Automatically resolves and follows proxy implementations
* **Works with unverified contracts** - Uses bytecode analysis when sources aren't available
* **IDE integration** - Autocompletion and hover documentation for contract methods
### How It Works
#### 1. Bytecode Analysis
WhatsABI extracts function selectors from the contract's bytecode by analyzing jump tables and other bytecode patterns. This works for any contract, even if it's not verified on block explorers.
#### 2. Signature Lookup
The extracted function selectors are looked up in signature databases to match known function signatures. This helps identify common functions like `transfer`, `approve`, etc.
#### 3. Proxy Detection
WhatsABI checks for common proxy patterns (ERC-1967, Transparent, Beacon, etc.) and can automatically resolve and follow these implementations.
#### 4. On-chain Source Verification
WhatsABI attempts to find verified contract sources from Sourcify, Etherscan, and other sources based on the client's chain ID.
### Parameters
The `loadContract` action accepts the following parameters:
| Parameter | Type | Description |
| ---------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `address` | `Address` | **Required**. The contract address to analyze |
| `followProxies` | `boolean` | Whether to automatically follow proxy contracts. Default: `false` |
| `includeBytecode` | `boolean` | Whether to include contract bytecode in the returned contract. Default: `false` |
| `includeSourceCode` | `boolean` | Whether to include contract source code as [SolcInputSources](https://github.com/evmts/tevm-monorepo/blob/00e1397cf07f637a93b7eaf51178bda50f5c5d6e/bundler-packages/solc/src/solcTypes.ts#L319). Default: `false` |
| `loaders` | `ABILoader[]` | Array of ABI loaders to use for resolving contract ABIs. See [Available Loaders](#available-loaders) |
| `enableExperimentalMetadata` | `boolean` | Whether to include experimental metadata like event topics. Default: `false` |
| `signatureLookup` | `SignatureLookup \| false` | Custom signature lookup or `false` to disable. Default: uses WhatsABI's default lookup |
| `onProgress` | `(phase: string, ...args: any[]) => void` | Progress callback |
| `onError` | `(phase: string, error: Error) => boolean \| void` | Error callback |
### Return Value
The action returns a full [Tevm Contract](/reference/contract) instance with the following properties:
| Property | Type | Description |
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `abi` | `Abi` | The resolved ABI from bytecode or verified sources |
| `address` | `Address` | The contract address (may be different if proxies were followed) |
| `read` | `Object` | Type-safe read methods for view/pure functions |
| `write` | `Object` | Type-safe write methods for state-changing functions |
| `events` | `Object` | Type-safe event filters for subscription |
| `withAddress` | `Function` | Method to create a new instance with a different address |
| `abiLoadedFrom` | `{name: string, url?: string}` | Information about where the ABI was loaded from |
| `proxyDetails` | `Array<{name: string, implementation?: Address, selector?: string}>` | If the contract is a proxy, details about the proxy implementation |
| `sources` | `SolcInputSources` | If `includeSourceCode` is `true`, contains the contract source code files |
### Available Loaders
You can import various ABI loaders from `tevm/whatsabi`:
#### MultiABILoader
Tries multiple ABI loaders until a result is found.
```ts
import { MultiABILoader } from "tevm/whatsabi";
// Create loader that tries multiple sources in order
const loader = new MultiABILoader([
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: "YOUR_ETHERSCAN_KEY" }),
]);
// Use with loadContract
const contract = await loadContract(client, {
address: "0x123...",
loaders: [loader],
});
```
#### EtherscanABILoader
Loads contract ABIs from Etherscan and similar explorers.
```ts
import { EtherscanABILoader } from "tevm/whatsabi";
// For Etherscan on Ethereum mainnet
const etherscan = new EtherscanABILoader({
apiKey: "YOUR_ETHERSCAN_KEY",
});
// For Polygonscan
const polygonscan = new EtherscanABILoader({
apiKey: "YOUR_POLYGONSCAN_KEY",
baseUrl: "https://api.polygonscan.com/api",
});
```
#### SourcifyABILoader
Loads contract ABIs from Sourcify's decentralized repository.
```ts
import { SourcifyABILoader } from "tevm/whatsabi";
// Default configuration uses public Sourcify endpoint
const sourcify = new SourcifyABILoader();
// Custom Sourcify server
const customSourcify = new SourcifyABILoader({
baseUrl: "https://your-sourcify-server.com",
});
```
#### BlockscoutABILoader
Loads contract ABIs from Blockscout explorers.
```ts
import { BlockscoutABILoader } from "tevm/whatsabi";
// For default Blockscout
const blockscout = new BlockscoutABILoader({
apiKey: "YOUR_BLOCKSCOUT_KEY", // optional
});
// For custom Blockscout instance
const customBlockscout = new BlockscoutABILoader({
baseUrl: "https://your-blockscout-instance.com",
});
```
### Examples
#### Resolving a Proxy Contract
```ts showLineNumbers {1-2,4,6-11,13-15} filename="proxy-resolution.ts"
import { loadContract, createMemoryClient } from "tevm";
import { http } from "viem";
import { SourcifyABILoader, EtherscanABILoader } from "tevm/whatsabi";
const client = createMemoryClient({
fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY") },
});
// Analyze a proxy contract (e.g., a typical ERC-1967 proxy)
const contract = await loadContract(client, {
address: "0x4f8AD938eBA0CD19155a835f617317a6E788c868",
followProxies: true,
loaders: [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: "YOUR_KEY" }),
],
});
console.log(`Original address: 0x4f8AD938eBA0CD19155a835f617317a6E788c868`);
console.log(`Implementation address: ${contract.address}`);
console.log(`Detected proxies: ${contract.proxyDetails.length}`);
```
In this example, if the contract at 0x4f8AD... is a proxy, the Contract Loader
will detect it and automatically follow the implementation. The returned
`address` will be the implementation address, not the proxy address.
#### Working with Unverified Contracts
```ts showLineNumbers {1-2,4,6-10,12-15} filename="unverified-contract.ts"
import { loadContract, createMemoryClient } from "tevm";
import { http } from "viem";
const client = createMemoryClient({
fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY") },
});
// Analyze an unverified contract to extract its interface
const contract = await loadContract(client, {
address: "0xUnverifiedContractAddress",
// No loaders - will use bytecode analysis only
loaders: [],
});
// The resulting ABI will contain entries discovered through bytecode analysis
console.log("Discovered functions:");
contract.abi
.filter((item) => item.type === "function")
.forEach((func) => console.log(`- ${func.name || "Unknown"}`));
```
#### Creating a Contract Macro
```ts showLineNumbers filename="aave-macro.js"
import { createClient } from "tevm";
import { http } from "viem";
import { optimism } from "viem/chains";
import { loadContract } from "tevm";
import { SourcifyABILoader, EtherscanABILoader } from "tevm/whatsabi";
// Client for the Optimism network
const client = createClient({
chain: optimism,
transport: http("https://mainnet.optimism.io"),
});
// Configure loaders
const loaders = [
new SourcifyABILoader(),
new EtherscanABILoader({
apiKey: "YOUR_ETHERSCAN_KEY",
baseUrl: "https://api-optimistic.etherscan.io/api",
}),
];
// Directly export the contract instance using top-level await
export const aaveV3Pool = await loadContract(client, {
address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD", // Aave V3 Pool on Optimism
followProxies: true,
includeBytecode: true,
includeSourceCode: true,
loaders,
});
```
```ts showLineNumbers filename="app.js"
// Import with tevm attribute
import { aaveV3Pool } from "./aave-macro.js" with { type: "tevm" };
import { createPublicClient, http } from "viem";
import { optimism } from "viem/chains";
const client = createPublicClient({
chain: optimism,
transport: http(),
});
// All methods are strongly typed
const reserves = await client.readContract({
...aaveV3Pool.read.getReservesList(),
address: aaveV3Pool.address,
});
console.log(`Aave has ${reserves.length} reserves on Optimism`);
```
#### Using Multiple Loaders
```ts showLineNumbers filename="multi-loader.ts"
import { loadContract, createMemoryClient } from "tevm";
import { http } from "viem";
import {
MultiABILoader,
SourcifyABILoader,
EtherscanABILoader,
BlockscoutABILoader,
} from "tevm/whatsabi";
const client = createMemoryClient({
fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY") },
});
// Create a multi-loader that tries different sources in order
const multiLoader = new MultiABILoader([
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: "YOUR_ETHERSCAN_KEY" }),
new BlockscoutABILoader({ apiKey: "YOUR_BLOCKSCOUT_KEY" }),
]);
// Load a contract with comprehensive ABI resolution
const contract = await loadContract(client, {
address: "0xVerifiedContractAddress",
followProxies: true,
loaders: [multiLoader],
onProgress: (phase) => console.log(`Phase: ${phase}`),
});
// Use the contract directly with the client
const name = await client.readContract({
...contract.read.name(),
address: contract.address,
});
console.log(`Contract name: ${name}`);
```
#### Working with Source Code
```ts showLineNumbers filename="source-code-example.ts"
import { loadContract, createMemoryClient } from "tevm";
import { http } from "viem";
import { SourcifyABILoader, EtherscanABILoader } from "tevm/whatsabi";
const client = createMemoryClient({
fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY") },
});
// Load a contract with source code included
const contract = await loadContract(client, {
address: "0xVerifiedContractAddress",
followProxies: true,
includeBytecode: true,
includeSourceCode: true,
loaders: [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: "YOUR_ETHERSCAN_KEY" }),
],
});
// Check if source code was retrieved
if (contract.sources) {
console.log("Source files available:");
// Log the names of the source files
Object.keys(contract.sources).forEach((fileName) => {
console.log(`- ${fileName}`);
// Access the source content
const sourceContent = contract.sources[fileName].content;
console.log(`First 100 chars: ${sourceContent.substring(0, 100)}...`);
});
// Use the source code for integration with tools, compilation, etc.
// For example, you could write it to a file for local development
// Or use it for analysis, documentation generation, etc.
}
// You can still use the contract for interactions as usual
const balance = await client.readContract({
...contract.read.balanceOf("0x123..."),
address: contract.address,
});
```
#### Progress Tracking for Large Contracts
```ts showLineNumbers {5-14,17-22} filename="progress-tracking.ts"
import { loadContract, createMemoryClient } from "tevm";
import { http } from "viem";
import { SourcifyABILoader } from "tevm/whatsabi";
// Custom progress tracker
const progressTracker = (phase, ...args) => {
switch (phase) {
case "bytecode":
console.log("Analyzing bytecode...");
break;
case "proxy":
console.log("Checking for proxy patterns...");
break;
case "abi":
console.log("Loading ABI information...");
break;
}
};
const contract = await loadContract(client, {
address: "0xLargeContractAddress",
followProxies: true,
loaders: [new SourcifyABILoader()],
onProgress: progressTracker,
});
```
### When to Use Contract Loader vs Direct Solidity Imports
#### Use Contract Loader When:
* **Working with third-party contracts**: The source of truth is managed by another team
* **Needing the latest contract implementation**: Always stay up-to-date with the latest contract state
* **Dealing with unverified contracts**: Extract function selectors from bytecode directly
* **Interacting with proxy contracts**: Automatically resolve and follow proxy implementations
* **Building exploratory tools**: Analyze and interact with arbitrary contracts
* **Creating SDKs for protocols**: Use macros to generate type-safe interfaces at build time
#### Use Direct Solidity Imports When:
* **You own the contract code**: You control the source of truth
* **Need fully hermetic builds**: Direct imports ensure deterministic, reproducible builds
* **Working with fixed contract versions**: Import exact versions from npm or git submodules
* **Need complete control over ABI generation**: Custom ABI transformations or optimizations
Contract Loader is perfect for building exploratory tools, frontends that can
work with any contract, or for debugging contract interactions. It enables
"magic" experiences where users can interact with contracts without needing to
know their interface details. As a macro, it validates your contracts are
up-to-date at build time while providing excellent developer experience with
full type safety.
#### Ensuring Hermetic Builds
For production applications that need deterministic builds while using Contract Loader:
1. **Pin block heights**: Use `createMemoryClient` with a specific `blockNumber` in the fork config:
```ts
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.infura.io"),
blockNumber: 19000000n, // Pin to a specific block
},
});
```
2. **Generate and commit contracts**: Use the CLI to generate static contract files:
```bash
tevm generate contract-loader --address 0x123... --output ./src/contracts
```
3. **Use npm packages**: If available, use published contracts from official packages:
```bash
npm install @uniswap/v3-core
```
Feedback Welcome
This API design is currently in the planning phase. If you have suggestions,
feature requests, or other feedback about the proposed Contract Loader
integration, please share your thoughts in our GitHub repository or Telegram community.
## Building a Debugger UI
These docs have not been checked for correctness yet. Use with caution
This example demonstrates how to create a minimal EVM debugger interface using [Svelte](https://svelte.dev) and Tevm Node. The debugger will show:
* Live opcode execution
* Stack contents
* Memory state
* Error messages
* Gas usage
### Project Setup
First, create a new Svelte project and install dependencies:
```bash
npm create vite@latest tevm-debugger -- --template svelte-ts
cd tevm-debugger
npm install tevm tevm/contract
```
### Components
#### 1. EVMDebugger.svelte
```svelte
```
### Advanced Features
#### Memory Viewer Component
```svelte
{#each rows as row, i}
{(startOffset + i * bytesPerRow).toString(16).padStart(8, '0')}:
{#each row as byte}
{formatByte(byte)}
{/each}
{#each row as byte}
{formatAscii(byte)}
{/each}
{/each}
```
#### Storage Viewer Component
```svelte
{#if storage.size > 0}
{#each [...storage] as [slot, value]}
{slot}:{value}
{/each}
{/if}
```
### Usage
1. Create the project structure:
```
tevm-debugger/
โโโ src/
โ โโโ lib/
โ โ โโโ EVMDebugger.svelte
โ โ โโโ MemoryViewer.svelte
โ โ โโโ StorageViewer.svelte
โ โโโ App.svelte
โ โโโ main.ts
โโโ package.json
```
2. Run the development server:
```bash
npm run dev
```
3. Use the debugger:
```ts
// Example contract deployment
import { createImpersonatedTx } from 'tevm/tx'
const bytecode = '0x...' // Your contract bytecode
const deployTx = createImpersonatedTx({
data: bytecode
})
await vm.runTx({ tx: deployTx })
// Example contract interaction
const callTx = createImpersonatedTx({
to: '0x...', // Contract address
data: '0x...', // Encoded function call
})
await vm.runTx({ tx: callTx })
```
### Customization
#### Adding Transaction History
```svelte
Transaction History
{#each $transactions as tx}
Hash: {tx.hash}
To: {tx.to}
Data: {tx.data}
{/each}
```
#### Adding Gas Profiling
```svelte
Gas Profile
Opcode
Count
Total Gas
{#each [...gasProfile] as [opcode, stats]}
{opcode}
{stats.count}
{stats.totalGas.toString()}
{/each}
```
### Related Topics
* [EVM Events](../api/evm-events)
* [Performance Profiler](../advanced/performance-profiler)
## Using with Ethers.js v6
> **Info:** Tevm integrates seamlessly with [Ethers.js v6](https://docs.ethers.org/v6/) allowing you to use your favorite Ethereum library alongside Tevm's powerful in-memory EVM capabilities.
This guide demonstrates how to use Tevm Node with Ethers.js to build powerful Ethereum applications with a familiar, developer-friendly API.
### Setup
#### 1. Install Dependencies
First, install the required packages:
```bash
npm install tevm ethers
```
**Using Yarn or pnpm?**
```bash
# Yarn
yarn add tevm ethers
# pnpm
pnpm add tevm ethers
```
#### 2. Create the Tevm Node
Set up a Tevm node with EIP-1193 compatibility:
```ts
import { createTevmNode } from 'tevm'
import { requestEip1193 } from 'tevm/decorators'
// Create a Tevm Node with optional configuration
const node = createTevmNode({
// Configure mining behavior (auto or interval)
miningConfig: {
type: 'interval',
interval: 2000 // Mine blocks every 2 seconds
}
})
// Add EIP-1193 compatibility layer for Ethers.js
const nodeWithEip1193 = node.extend(requestEip1193())
// Wait for the node to be ready
await node.ready()
```
> **Tip:** The `requestEip1193()` decorator is essential - it adds the standard Ethereum provider interface that Ethers.js requires.
#### 3. Create an Ethers Provider
Connect Ethers to your Tevm node:
##### BrowserProvider (Recommended)
```ts
import { BrowserProvider } from 'ethers'
// Create a provider using the EIP-1193 compatible node
const provider = new BrowserProvider(nodeWithEip1193)
// Test the connection
const blockNumber = await provider.getBlockNumber()
console.log(`Connected to block: ${blockNumber}`)
```
> **Success:** BrowserProvider is recommended for most applications - it's the modern Ethers.js provider and handles all async operations correctly.
##### JsonRpcProvider (Legacy)
```ts
import { JsonRpcProvider } from 'ethers'
// For legacy code bases that require JsonRpcProvider
const legacyProvider = new JsonRpcProvider(
// Pass the node as an endpoint
nodeWithEip1193
)
// Test the connection
const network = await legacyProvider.getNetwork()
console.log(`Connected to network: ${network.name} (${network.chainId})`)
```
#### 4. Set Up a Wallet
Create a wallet for transactions:
```ts
import { Wallet } from 'ethers'
// Generate a random wallet
const wallet = Wallet.createRandom()
console.log(`Generated wallet address: ${wallet.address}`)
// Connect the wallet to your provider
const signer = wallet.connect(provider)
// The default balance will be zero
const balance = await provider.getBalance(signer.address)
console.log(`Initial wallet balance: ${balance} wei (${balance === 0n ? 'empty' : balance})`)
```
#### 5. Fund the Account
Use Tevm's state manipulation to fund your testing wallet:
```ts
import { parseEther, formatEther } from 'ethers'
// Manipulate blockchain state directly with Tevm
await node.setAccount({
address: signer.address,
balance: parseEther('100') // Add 100 ETH
})
// Verify the new balance
const newBalance = await provider.getBalance(signer.address)
console.log(`New wallet balance: ${formatEther(newBalance)} ETH`)
```
> **Tip:** This direct state manipulation is one of Tevm's powerful features - it allows you to set up any testing scenario without complex transactions.
### Core Functionality
* **Reading Contracts** -
## Forking Mainnet Example
These docs have not been checked for correctness yet. Use with caution
### Basic Fork Setup
```ts
import { createTevmNode } from 'tevm'
import { http } from 'viem'
const node = createTevmNode({
fork: {
transport: http('https://mainnet.infura.io/v3/'),
blockTag: 17_000_000n,
},
loggingLevel: 'debug',
})
await node.ready()
// Now any calls to an unknown account or contract
// will fetch the data from mainnet, but store the result locally for caching.
```
### Account Impersonation
```ts
// Impersonate a whale account
node.setImpersonatedAccount('0x28C6c06298d514Db089934071355E5743bf21d60') // Binance 14
// Now you can run transactions "as" that address, no signature needed
const tx = createImpersonatedTx({
to: '0x1234...',
value: 1000000000000000000n, // 1 ETH
})
const vm = await node.getVm()
await vm.runTx({ tx })
```
### Working with Forked Contracts
```ts
// Example: Interacting with USDC on mainnet
const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'
// Get the contract state from mainnet
const usdcContract = new Contract(USDC_ADDRESS, USDC_ABI)
const balance = await usdcContract.read.balanceOf(['0x1234...'])
// Modify state locally
await usdcContract.write.transfer(['0x5678...', 1000000]) // 1 USDC
// Changes only affect your local fork
const newBalance = await usdcContract.read.balanceOf(['0x1234...'])
```
### Fork at Specific Block
```ts
const node = createTevmNode({
fork: {
transport: http('https://mainnet.infura.io/v3/'),
blockTag: 15_000_000n, // Fork from a specific block
},
})
// Test historical state
const historicalBalance = await getBalance(address)
```
### Multiple Network Support
```ts
// Fork from different networks
const optimismNode = createTevmNode({
fork: {
transport: http('https://mainnet.optimism.io'),
},
})
const arbitrumNode = createTevmNode({
fork: {
transport: http('https://arb1.arbitrum.io/rpc'),
},
})
```
**Related**
* [Forking Guide](../core/forking)
* [State Management](../core/managing-state)
* [JSON-RPC Support](../api/json-rpc)
## Local Testing
These docs have not been checked for correctness yet. Use with caution
This guide demonstrates how to use Tevm Node for local testing of [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/) and [transactions](https://ethereum.org/en/developers/docs/transactions/). For more background on testing Ethereum applications, see the [Smart Contract Testing Guide](https://ethereum.org/en/developers/docs/smart-contracts/testing/).
### Basic Test Setup
```ts
import { createTevmNode } from 'tevm'
import { createImpersonatedTx } from 'tevm/tx'
import { expect, test } from 'vitest' // or jest, mocha, etc.
test('Basic ETH transfer', async () => {
// Create a new node instance
const node = createTevmNode({
miningConfig: { type: 'auto' }, // Mine blocks automatically
})
await node.ready()
const vm = await node.getVm()
// Create and run a transaction
const tx = createImpersonatedTx({
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
value: 1000000000000000000n, // 1 ETH
})
const result = await vm.runTx({ tx })
// Assert transaction succeeded
expect(result.execResult.exceptionError).toBeUndefined()
// Check recipient balance
const account = await vm.stateManager.getAccount(tx.to)
expect(account.balance).toBe(1000000000000000000n)
})
```
For more information on transaction execution, see the [EVM Execution Model](https://ethereum.org/en/developers/docs/evm/execution/).
### Contract Testing
#### 1. Deployment & Interaction
For background on contract deployment and interaction, see the [Contract Deployment Guide](https://ethereum.org/en/developers/docs/smart-contracts/deploying/).
```ts
import { Contract } from 'tevm/contract'
import { bytecode, abi } from './MyContract.json'
test('Deploy and interact with contract', async () => {
const node = createTevmNode()
await node.ready()
const vm = await node.getVm()
// Deploy contract
const deployTx = createImpersonatedTx({
data: bytecode,
})
const result = await vm.runTx({ tx: deployTx })
expect(result.execResult.exceptionError).toBeUndefined()
const contractAddress = result.createdAddress
expect(contractAddress).toBeDefined()
// Create contract instance
const contract = new Contract(contractAddress, abi)
// Call contract method
const callResult = await contract.read.getValue()
expect(callResult).toBe(expectedValue)
// Send transaction to contract
const tx = await contract.write.setValue([newValue])
const txResult = await vm.runTx({ tx })
expect(txResult.execResult.exceptionError).toBeUndefined()
// Verify state change
const updatedValue = await contract.read.getValue()
expect(updatedValue).toBe(newValue)
})
```
#### 2. Event Testing
For more information on events and logs, see the [Events and Logs Guide](https://ethereum.org/en/developers/docs/smart-contracts/anatomy/#events-and-logs).
```ts
test('Contract events', async () => {
const node = createTevmNode()
await node.ready()
// Deploy contract
const contract = await deployContract(node)
// Create event filter
node.setFilter({
id: '0x1',
address: contract.address,
topics: [
contract.interface.getEventTopic('ValueChanged'),
],
})
// Trigger event
const tx = await contract.write.setValue([123])
await vm.runTx({ tx })
// Get event logs
const receipts = await node.getReceiptsManager()
const logs = await receipts.getLogs({
fromBlock: 0n,
toBlock: 'latest',
address: contract.address,
})
expect(logs.length).toBe(1)
expect(logs[0].topics[0]).toBe(contract.interface.getEventTopic('ValueChanged'))
})
```
### Complex Testing Scenarios
#### 1. State Management
For more information on state management, see the [Ethereum State Guide](https://ethereum.org/en/developers/docs/evm/state-machine/).
```ts
test('Complex state changes', async () => {
const node = createTevmNode()
await node.ready()
const vm = await node.getVm()
// Create checkpoint
await vm.stateManager.checkpoint()
try {
// Perform multiple state changes
await performStateChanges(vm)
// Verify intermediate state
const intermediateState = await getState(vm)
expect(intermediateState).toMatchSnapshot()
// More changes
await performMoreChanges(vm)
// Commit changes
await vm.stateManager.commit()
} catch (error) {
// Revert on failure
await vm.stateManager.revert()
throw error
}
})
```
#### 2. Fork Testing
For more information on network forking, see the [Forking Guide](../core/forking).
```ts
test('Mainnet fork testing', async () => {
const node = createTevmNode({
fork: {
transport: http('https://mainnet.infura.io/v3/YOUR-KEY'),
blockTag: 17_000_000n,
},
})
await node.ready()
// Impersonate a whale account
node.setImpersonatedAccount('0x28C6c06298d514Db089934071355E5743bf21d60')
// Test DeFi interactions
const uniswap = new Contract(UNISWAP_ADDRESS, UNISWAP_ABI)
const tx = await uniswap.write.swapExactTokensForTokens([/* ... */])
const result = await vm.runTx({ tx })
expect(result.execResult.exceptionError).toBeUndefined()
})
```
#### 3. Time-based Testing
For more information on block timestamps and time-based operations, see the [Block Time Guide](https://ethereum.org/en/developers/docs/blocks/blocks-and-time/).
```ts
test('Time-dependent behavior', async () => {
const node = createTevmNode({
miningConfig: { type: 'interval', interval: 1000 },
})
await node.ready()
const vm = await node.getVm()
// Deploy time-locked contract
const contract = await deployTimeLock(vm)
// Try to withdraw (should fail)
let tx = await contract.write.withdraw()
let result = await vm.runTx({ tx })
expect(result.execResult.exceptionError).toBeDefined()
// Advance time by mining blocks
for (let i = 0; i < 100; i++) {
await vm.blockchain.putBlock(createBlock({ timestamp: Date.now() + i * 1000 }))
}
// Try withdraw again (should succeed)
tx = await contract.write.withdraw()
result = await vm.runTx({ tx })
expect(result.execResult.exceptionError).toBeUndefined()
})
```
### Testing Utilities
#### 1. Account Management
For more information on Ethereum accounts, see the [Accounts Guide](https://ethereum.org/en/developers/docs/accounts/).
```ts
// Helper to setup test accounts
async function setupAccounts(vm) {
const accounts = [
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
]
for (const address of accounts) {
await vm.stateManager.putAccount(address, {
nonce: 0n,
balance: 10000000000000000000n, // 10 ETH
})
}
return accounts
}
```
#### 2. Transaction Helpers
For more information on transaction types and formats, see the [Transaction Types Guide](https://ethereum.org/en/developers/docs/transactions/transaction-types/).
```ts
// Helper to send ETH
async function sendEth(vm, from, to, value) {
const tx = createImpersonatedTx({
from,
to,
value,
})
return vm.runTx({ tx })
}
// Helper to deploy contract
async function deployContract(vm, bytecode, args = []) {
const tx = createImpersonatedTx({
data: bytecode + encodeConstructor(args),
})
const result = await vm.runTx({ tx })
return result.createdAddress
}
```
### Related Topics
* [Viem Testing Guide](https://viem.sh/docs/testing/overview.html)
* [EthereumJS VM](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm)
* [JSON-RPC Methods](../api/json-rpc)
* [Contract reference](/reference/contract)
* [State Management](../core/managing-state)
### Replaying Contracts with Shadow Events
```ts
import { createTevmNode } from 'tevm'
import { http } from 'viem'
// Create a node that forks from mainnet
const node = createTevmNode({
fork: {
transport: http('https://mainnet.infura.io/v3/YOUR-KEY'),
},
})
// Get the transaction receipt to find its index
const receipt = await node.request({
method: 'eth_getTransactionReceipt',
params: ['0x...'] // Original tx hash
})
// Get the block and its transactions
const block = await node.request({
method: 'eth_getBlockByNumber',
params: [(receipt.blockNumber - 1n).toString(16), true]
})
// Replay all transactions before our target tx
for (let i = 0; i < receipt.transactionIndex; i++) {
const tx = block.transactions[i]
await node.getVm().runTx({ tx })
}
// Deploy modified contract with new event
const modifiedBytecode = '0x...' // Contract bytecode with new event
await node.setAccount({
address: receipt.contractAddress,
deployedBytecode: modifiedBytecode
})
// Now run the target transaction
// Note: These are transactions from the block, so we don't need createImpersonatedTx
const result = await node.getVm().runTx({
tx: block.transactions[receipt.transactionIndex]
})
// The result will include the new shadow event
console.log(result.execResult.logs)
```
### Estimating Gas for Token Approval
```ts
import { createTevmNode } from 'tevm/node'
import { encodeFunctionData } from 'viem'
const node = createTevmNode()
const vm = await node.getVm()
// First approve the token
import { createImpersonatedTx } from 'tevm/tx'
const approveTx = createImpersonatedTx({
to: tokenAddress,
data: encodeFunctionData({
abi: erc20ABI,
functionName: 'approve',
args: [spenderAddress, amount]
})
})
// Estimate gas by running the tx
const result = await vm.runTx({ tx: approveTx })
console.log('Gas used:', result.execResult.executionGasUsed)
// Now we can estimate transferFrom
const transferFromTx = createImpersonatedTx({
to: tokenAddress,
data: encodeFunctionData({
abi: erc20ABI,
functionName: 'transferFrom',
args: [ownerAddress, recipientAddress, amount]
})
})
const transferResult = await vm.runTx({ tx: transferFromTx })
console.log('TransferFrom gas:', transferResult.execResult.executionGasUsed)
```
import { Callout } from "vocs/components";
import { Tab, TabGroup, Card, CardGrid } from "../../components";
## Using with Viem
Tevm integrates seamlessly with viem
, providing a powerful local Ethereum environment with viem's familiar API
surface.
This guide demonstrates how to use Tevm with viem, enabling you to leverage viem's type-safe, modular Ethereum development tools alongside Tevm's in-memory EVM capabilities.
### Integration Options
Tevm offers two integration approaches with viem to suit different development needs:
The recommended approach for production frontend applications that need to minimize bundle size:
```ts
import { requestEip1193 } from 'tevm/decorators'
import { createClient } from 'viem'
const tevmTransport = createTevmTransport()
// Create Viem client
const client = createClient({
// Use Tevm node as the viem transport
transport: tevmTransport,
})
// Import and use viem actions individually
import { getBlockNumber } from 'viem/actions'
await getBlockNumber(client)
// Import and use tevm actions
import { tevmDumpState } from 'tevm'
await tevmDumpState(client)
```
This approach enables tree-shaking, resulting in smaller bundle sizes as only the actions you import are included in your final build.
A more convenient approach when bundle size isn't a primary concern is to use createMemoryclient:
```ts
import { createMemoryClient } from 'tevm'
// Create a fully-loaded client with all actions attached
const client = createMemoryClient()
// Use viem actions directly from the client
await client.getBlockNumber()
// Use tevm-specific actions
await client.tevmDumpState()
```
This approach provides a more convenient developer experience with all actions available directly on the client object, ideal for rapid development and testing.
### Core Functionality
Read blockchain state, query contracts, and estimate gas
Send transactions, sign messages, and interact with accounts
Manipulate blockchain state for testing and development
Tevm-specific extensions for enhanced EVM capabilities
### Public Actions
Public actions allow you to read data from the blockchain and interact with
smart contracts in a read-only fashion.
Use [viem's public actions](https://viem.sh/docs/actions/public/introduction) to query your local Tevm environment:
```ts
// Get the latest block
const block = await client.getBlock();
console.log(`Block number: ${block.number}`);
// Get an account's balance
const balance = await client.getBalance({
address: "0x1234567890123456789012345678901234567890",
});
console.log(`Balance: ${balance} wei`);
// Get transaction count (nonce)
const nonce = await client.getTransactionCount({
address: "0x1234567890123456789012345678901234567890",
});
console.log(`Transaction count: ${nonce}`);
// Read from a contract
const result = await client.readContract({
address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC on mainnet
abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
functionName: "balanceOf",
args: ["0x1234567890123456789012345678901234567890"],
});
console.log(`Contract result: ${result}`);
```
### Wallet Actions
Wallet actions enable you to simulate transactions, sign messages, and
interact with accounts and contracts in a write capacity.
Tevm supports all of [viem's wallet actions](https://viem.sh/docs/actions/wallet/introduction) with built-in prefunded accounts:
```ts
import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { parseEther } from "viem";
// Create a client with one of Tevm's prefunded accounts
const client = createMemoryClient({
account: PREFUNDED_ACCOUNTS[0], // First prefunded account with 10000 ETH
});
// Send ETH to another address
const hash = await client.sendTransaction({
to: "0x1234567890123456789012345678901234567890",
value: parseEther("1"), // Send 1 ETH
});
console.log(`Transaction sent: ${hash}`);
// Wait for the transaction to be mined
const receipt = await client.waitForTransactionReceipt({ hash });
console.log(`Transaction mined in block: ${receipt.blockNumber}`);
// Deploy a contract
const { contractAddress } = await client.deployContract({
abi: parseAbi([
"function greet() view returns (string)",
"function setGreeting(string) returns ()",
]),
bytecode: "0x608060405234801561...", // Contract bytecode
});
console.log(`Contract deployed at: ${contractAddress}`);
```
Working with Custom Accounts
```ts
import { createMemoryClient } from "tevm";
import { privateKeyToAccount } from "viem/accounts";
// Create an account from a private key
const account = privateKeyToAccount(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
);
// Use that account with the client
const client = createMemoryClient({ account });
// First set some balance for the account
await client.setBalance({
address: account.address,
value: parseEther("10"),
});
// Now use the account to send transactions
const hash = await client.sendTransaction({
to: "0x1234567890123456789012345678901234567890",
value: parseEther("1"),
});
```
### Test Actions
Test actions allow you to manipulate the blockchain state for testing
purposes, similar to using Anvil or Hardhat.
All of [viem's test actions](https://viem.sh/docs/actions/test/introduction) are supported for comprehensive testing capabilities:
```ts
import { createMemoryClient } from "tevm";
import { parseEther } from "viem";
const client = createMemoryClient();
// Mine additional blocks
await client.mine({ blocks: 5 });
console.log(`New block number: ${await client.getBlockNumber()}`);
// Set an account's balance
await client.setBalance({
address: "0x1234567890123456789012345678901234567890",
value: parseEther("100"),
});
// Set block timestamp for time-dependent tests
await client.setNextBlockTimestamp(1695311333n); // Set timestamp for next block
await client.mine({ blocks: 1 }); // Mine the block with that timestamp
// Snapshot and revert state
const snapshotId = await client.snapshot();
console.log(`Created snapshot: ${snapshotId}`);
// Make some changes...
await client.setBalance({
address: "0x1234567890123456789012345678901234567890",
value: parseEther("999"),
});
// Revert to the snapshot
await client.revert({ id: snapshotId });
console.log("Reverted to previous state");
// Check balance is back to previous value
const balance = await client.getBalance({
address: "0x1234567890123456789012345678901234567890",
});
console.log(`Balance after revert: ${balance}`);
```
### Tevm Actions
Tevm provides additional actions beyond standard viem functionality to give
you enhanced control over the EVM environment.
#### Contract Interactions
```ts
import { createMemoryClient } from "tevm";
import { parseAbi } from "viem";
const client = createMemoryClient();
// Using the tevmContract action for contract interaction
const result = await client.tevmContract({
abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC on mainnet
functionName: "balanceOf",
args: ["0x1234567890123456789012345678901234567890"],
});
console.log(`Contract result: ${result}`);
// Low-level EVM call with tevmCall
const callResult = await client.tevmCall({
to: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
data:
"0x70a08231000000000000000000000000" +
"1234567890123456789012345678901234567890".slice(2),
});
console.log(`Raw call result: ${callResult.data}`);
```
#### Account Management
```ts
import { createMemoryClient } from "tevm";
import { parseEther } from "viem";
const client = createMemoryClient();
// Get account state with all details
const account = await client.tevmGetAccount({
address: "0x1234567890123456789012345678901234567890",
});
console.log("Account state:", account);
// Set up a complex account state (EOA or contract)
await client.tevmSetAccount({
address: "0xabcdef1234567890abcdef1234567890abcdef12",
balance: parseEther("100"),
nonce: 5n,
// For contracts:
code: "0x608060405234801...", // Contract bytecode
storage: {
// Storage slots
"0x0": "0x1", // slot 0 -> value 1
"0x1": "0x2", // slot 1 -> value 2
},
});
// Add tokens to an account
// For native ETH:
await client.tevmDeal({
account: "0x1234567890123456789012345678901234567890",
amount: parseEther("10"),
});
// For ERC20 tokens:
await client.tevmDeal({
erc20: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC on mainnet
account: "0x1234567890123456789012345678901234567890",
amount: 1000000n, // 1 USDC (6 decimals)
});
```
#### State Management
```ts
import { createMemoryClient } from "tevm";
const client = createMemoryClient();
// Dump the entire EVM state
const state = await client.tevmDumpState();
console.log("Current state:", state);
// Save state to local variable
const savedState = await client.tevmDumpState();
// Make changes
await client.setBalance({
address: "0x1234567890123456789012345678901234567890",
value: 123456789n,
});
// Restore previous state
await client.tevmLoadState({
state: savedState,
});
// Mine blocks with Tevm action
await client.tevmMine({
blocks: 5,
});
```
The `tevmDumpState` and `tevmLoadState` actions are more powerful than regular
snapshots, as they capture the complete VM state, including the fork cache.
### Inside the Memory Client
Understanding how a MemoryClient is constructed helps you grasp Tevm's
architecture and integration capabilities.
A MemoryClient is essentially a viem client with Tevm's functionality added. Here's how you could build one from scratch:
```ts
// Step 1: Create a fork transport (for connecting to an existing network)
import { http } from "viem";
const forkTransport = http("https://mainnet.optimism.io");
// Step 2: Create a Tevm Node and make it EIP-1193 compatible
import { createTevmNode } from "tevm";
import { requestEip1193 } from "tevm/decorators";
const node = createTevmNode({
fork: {
transport: forkTransport,
},
}).extend(requestEip1193());
// Step 3: Create a viem client with Tevm extensions
import {
custom,
createClient,
publicActions,
testActions,
walletActions,
} from "viem";
import { tevmViemActions } from "tevm/memory-client";
const memoryClient = createClient({
transport: custom(node),
})
// Add Tevm-specific actions
.extend(tevmViemActions())
// Add viem standard actions
.extend(publicActions)
.extend(walletActions)
.extend(testActions({ mode: "anvil" }));
// Now you have a fully functional memoryClient
```
This breakdown illustrates Tevm's key architectural components:
1. **EIP-1193 Compatibility Layer**: Tevm implements the standard Ethereum provider interface
2. **In-Memory EVM**: Tevm runs a complete Ethereum Virtual Machine locally
3. **Viem Integration**: Tevm extends viem's functionality with EVM-specific capabilities
### Complete Action Reference
Public Actions - Read blockchain state
##### Contract Interactions
* [`call`](https://viem.sh/docs/actions/public/call) - Call a contract method without sending a transaction
* [`readContract`](https://viem.sh/docs/contract/readContract) - Read a contract's constant/view method
* [`simulateContract`](https://viem.sh/docs/contract/simulateContract) - Simulate a contract write without executing
* [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas) - Estimate gas for a contract call
* [`estimateGas`](https://viem.sh/docs/actions/public/estimateGas) - Estimate gas for a transaction
* [`getBytecode`](https://viem.sh/docs/contract/getBytecode) - Get a contract's bytecode
##### Block & Transaction
* [`getBlock`](https://viem.sh/docs/actions/public/getBlock) - Get a block by number or hash
* [`getBlockNumber`](https://viem.sh/docs/actions/public/getBlockNumber) - Get the latest block number
* [`getBlockTransactionCount`](https://viem.sh/docs/actions/public/getBlockTransactionCount) - Get the transaction count for a block
* [`getTransaction`](https://viem.sh/docs/actions/public/getTransaction) - Get transaction details by hash
* [`getTransactionCount`](https://viem.sh/docs/actions/public/getTransactionCount) - Get the transaction count (nonce) for an address
* [`getTransactionReceipt`](https://viem.sh/docs/actions/public/getTransactionReceipt) - Get a transaction receipt by hash
* [`waitForTransactionReceipt`](https://viem.sh/docs/actions/public/waitForTransactionReceipt) - Wait for a transaction to be mined
##### Account & Chain
* [`getBalance`](https://viem.sh/docs/actions/public/getBalance) - Get the balance of an address
* [`getChainId`](https://viem.sh/docs/actions/public/getChainId) - Get the chain ID
* [`getGasPrice`](https://viem.sh/docs/actions/public/getGasPrice) - Get the current gas price
* [`estimateFeesPerGas`](https://viem.sh/docs/actions/public/estimateFeesPerGas) - Estimate fees per gas unit
* [`getStorageAt`](https://viem.sh/docs/actions/public/getStorageAt) - Get the value from a storage slot
Test Actions - Manipulate blockchain state
##### Block & Mining
* [`mine`](https://viem.sh/docs/actions/test/mine) - Mine a number of blocks
* [`setAutomine`](https://viem.sh/docs/actions/test/setAutomine) - Enable/disable automatic mining
* [`setIntervalMining`](https://viem.sh/docs/actions/test/setIntervalMining) - Set mining to occur at intervals
* [`setBlockGasLimit`](https://viem.sh/docs/actions/test/setBlockGasLimit) - Set the block gas limit
* [`setBlockTimestampInterval`](https://viem.sh/docs/actions/test/setBlockTimestampInterval) - Set increment for timestamps
* [`setNextBlockBaseFeePerGas`](https://viem.sh/docs/actions/test/setNextBlockBaseFeePerGas) - Set the base fee for the next block
* [`setNextBlockTimestamp`](https://viem.sh/docs/actions/test/setNextBlockTimestamp) - Set the timestamp for the next block
##### Account & State
* [`setBalance`](https://viem.sh/docs/actions/test/setBalance) - Set an address's balance
* [`setCode`](https://viem.sh/docs/actions/test/setCode) - Set contract bytecode at an address
* [`setNonce`](https://viem.sh/docs/actions/test/setNonce) - Set the nonce for an address
* [`setStorageAt`](https://viem.sh/docs/actions/test/setStorageAt) - Set a storage slot's value
* [`setCoinbase`](https://viem.sh/docs/actions/test/setCoinbase) - Set the block miner address
* [`setMinGasPrice`](https://viem.sh/docs/actions/test/setMinGasPrice) - Set the minimum gas price
##### State Management
* [`snapshot`](https://viem.sh/docs/actions/test/snapshot) - Create a snapshot of the current state
* [`revert`](https://viem.sh/docs/actions/test/revert) - Revert to a previous snapshot
* [`reset`](https://viem.sh/docs/actions/test/reset) - Reset the fork to a fresh state
* [`dumpState`](https://viem.sh/docs/actions/test/dumpState) - Export the current state
* [`loadState`](https://viem.sh/docs/actions/test/loadState) - Import a previously exported state
Wallet Actions - Send transactions and interact with accounts
##### Account Management
* [`getAddresses`](https://viem.sh/docs/actions/wallet/getAddresses) - Get available addresses
* [`requestAddresses`](https://viem.sh/docs/actions/wallet/requestAddresses) - Request permission to view addresses
##### Transaction Operations
* [`prepareTransactionRequest`](https://viem.sh/docs/actions/wallet/prepareTransactionRequest) - Prepare a transaction
* [`sendTransaction`](https://viem.sh/docs/actions/wallet/sendTransaction) - Send a transaction
* [`sendRawTransaction`](https://viem.sh/docs/actions/wallet/sendRawTransaction) - Send a signed transaction
* [`signTransaction`](https://viem.sh/docs/actions/wallet/signTransaction) - Sign a transaction
##### Signing Operations
* [`signMessage`](https://viem.sh/docs/actions/wallet/signMessage) - Sign a message
* [`signTypedData`](https://viem.sh/docs/actions/wallet/signTypedData) - Sign typed data (EIP-712)
##### Chain Management
* [`addChain`](https://viem.sh/docs/actions/wallet/addChain) - Add a chain to the wallet
* [`switchChain`](https://viem.sh/docs/actions/wallet/switchChain) - Switch to a different chain
##### Permissions & Assets
* [`getPermissions`](https://viem.sh/docs/actions/wallet/getPermissions) - Get wallet permissions
* [`requestPermissions`](https://viem.sh/docs/actions/wallet/requestPermissions) - Request wallet permissions
* [`watchAsset`](https://viem.sh/docs/actions/wallet/watchAsset) - Add a token to the wallet
Tevm Actions - Enhanced EVM capabilities
* `tevmCall` - Low-level EVM call
* `tevmContract` - Call a contract method with detailed EVM info
* `tevmDeploy` - Deploy a contract with detailed results
* `tevmGetAccount` - Get detailed account information
* `tevmSetAccount` - Set up a complex account state
* `tevmDeal` - Add native ETH or ERC20 tokens to an account
* `tevmDumpState` - Export complete EVM state
* `tevmLoadState` - Import complete EVM state
* `tevmMine` - Mine blocks with additional options
### Next Steps
Learn how to integrate Tevm with ethers.js
Create a local fork of mainnet for testing
Set up a comprehensive local testing environment
Explore the low-level node interface
## Bundler Quickstart
This quickstart guide will help you set up Tevm's bundler functionality, allowing you to import Solidity contracts directly into your TypeScript/JavaScript code.
### Overview
Tevm bundler enables a seamless integration between Solidity and TypeScript, letting you:
* Import `.sol` files directly in your code
* Receive full TypeScript type information for contract methods
* Use go-to-definition and hover documentation for Solidity code
* Interact with contracts in a type-safe way
### Prerequisites
* Node.js 18+ (recommended for best ESM support) and npm/yarn/pnpm
* A supported bundler (Vite, Webpack, Rollup, ESBuild, or Bun)
### Step 1: Install Tevm and TypeScript Plugin
First, install Tevm and the TypeScript plugin in your project:
```bash
npm install tevm
npm install -D @tevm/ts-plugin
```
### Step 2: Configure Your Bundler
The bundler plugin handles the actual compilation of Solidity files during your build process. This ensures your final JavaScript output will contain the compiled Solidity contract ABIs, bytecode, and TypeScript interfaces.
:::tip
If you have a complex project or Foundry-based imports, you might need advanced configuration. See the [Advanced Configuration](#advanced-configuration) section for setting up `tevm.config.json`.
The bundler creates a `.tevm` cache directory. Consider adding it to your `.gitignore`:
```bash
# .gitignore
.tevm
```
:::
#### For Vite (recommended)
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import { vitePluginTevm } from 'tevm/bundler/vite-plugin'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react(),
vitePluginTevm(),
],
})
```
#### For Other Bundlers
Webpack
```js
// webpack.config.mjs
import { webpackPluginTevm } from 'tevm/bundler/webpack-plugin'
export default {
// Other webpack config
plugins: [
webpackPluginTevm(),
],
}
```
Next.js
```js
// next.config.js or next.config.mjs
const nextConfig = {
// Your Next.js config...
webpack: (config) => {
// Optional: Any custom webpack configuration
// Note: Next.js may have typechecking issues with .sol imports.
// If you encounter type errors, consider the following option:
return config
},
typescript: {
// Typechecking will only be available after the LSP is migrated to volar
// Until then typechecking will work in editor but not during a next.js build
// If you absolutely need typechecking before then there is a way to generate .ts files via a ts-plugin cli command
// To do that run `npx evmts-gen` in the root of your project
ignoreBuildErrors: true,
}
}
export default nextConfig
```
:::warning
Next.js has its own type-checking pipeline that may conflict with Solidity imports. If you see type errors during `next build`, either:
1. Use `typescript.ignoreBuildErrors: true` (as shown above)
2. Use the codegen approach with `npx tevm gen` to generate explicit TypeScript files
:::
Rollup
```js
// rollup.config.js
import { rollupPluginTevm } from 'tevm/bundler/rollup-plugin'
export default {
// Other rollup options
plugins: [
rollupPluginTevm(),
],
}
```
Rspack
```js
// rspack.config.mjs
import { rspackPluginTevm } from 'tevm/bundler/rspack-plugin'
export default {
// Other rspack config
plugins: [
rspackPluginTevm(),
],
}
```
Bun
```ts
// plugins.ts
import { plugin } from 'bun'
import { tevmBunPlugin } from 'tevm/bundler/bun-plugin'
plugin(tevmBunPlugin({}))
```
Then in your `bunfig.toml`:
```toml
preload = ["./plugins.ts"]
[test]
preload = ["./plugins.ts"]
```
ESBuild
```js
// build.mjs
import { build } from 'esbuild'
import { esbuildPluginTevm } from 'tevm/bundler/esbuild-plugin'
build({
entryPoints: ['src/index.js'],
outdir: 'dist',
bundle: true,
plugins: [
esbuildPluginTevm(),
],
})
```
### Step 3: Configure TypeScript
While the bundler plugin handles compilation during build time, the TypeScript plugin configures your TypeScript Language Service (LSP) to be aware of Solidity imports during development. This enables editor features like code completion, type checking, and go-to-definition for Solidity contracts.
Add the Tevm TypeScript plugin to your `tsconfig.json`:
```json
{
"compilerOptions": {
"plugins": [
{ "name": "@tevm/ts-plugin" }
],
// Other TypeScript options...
}
}
```
### Step 4: Configure Your Editor
#### VS Code and Cursor
For VS Code and Cursor users, ensure you're using the workspace version of TypeScript:
1. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P in VS Code; โ+K or Ctrl+K in Cursor)
2. Type "TypeScript: Select TypeScript Version"
3. Select "Use Workspace Version"
This step is crucial because the workspace TypeScript installation is what loads the @tevm/ts-plugin to provide Solidity import support.
#### Vim, Neovim, and Other Editors
For Vim, Neovim, and most other editors with TypeScript support, everything should work automatically without any additional configuration as long as they use the project's workspace version of TypeScript. This is crucial because the workspace TypeScript installation is what loads the @tevm/ts-plugin to provide Solidity import support.
### Step 5: Create a Solidity Contract
Create a simple contract in your project. Name it `Counter.s.sol` to include bytecode (the `.s.sol` extension tells Tevm to generate deployable bytecode):
:::info
**Understanding .sol vs .s.sol extensions:**
* **`.sol`** - Only generates the ABI (Application Binary Interface)
* Use for interacting with existing deployed contracts
* Cannot be deployed (no bytecode)
* Example: `import { Token } from './Token.sol'`
* **`.s.sol`** - Generates both ABI and bytecode
* Can be deployed or used as an interface
* Increases bundle size but enables deployment
* Example: `import { Token } from './Token.s.sol'`
:::
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Counter {
uint256 private count = 0;
function increment() public {
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
}
```
:::info
Files with the `.s.sol` extension generate both ABI and bytecode, allowing contract deployment.
Regular `.sol` files only generate ABIs (useful for interacting with existing contracts).
:::
### Step 6: Import and Use the Contract
Now you can import the contract directly in your TypeScript code:
```tsx
// src/App.tsx or any other file
import { Counter } from './Counter.s.sol'
import { createMemoryClient } from 'tevm'
import { useState, useEffect } from 'react'
function App() {
const [count, setCount] = useState(0n)
const [client] = useState(() => createMemoryClient())
const [deployedAddress, setDeployedAddress] = useState('')
useEffect(() => {
const deploy = async () => {
// Deploy the contract - need to call .deploy() and pass any constructor args
// (Counter doesn't have constructor args in this example)
const deployed = await client.deployContract(Counter.deploy())
setDeployedAddress(deployed.address)
// Get initial count
const initialCount = await deployed.read.getCount()
setCount(initialCount)
}
deploy()
}, [client])
const handleIncrement = async () => {
if (!deployedAddress) return
// Get contract with address
const contract = Counter.withAddress(deployedAddress)
// Call increment method
await client.writeContract(contract.write.increment())
// Mine a block to include the transaction
await client.mine({ blocks: 1 })
// Get updated count
const newCount = await client.readContract(contract.read.getCount())
setCount(newCount)
}
return (
Tevm Counter Example
Count: {count.toString()}
)
}
export default App
```
#### Example of Type-Safe Method Calls
The bundler generates strongly-typed methods for your contract:
```ts
// Read methods (view/pure functions)
const balance = await client.readContract(
ERC20.read.balanceOf('0x' + '20'.repeat(20))
)
// Write methods (state-changing functions)
await client.writeContract(
ERC20.write.transfer('0x' + '30'.repeat(20), 1000000n)
)
// Listen to events
client.watchEvent(
ERC20.events.Transfer({
from: '0x' + '20'.repeat(20),
to: null // `null` acts as a wildcard
}),
(log) => console.log('Transfer event:', log)
)
```
### What's Happening?
When you import `Counter.s.sol`, Tevm:
1. Compiles the Solidity code using solc
2. Generates TypeScript with the ABI, bytecode, and a Contract instance
3. Provides you with a fully typed interface to the contract
The `Counter` object is a [Tevm Contract](/reference/contract) instance with:
* `abi`: The contract's ABI
* `address`: The contract's address (if set with `withAddress()`)
* `bytecode`: The contract's creation bytecode (undefined for regular .sol files)
* `deployedBytecode`: The contract's runtime bytecode (undefined for regular .sol files)
* `deploy()`: Method to create deployment data with constructor arguments
* `read`: Type-safe read methods (view/pure functions)
* `write`: Type-safe write methods (state-changing functions)
* `events`: Type-safe event filters for subscription
* `withAddress()`: Method to create a new instance with an address
:::info
Note that `bytecode` and `deployedBytecode` are only available when using the `.s.sol` extension. If you import a regular `.sol` file, these properties will be `undefined`, and you won't be able to deploy the contract.
:::
### Advanced Configuration
#### Foundry Integration
By default, Tevm uses Node.js resolution to resolve contract imports (similar to how JavaScript imports work). However, if you're working with a Foundry project or need custom import remappings, create a `tevm.config.json` file in your project root:
```json
{
"foundryProject": true,
"libs": ["lib", "node_modules"],
"remappings": {
"@openzeppelin/": "node_modules/@openzeppelin/"
},
"cacheDir": ".tevm"
}
```
:::tip
It's highly recommended to add the `.tevm` cache directory to your `.gitignore`:
```bash
# .gitignore
.tevm
```
This prevents compiled artifacts from being committed to your repository. The `.tevm` directory is ephemeral - you can safely delete it if you encounter stale builds or caching issues, but it will require recompiling all contracts on the next build.
:::
Setting `"foundryProject": true` will:
* Automatically read your Foundry remappings from `foundry.toml` or `remappings.txt`
* Include your Foundry library paths (`lib/` directory by default)
* Allow you to import contracts using the same paths as in your Foundry project
* Merge your manually specified remappings and libs with those from Foundry
You can also manually set remappings and lib paths without using Foundry:
```json
{
"foundryProject": false, // or omit this line
"libs": [
"./contracts",
"node_modules"
],
"remappings": {
"@openzeppelin/": "node_modules/@openzeppelin/",
"ds-test/": "lib/forge-std/lib/ds-test/src/",
"solmate/": "node_modules/solmate/src/"
}
}
```
This configuration is especially useful if you:
* Have a mixed Foundry/JavaScript project
* Use Forge libraries like OpenZeppelin or Solmate
* Have complex import paths in your Solidity code
For a complete example of Bundler + Foundry integration, see the [Foundry example](https://github.com/evmts/tevm-monorepo/tree/main/examples/vite) in the Tevm examples repository.
### Using Third-Party Contracts
#### NPM Packages
You can import contracts from npm packages:
```ts
// Import OpenZeppelin ERC20 - use .s.sol extension if you need bytecode
import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.s.sol'
// Deploy with constructor arguments - need to call .deploy() with constructor args
const myToken = await client.deployContract(
ERC20.deploy("MyToken", "MTK")
)
```
#### Coming Soon Features
Tevm is actively developing exciting new features to make working with contracts even easier:
##### Network Imports with Macros
:::info
The macros-based network import feature is coming soon and is not yet available in the current release.
:::
In an upcoming version, Tevm will support importing contracts from any EVM network using build-time macros. This provides better performance and stronger type safety by resolving contracts during your build process instead of at runtime:
```ts
// First, create a file with contract macro functions
// contract-macros.js
import { createMemoryClient } from 'tevm'
import { http } from 'viem'
import { mainnet, optimism } from 'viem/chains'
import { loadContract } from 'tevm'
import { SourcifyABILoader, EtherscanABILoader } from 'tevm/whatsabi'
// Set up clients for different networks
// For reproducible builds, use fixed block heights
const mainnetClient = createMemoryClient({
fork: {
transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY'),
blockNumber: 19000000n
}
})
const optimismClient = createMemoryClient({
fork: {
transport: http('https://mainnet.optimism.io'),
blockNumber: 116000000n
}
})
// Configure loaders for each network
const mainnetLoaders = [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: 'YOUR_ETHERSCAN_KEY' })
]
const optimismLoaders = [
new SourcifyABILoader(),
new EtherscanABILoader({
apiKey: 'YOUR_ETHERSCAN_KEY',
baseUrl: 'https://api-optimistic.etherscan.io/api'
})
]
// Create macro function for WETH on Ethereum
export async function wethContract() {
return loadContract(mainnetClient, {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
followProxies: true,
loaders: mainnetLoaders
})
}
// Create macro function for USDC on Optimism
export async function usdcContract() {
return loadContract(optimismClient, {
address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
followProxies: true,
loaders: optimismLoaders
})
}
```
Then import the contracts using the `with {type: 'macro'}` attribute syntax:
```ts
// Import contracts using macros
import { wethContract } from './contract-macros.js' with { type: 'macro' }
import { usdcContract } from './contract-macros.js' with { type: 'macro' }
// Use these contracts directly with full type safety
const wethBalance = await client.readContract({
...wethContract.read.balanceOf('0x123...'),
address: wethContract.address
})
// Access additional properties
console.log(`USDC decimals: ${await client.readContract({
...usdcContract.read.decimals(),
address: usdcContract.address
})}`)
console.log(`Human readable ABI: ${usdcContract.humanReadableAbi}`)
console.log(`Implementation address: ${usdcContract.proxyDetails?.[0]?.implementation || 'Not a proxy'}`)
```
Macros must be enabled in your `tevm.config.json` file for security reasons:
```json
{
"macros": true,
"foundryProject": true,
"libs": ["lib", "node_modules"]
}
```
This approach automatically resolves ABIs at build time and creates fully type-safe contract interfaces. For more details, see the [Contract Loader](/api/whatsabi-integration#network-imports-via-macros) documentation.
##### Inline Solidity with Template Literals
:::info
The inline Solidity feature is coming soon and is not yet available in the current release.
:::
For quick prototyping or one-off contracts, you'll soon be able to define Solidity contracts directly in your JavaScript/TypeScript using template literals:
```ts
import { sol } from 'tevm'
// Define a contract inline
const { Counter } = sol`
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Counter {
uint256 private count = 0;
function increment() public {
count += 1;
}
function getCount() public view returns (uint256) {
return count;
}
}
`
// Use it just like an imported contract
const deployed = await client.deployContract(Counter.deploy())
await deployed.write.increment()
const count = await deployed.read.getCount()
```
This will provide the same type-safety and features as file-based imports, but with the convenience of inline code.
### Documentation Resources
* Bundler Reference Documentation:
* [Overview](/reference/bundler/overview) - Key benefits and features
* [Internals](/reference/bundler/internals) - How the bundler works
* [Methods & Exports](/reference/bundler/methods) - Advanced APIs
* [Troubleshooting](/reference/bundler/troubleshooting) - Common issues and solutions
* For AI and LLM users, the full Tevm documentation is available as plain text at [https://node.tevm.sh/llms-full.txt](https://node.tevm.sh/llms-full.txt)
### Troubleshooting
* **Red underlines in imported Solidity**: Make sure your editor is using the workspace version of TypeScript
* **No bytecode available**: Check that you're using the `.s.sol` extension for contracts you want to deploy
* **Deployment errors**: Make sure you're calling `.deploy()` with any required constructor arguments
* **Compilation errors**: Check your Solidity code and ensure you're using a compatible Solidity version
* **TypeScript errors**: Ensure you have the TypeScript plugin correctly configured
* **Red underlines in editor**: Verify your editor is using the workspace version of TypeScript with the plugin loaded
* **Import resolution failures**: If you're using Foundry-style imports (like `@openzeppelin/contracts/...`) and seeing errors:
* Create a `tevm.config.json` with `"foundryProject": true`
* Check that your remappings in `foundry.toml` are correct
* Try adding explicit remappings in `tevm.config.json`
* **Test runner issues**: Most test runners (Vitest, Jest) work out-of-the-box once the bundler plugin is configured. For Jest, you might need extra configuration or use the codegen approach with `tevm generate contract`
### Codegen Alternative to Bundler
:::tip
If your framework conflicts with the bundler approach or you prefer explicit type generation, try the codegen approach using the Tevm CLI:
```bash
tevm generate contract
```
This generates `.ts` files alongside your `.sol` files that can be imported directly, without requiring bundler integration. This is especially useful for Next.js or other frameworks with strict type-checking pipelines.
For more control over the generation process, you can use additional options:
```bash
# Generate types for specific contracts only
tevm generate contract "ERC20Token"
# Customize input patterns and output location
tevm generate contract --include "contracts/**/*.sol" --output "types/"
```
:::
### Next Steps
Now that you have the bundler set up, you can:
* [Learn more about Tevm Contracts](/reference/contract)
* Explore the bundler in depth:
* [Bundler Overview](/reference/bundler/overview) - Key benefits and features
* [Bundler Internals](/reference/bundler/internals) - How the bundler works
* [Advanced Methods & APIs](/reference/bundler/methods) - For custom implementations
* [Troubleshooting](/reference/bundler/troubleshooting) - Solutions for common issues
* [Build applications with Tevm Node](/core/create-tevm-node)
For complete project examples, check out the [Tevm examples repository](https://github.com/evmts/tevm-monorepo/tree/main/examples).
## Tevm CLI Quickstart
The Tevm CLI offers a powerful command-line interface to interact with Ethereum networks, local development environments, and smart contracts directly from your terminal. This guide will help you get started with the most important CLI commands.
### Installation
Install the Tevm CLI globally:
```bash
npm install -g tevm
```
Or use it directly with npx:
```bash
npx tevm
```
### Core Commands
Tevm CLI provides several key commands to help with Ethereum development workflows:
#### 1. Project Creation
Create a new Ethereum project with the interactive CLI:
```bash
tevm create my-project
```
This launches an interactive wizard that guides you through configuring:
* Project name and location
* Framework selection (React, Vue, Vanilla, etc.)
* Use case (UI, API, testing)
* Package manager preference
* Other project settings
For non-interactive mode, specify options directly:
```bash
tevm create my-project --template react --skip-prompts
```
#### 2. Running a Local Ethereum Server
Start a full-featured Ethereum JSON-RPC server with Tevm features:
```bash
tevm serve
```
This launches a server on `localhost:8545` with an interactive terminal UI for managing your node.
Options include:
```bash
# Launch on a custom port
tevm serve --port 8546
# Fork from a mainnet or testnet
tevm serve --fork https://mainnet.infura.io/v3/YOUR_KEY
# Set custom chain ID
tevm serve --chainId 1337
# Advanced logging
tevm serve --verbose --loggingLevel debug
```
The interactive server interface provides tabs for:
* Viewing server status
* Executing RPC calls
* Managing accounts
* Viewing transaction logs
#### 3. Smart Contract Interaction
Execute calls against contracts with full type safety:
```bash
tevm call --to 0x123... --data 0xabcdef...
```
This command opens an interactive editor for constructing your call parameters, then executes the call against the target contract.
For direct execution without the interactive editor:
```bash
tevm call --to 0x123... --data 0xabcdef... --run
```
The `call` command supports a wide range of options for customizing your EVM execution:
```bash
# Specify gas limit, price, and value
tevm call --to 0x123... --gas 1000000 --gasPrice 1000000000 --value 1000000000000000000
# Connect to a remote node instead of local
tevm call --to 0x123... --rpc https://mainnet.infura.io/v3/YOUR_KEY
# Create a trace of the execution
tevm call --to 0x123... --createTrace
```
#### 4. TypeScript Generation from Solidity
Generate TypeScript types and interfaces from Solidity contracts:
```bash
tevm generate contract
```
This command scans for all Solidity files in your project and generates corresponding TypeScript files with full type definitions, without requiring a bundler plugin.
```bash
# Generate types for specific files or patterns
tevm generate contract "ERC20Token"
# Specify input and output directories
tevm generate contract --include "contracts/**/*.sol" --output "types/"
# Force overwrite existing files
tevm generate contract --force
```
### Additional Useful Commands
Tevm CLI includes many specialized commands for specific Ethereum operations:
#### Account & State Management
```bash
# Get account details
tevm getAccount --address 0x123...
# Set account state
tevm setAccount --address 0x123... --balance 1000000000000000000
# Set contract code
tevm setCode --address 0x123... --code 0x...
# Dump full state for backup/debugging
tevm dumpState --output state.json
```
#### Mining & Block Control
```bash
# Mine new blocks
tevm mine --blocks 1
# Load state from a file
tevm loadState --input state.json
```
#### Contract Testing
```bash
# Deploy a contract
tevm deploy --code 0x...
# Read from a contract
tevm readContract --address 0x123... --abi [...] --function "balanceOf" --args "[\"0x456...\"]"
```
### Interactive Mode
Most Tevm CLI commands support an interactive mode that launches your default editor (configurable via environment variables) with a template for the command parameters. This is especially useful for complex operations where you need to carefully construct parameters.
Disable interactive mode with the `--run` flag to execute commands directly.
### Environment Variables
The Tevm CLI supports configuration via environment variables:
```bash
# Set default RPC endpoint
export TEVM_RPC=http://localhost:8545
# Set default "from" address for transactions
export TEVM_FROM=0x123...
# Configure default editor for interactive mode
export EDITOR=code
```
### Next Steps
Now that you're familiar with the Tevm CLI basics, you can:
* [Learn about the Tevm Node API](/core/create-tevm-node)
* [Explore the Tevm bundler for importing Solidity directly](/getting-started/bundler)
* [Check out the full CLI reference](/reference/cli)
* \[Run the `tevm --help` command for a complete list of available commands and options]
The Tevm CLI makes Ethereum development and testing more efficient by providing a unified interface for common operations directly from your terminal. Combined with Tevm's powerful EVM implementation, you can build, test, and deploy Ethereum applications with confidence.
import { Callout, Steps, Button } from "vocs/components";
import { TabGroup } from "../../components";
## Getting Started with Ethers.js
While Tevm is built with viem as its primary API, it provides **full
compatibility** with [ethers.js](https://docs.ethers.org/v6/) through its
EIP-1193 provider interface! This makes Tevm a perfect fit for existing
ethers.js projects.
This guide shows you how to use Tevm Node with ethers.js, allowing you to leverage Tevm's powerful features with the ethers library you already know.
### Installation
#### Install Dependencies
First, install Tevm and ethers as dependencies:
`bash npm install tevm ethers@latest `
`bash pnpm add tevm ethers@latest `
`bash yarn add tevm ethers@latest `
`bash bun add tevm ethers@latest `
#### Create Tevm Client
Set up your Tevm client and enable the EIP-1193 interface:
```ts
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/chains";
import { requestEip1193 } from "tevm/decorators";
// Create a client (optionally forking from a network)
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
// Enable EIP-1193 compatibility for ethers
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
```
#### Connect Ethers
Create an ethers provider that uses your Tevm node:
```ts
import { BrowserProvider, Wallet } from "ethers";
// Create ethers provider using the Tevm node
const provider = new BrowserProvider(client.transport.tevm);
// Create a new wallet or connect an existing one
const signer = Wallet.createRandom().connect(provider);
// Fund the wallet using Tevm's direct state modification
await client.setBalance({
address: signer.address,
value: 1000000000000000000n, // 1 ETH
});
```
#### Start Using Ethers
You're now ready to use ethers.js with Tevm's local environment:
```ts
// Check the current block
const blockNumber = await provider.getBlockNumber();
console.log(`Current block: ${blockNumber}`);
// Check your wallet balance
const balance = await provider.getBalance(signer.address);
console.log(`Wallet balance: ${balance.toString()}`);
```
### Complete Example
The following example demonstrates how to use ethers.js with Tevm to interact with a contract:
```ts showLineNumbers {3,12-13,27-28,47-48,66} filename="ethers-with-tevm.ts"
import { createMemoryClient, http, parseAbi } from "tevm";
import { optimism } from "tevm/chains";
import { requestEip1193 } from "tevm/decorators"; // [!code focus]
import { BrowserProvider, Wallet, Contract, formatEther } from "ethers";
import { parseUnits } from "ethers/utils";
// Step 1: Create a memory client forking from Optimism
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
// Step 2: Enable EIP-1193 compatibility for ethers // [!code focus]
client.transport.tevm.extend(requestEip1193()); // [!code focus]
await client.tevmReady();
// Step 3: Set up ethers provider and wallet
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
// Verify connectivity by getting the block number
const blockNumber = await provider.getBlockNumber();
console.log(`Current block number: ${blockNumber}`);
// Step 4: Set up contract interaction // [!code focus]
const greeterContractAddress = "0x10ed0b176048c34d69ffc0712de06CbE95730748"; // [!code focus]
const greeterAbi = parseAbi([
"function greet() view returns (string)",
"function setGreeting(string memory _greeting) public",
]);
const greeter = new Contract(greeterContractAddress, greeterAbi, signer);
// Helper functions for cleaner interaction
const getGreeting = async () => await greeter.greet();
const setGreeting = async (newGreeting) => {
const tx = await greeter.setGreeting(newGreeting);
return tx.hash;
};
// Step 5: Fund our wallet (using Tevm's test actions)
await client.setBalance({
address: signer.address,
value: parseUnits("1.0", "ether"),
});
console.log(
`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`,
);
// Step 6: Read current greeting
console.log(`Original greeting: ${await getGreeting()}`);
// Step 7: Update the greeting // [!code focus]
const txHash = await setGreeting("Hello from ethers.js and Tevm!"); // [!code focus]
console.log(`Transaction sent: ${txHash}`);
// Step 8: Mine the block to include our transaction
await client.mine({ blocks: 1 });
// Step 9: Verify the updated greeting
console.log(`Updated greeting: ${await getGreeting()}`);
```
Unlike real networks, Tevm requires you to manually mine blocks after sending
transactions. Don't forget to call `client.mine()` after each transaction!
Understanding the Code
The integration between Tevm and ethers.js happens through these key components:
#### EIP-1193 Decorator
```ts
// Enable EIP-1193 standard provider interface
client.transport.tevm.extend(requestEip1193());
```
The `requestEip1193()` decorator transforms Tevm's internal interface into the standard provider interface that ethers.js expects. This crucial bridge allows the two libraries to communicate seamlessly.
#### Provider Creation
```ts
// Create ethers provider using the Tevm node
const provider = new BrowserProvider(client.transport.tevm);
```
Ethers accepts the decorated Tevm node as a standard Ethereum provider, allowing all ethers functionality to work as expected.
#### Hybrid API Approach
The example demonstrates a hybrid approach:
* **Ethers API** for contract interaction (`Contract`, `provider`, `signer`)
* **Tevm API** for blockchain control (`client.setBalance()`, `client.mine()`)
This gives you the best of both worlds - familiar ethers.js contract interaction with Tevm's powerful state control.
#### Mining Control
```ts
// Mine the block to include our transaction
await client.mine({ blocks: 1 });
```
Unlike when using real networks with ethers, you need to explicitly mine blocks with Tevm to include transactions. This gives you precise control over transaction execution timing.
### Common Patterns
#### Contract Deployment
Here's how to deploy a new contract using ethers.js with Tevm:
```ts showLineNumbers
import { createMemoryClient } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet, ContractFactory, formatEther } from "ethers";
// Set up Tevm and ethers
const client = createMemoryClient();
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
// Fund the signer
await client.setBalance({
address: signer.address,
value: 10000000000000000000n, // 10 ETH
});
console.log(
`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`,
);
// Contract ABI and bytecode
const counterAbi = [
"function count() view returns (uint256)",
"function increment() public",
];
const counterBytecode =
"0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5fb46adf6ce0cfd90fa4324ffd8c48b0fc6fb6c4cac9ca2c69c97e25f355c9d64736f6c63430008110033";
// Deploy contract
const counterFactory = new ContractFactory(counterAbi, counterBytecode, signer);
const counterDeploy = await counterFactory.deploy();
console.log(
`Deployment transaction: ${counterDeploy.deploymentTransaction().hash}`,
);
// Mine to include the deployment transaction
await client.mine({ blocks: 1 });
// Get contract at deployed address
const counter = await counterDeploy.waitForDeployment();
const counterAddress = await counter.getAddress();
console.log(`Counter deployed at: ${counterAddress}`);
// Use the contract
console.log(`Initial count: ${await counter.count()}`);
const tx = await counter.increment();
console.log(`Increment transaction: ${tx.hash}`);
await client.mine({ blocks: 1 });
console.log(`New count: ${await counter.count()}`);
```
#### Reading and Writing Contract Data
Interact with an existing contract using ethers.js:
```ts
import { createMemoryClient, http, parseAbi } from "tevm";
import { mainnet } from "tevm/chains";
import { requestEip1193 } from "tevm/decorators";
import {
BrowserProvider,
Wallet,
Contract,
formatEther,
formatUnits,
} from "ethers";
// Setup client with a mainnet fork
const client = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
},
});
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
// Setup provider and wallet
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
// Fund the wallet
await client.setBalance({
address: signer.address,
value: 100000000000000000000n, // 100 ETH
});
// Connect to USDC contract
const usdcAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const usdcAbi = parseAbi([
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address owner) view returns (uint256)",
]);
const usdc = new Contract(usdcAddress, usdcAbi, provider);
// Read-only operation
const [name, symbol, decimals, totalSupply] = await Promise.all([
usdc.name(),
usdc.symbol(),
usdc.decimals(),
usdc.totalSupply(),
]);
console.log(`Contract: ${name} (${symbol})`);
console.log(`Decimals: ${decimals}`);
console.log(`Total Supply: ${formatUnits(totalSupply, decimals)} ${symbol}`);
// Check DAI's USDC balance
const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const daiUsdcBalance = await usdc.balanceOf(daiAddress);
console.log(
`DAI contract's USDC balance: ${formatUnits(daiUsdcBalance, decimals)} ${symbol}`,
);
```
#### Working with Events
Listen for and query contract events:
```ts
import { createMemoryClient } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet, Contract, formatUnits } from "ethers";
import { parseAbi } from "tevm";
// Setup
const client = createMemoryClient();
client.transport.tevm.extend(requestEip1193());
await client.tevmReady();
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
// Fund account
await client.setBalance({
address: signer.address,
value: 10000000000000000000n,
});
// Deploy a simple ERC20 token
const tokenAbi = parseAbi([
"constructor(string name, string symbol, uint8 decimals)",
"function transfer(address to, uint256 amount) returns (bool)",
"function balanceOf(address owner) view returns (uint256)",
"event Transfer(address indexed from, address indexed to, uint256 value)",
]);
const tokenBytecode =
"0x608060405234801561001057600080fd5b506040516107fa3803806107fa83398101604081905261002f91610215565b600380546001600160a01b031916331790558151610052906004906020850190610076565b5081516100669060059060208401906100fd565b506006805460ff191660ff929092169190911790555061030c565b828054610082906102c8565b90600052602060002090601f0160209004810192826100a4576000855561010a565b82601f106100bd57805160ff19168380011785556100ea565b828001600101855582156100ea579182015b828111156100ea5782518255916020019190600101906100cf565b506100f69291506100f6565b5090565b5b808211156100f657600081556001016100f7565b828054610109906102c8565b90600052602060002090601f01602090048101928261012b5760008552610167565b82601f1061014457805160ff1916838001178555610171565b82800160010185558215610171579182015b82811115610171578251825591602001919060010190610156565b5061017d9291506101bd565b5090565b5b8082111561017d576000815560010161017e565b6000602082840312156101a657600080fd5b81516001600160a01b03811681146101bd57600080fd5b9392505050565b5b8082111561017d57600081556001016101be565b80516001600160a01b03811681146101eb57600080fd5b919050565b600080600060608486031215610205578283fd5b833590925060208401359160408401359050509250925092565b60008060006060848603121561022a578081fd5b835160208501516040860151919450919290910181111581146102ad57634e487b7160e01b600052601160045260246000fd5b809150509250925092565b634e487b7160e01b600052602260045260246000fd5b600181811c908216806102dc57607f821691505b6020821081036102fc57634e487b7160e01b600052602260045260246000fd5b50919050565b6104df8061031b6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806370a082311461004657806395d89b4114610079578063a9059cbb14610081575b600080fd5b610066610054366004610356565b6001600160a01b031660009081526001602052604090205490565b60405190815260200160405180910390f35b61007b610094565b005b61007b61008f366004610379565b610121565b6005805461009f906104a0565b80601f01602080910402602001604051908101604052809291908181526020018280546100cb906104a0565b80156101185780601f106100ed57610100808354040283529160200191610118565b820191906000526020600020905b8154815290600101906020018083116100fb57829003601f168201915b505050505081565b6001600160a01b03821660009081526001602052604081205461014a908363ffffffff61022916565b6001600160a01b038416600090815260016020526040812091909155610177908263ffffffff61024116565b6001600160a01b0383166000908152600160205260409020556101d0816040518060600160405280602381526020016104876023913960405180604001604052806029815260200161045e60299139600090339161025a565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610216918252602082015260400190565b60405180910390a35050565b600081831061023857506000610239565b5b92915050565b6000828201610239825b8351602g2b6020600020905b905090565b60408051808201909152602d8152600080516020610457833981519152602082015290565b9050919050565b841515610297576001600160e01b031916600052601160045260246000fd5b5063ffffffff1690565b6000602082840312156102b357600080fd5b81356001600160a01b03811681146102ca57600080fd5b9392505050565b6000602082840312156102e357600080fd5b813560fc81168114t12870be52600052602260045260246000d5b602082108a5734e487b7160q0b600052602260045260246000fd5b50919050565b6004f3fe68747470733a2f2f6769646c6572732e696f2f6a616d657368756768657369o97365722f7468696e67736e6f626f6479656c7365686173a2646970667358221220f3acb75fca24514561f12a53c4a92042a9a6c524895dc1b01f3c3d2cda5d8ff364736f6c634300080a0033";
const tokenFactory = new ContractFactory(tokenAbi, tokenBytecode, signer);
const token = await tokenFactory.deploy("Test Token", "TST", 18);
await client.mine({ blocks: 1 });
const tokenAddress = await token.getAddress();
console.log(`Token deployed at: ${tokenAddress}`);
// Set up event listener
token.on("Transfer", (from, to, value, event) => {
console.log(`Transfer detected in block ${event.blockNumber}:`);
console.log(` From: ${from}`);
console.log(` To: ${to}`);
console.log(` Value: ${formatUnits(value, 18)} TST`);
});
// Send a transfer transaction
const recipient = Wallet.createRandom().address;
const tx = await token.transfer(recipient, 1000000000000000000n); // 1 TST
console.log(`Transfer transaction: ${tx.hash}`);
// Mine to include transaction
await client.mine({ blocks: 1 });
// Check balance
const balance = await token.balanceOf(recipient);
console.log(`Recipient balance: ${formatUnits(balance, 18)} TST`);
// Clean up event listener
token.removeAllListeners();
```
### Key Differences from Standard Ethers Usage
When using ethers.js with Tevm, keep these key differences in mind:
**Mining is manual!** Transactions won't be automatically mined - you need to
call `client.mine()` to process them.
1. **Transaction Flow**
* **Real Network**: Transactions are mined by the network automatically
* **Tevm**: You must manually call `client.mine()` to process transactions
2. **Account Management**
* **Real Network**: Accounts have real funds and nonces
* **Tevm**: Create and fund accounts on demand with `client.setBalance()`
3. **Environment Control**
* **Real Network**: Limited ability to manipulate state, block time, etc.
* **Tevm**: Complete control over all aspects of the environment
4. **Performance**
* **Real Network**: Operations require network calls and confirmations
* **Tevm**: All operations happen locally with near-instant execution
5. **Debugging Capabilities**
* **Real Network**: Limited visibility into transaction execution
* **Tevm**: Step-by-step EVM tracing and execution insights
6. **Determinism**
* **Real Network**: Network conditions can affect execution
* **Tevm**: 100% deterministic execution for reliable testing
### Advanced Features
#### EVM State Control
Take advantage of Tevm's state manipulation with ethers:
```ts
// Fund an account
await client.setBalance({
address: "0x123...",
value: parseUnits("1000", "ether"),
});
// Set storage directly
await client.setStorageAt({
address: contractAddress,
index: 0, // Storage slot
value: "0x1234...",
});
// Adjust block timestamp
await client.setNextBlockTimestamp(Date.now());
```
#### Contract Debugging
Debug ethers transactions with Tevm's EVM hooks:
```ts
// Get underlying Tevm node for advanced operations
const tevmNode = await client.getTevmNode();
const vm = await tevmNode.getVm();
// Listen to EVM execution steps
vm.evm.events.on("step", (data, next) => {
console.log(`EVM Step: ${data.opcode.name}`);
next();
});
// Now run a transaction with ethers as normal
// You'll see detailed logs of each EVM step
```
#### Fork Management
Work with mainnet contracts in your test environment:
```ts
// Reset to a clean fork state
await client.reset({
fork: {
blockNumber: 42069n,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
},
});
// After reset, your ethers provider still works
// but with the newly reset chain state
```
### Next Steps
Now that you know how to use ethers.js with Tevm, you can:
import { Callout, Steps, Button } from "vocs/components";
Tevm defaults to **manual mining mode**, which means transactions are added to the mempool but not automatically included in blocks.
* When using `client.sendTransaction()` or `tevmCall` with `addToMempool: true`, you must explicitly call `client.tevmMine()` to include the transaction in a block
* Use `addToBlockchain: true` for immediate transaction inclusion (automatically mines)
* See [Mining Modes](../core/mining-modes) for more details on mining configuration
### Getting Started
::::steps
#### Install Tevm
First, install Tevm in your project:
:::code-group
```bash [npm]
npm install tevm
```
```bash [pnpm]
pnpm add tevm
```
```bash [yarn]
yarn add tevm
```
```bash [bun]
bun add tevm
```
:::
#### Choose Your Client
Tevm offers different client types depending on your needs
##### In-memory client
You can spin up an empty ethereum node
```ts
import { createMemoryClient } from "tevm";
const memoryClient = createMemoryClient();
const blockNumber = await memoryClient.getBlockNumber();
```
:::details[How it works]
1. We create a `memoryClient` that runs an Ethereum node entirely in memory built with TypeScript and Wasm
2. We use the [`viem` api](https://viem.sh) to query it.
:::
##### Fork client
You can fork an existing chain similar to Anvil and Hardhat.
```typescript
import { createMemoryClient } from "tevm";
import { http } from "viem"; // http is available in tevm package as well for convenience
import { mainnet } from "tevm/chains";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
},
});
```
(coming soon) You can give this client out a try right now using the Tevm CLI
```bash
bunx tevm tevmCall --rpc=https://mainnet.optimism.io
```
:::details[How it works]
1. We create a `memoryClient` like before but this time we pass in a transport
2. Tevm will fetch the latest block upon creation
3. As Tevm executes it will lazily fetch state from the fork url
4. Tevm has optimizations that make it more efficient at fetching state than alternatives like Anvil or Hardhat
:::
##### Rebasing client (coming soon)
This powerful client will not only fork an existing chain but it will also listen for new blocks. As new blocks come in it will rebase it's state on the existing chain.
```typescript
import { createMemoryClient } from "tevm";
import { http } from "viem";
import { mainnet } from "tevm/chains";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
rebase: true,
},
});
```
:::details[How it works]
1. We create a `memoryClient` like our forked client but this time we pass in `rebase: true`
2. Tevm will fork just like before
3. Tevm will update it's fork as "latest" tag changes on the forked chain making sure to efficiently invalidate any state it needs to.
:::
##### Tree-shakable client
In addition to using `createMemoryClient` Tevm supports the Viem tree-shakable api.
```typescript
import { createClient, http } from "viem";
import { createTevmTransport } from "tevm/memory-client";
import { optimism } from "tevm/common";
const client = createClient({
transport: createTevmTransport({
fork: { transport: http("https://mainnet.optimism.io") },
common: optimism,
}),
});
// Import actions from viem
import { getBlockNumber } from "viem";
await getBlockNumber(client);
```
The tree-shakable API will not include any viem or tevm actions you aren't using. For apps running in the browser the tree-shakable client is recomended to keep bundle size minimal.
##### Ethers client
In addition to viem support Tevm plugs into Ethers as well as any other library that supports the `EIP-1193 Provider` standard.
```typescript
import { TevmProvider } from "tevm/ethers";
import { http, toHex, parseEth } from "tevm";
const provider = await TevmProvider.createMemoryProvider({
fork: {
transport: http("https://mainnet.optimism.io"),
},
});
await provider.send("tevm_setAccount", [
{
address: `0x${"69".repeat(20)}`,
nonce: toHex(1),
balance: toHex(parseEth("25")),
},
]);
```
#### Use your client to read and write to an in-memory blockchain
Below is a complete typical flow of using Tevm to:
1. Add a contract to the Tevm state
2. Write to the contract with viem
3. Mine a new block with the transaction
4. Read from contract with viem
```ts showLineNumbers {1,11-13,22} filename="simple-contract.ts"
import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { http } from "viem";
import { SimpleContract } from "tevm/contract";
import { optimism } from "tevm/common";
// Create an in-memory Ethereum client forking optimism
const client = createMemoryClient({
common: optimism,
fork: { transport: http("https://mainnet.optimism.io") },
});
const contract = SimpleContract.withAddress(`0x${"40".repeat(20)}`);
// Deploy contract bytecode. There are many ways to do this including tevmDeploy.
// Here we just use an anvil test action
await client.setCode({
address: contract.address,
bytecode: contract.deployedBytecode,
});
// Write to contract
await client.writeContract({
account: PREFUNDED_ACCOUNTS[0],
abi: contract.abi,
functionName: "set",
args: [420n],
address: contract.address,
});
await client.tevmMine();
// Read from contract
const value = await client.readContract({
abi: contract.abi,
functionName: "get",
address: contract.address,
});
console.log(value); // 420n
```
:::details[How it works]
1. We create a `memoryClient` that runs an Ethereum node entirely in memory
2. We define a contract and its address
3. We deploy the contract using `setCode` (simpler than a full deployment transaction)
4. We write to the contract using a familiar viem-style interface
5. We mine the transaction to include it in a block
6. We read the updated value from the contract
:::
#### Key Points:
* **Familiar Interface** โ Tevm uses [viem](https://viem.sh) as its primary API, making it instantly familiar to viem users
* **Ethers Support** - Tevm also supports `ethers.js` as via the `EIP-1193 provider` standard
* **In-Memory Execution** โ TevmNode runs the Ethereum environment directly in memory rather than using JSON-RPC over HTTP
* **Full Control** โ Mine blocks on demand, manipulate state, and more
* **Cross platform** - Run your ethereum node in the browser
* **Powerful** - Comes with many advantages over an `Anvil`, `Ganache`, or `Hardhat` node
#### Start Building
You're now ready to build with Tevm!
```ts
// Runs in browsers, Node.js, Deno, Bun and beyond
// Zero native dependencies
import { createMemoryClient } from "tevm";
// Optionally import solidity contracts directly into typescript in a typesafe way
// This requires the Tevm Bundler to be set up (see Bundler Quickstart)
import { ComplexSimulation } from "../contracts/ComplexSimulation.s.sol";
const client = createMemoryClient();
// Use the viem api you already know
console.log(await client.getBlockNumber());
// Or use powerful typescript native apis
const {
data,
error,
logs,
createdAddresses,
executionGasUsed,
l1Fee,
trace,
accessList,
txHash,
} = await client.tevmContract({
deployedBytecode: ComplexSimulation.deployedBytecode,
...ComplexSimulation.read.simulate(2n, "arg2"),
createTrace: true,
createAccessList: true,
createTransaction: true,
throwOnFail: false,
onStep: (step, next) => {
console.log(step.opcode);
next?.();
},
});
```
::::
### Essential viem concepts
If you're new to Ethereum development, don't worry! Tevm serves as an excellent learning platform for both Ethereum and TypeScript:
:::info
* **Isolated Environment** โ Experiment freely without worrying about network costs or confirmation times
* **TypeScript Integration** โ Get full type safety and autocomplete for Ethereum interactions
* **Simple API Surface** โ Focus on learning core concepts with a streamlined interface
* **Step Debugger** โ Trace transaction execution at the opcode level for deep understanding
:::
For users new to viem it is recomended you at least become [familiar with viem](https://viem.sh). But as a short introduction of the essential concepts you need to know:
* `Clients` are made with `createClient`, `createPublicClient`, `createWalletClient`, `createTestClient`, and `createMemoryClient` and are the main abstraction you interact with.
* `Actions` such as `getBlockNumber`, `call`, `readContract`, `estimateGas`, and `tevmSetAccount` are how you interact with `viem` and `tevm`
* `Transports` such as `http` and `createTevmTransport` are EIP-1193 compatable providers used to resolve JSON-RPC requests. Both `viem` and `tevm` use transports such as the `http()` transport to fetch remotely over http
* `TevmNode` itself is a transport that plugs an in-memory ethereum node into viem to resolve JSON-RPC requests
### Ready to dive in right away?
import { Callout, Button } from "vocs/components";
## Community
Here's what developers and industry leaders are saying about Tevm
### Community Highlights
:::tip[Developer Feedback]
Tevm's unique approach of bringing Ethereum execution directly to JavaScript environments has resonated with most developers who have given Tevm a try.
Developers report problems previously difficult being simple to solve with Tevm
:::
"Fully @tevmtools pilled now. The beauty and harmony of the dev tooling ๐."
"If you're building a blockchain application on the web, I'm almost certain
there is a use case for Tevm. It might change everything, and at worse it
would most likely improve your devX."
### Share Your Experience
:::steps
#### Join the Community
We'd love to hear how Tevm has improved your development workflow:
* [Telegram Group](https://t.me/+ANThR9bHDLAwMjUx)
* [Twitter/X](https://twitter.com/tevmtools)
#### Report Success Stories
Have a compelling use case or success story?
* [Create an issue](https://github.com/evmts/tevm-monorepo/issues/new?labels=testimonial\&template=testimonial.md) with the label "testimonial"
* Email the team at [support@tevm.sh](mailto\:support@tevm.sh)
:::
Your feedback helps us improve Tevm and guides our development priorities!
import { Callout, Steps, Button } from "vocs/components";
## Getting Started with Viem
:::tip[Perfect for Viem Users]
If you're already familiar with [viem](https://viem.sh), Tevm works seamlessly with your existing knowledge, providing a nearly identical API.
:::
This guide will help you integrate Tevm with [viem](https://viem.sh), the modern TypeScript interface for Ethereum. By the end, you'll have a working setup with Tevm Node and understand how to leverage viem's actions with Tevm.
### Installation
::::steps
#### Install Dependencies
First, install Tevm along with viem as a peer dependency:
:::code-group
```bash [npm]
npm install tevm viem@latest
```
```bash [pnpm]
pnpm add tevm viem@latest
```
```bash [yarn]
yarn add tevm viem@latest
```
```bash [bun]
bun add tevm viem@latest
```
:::
#### Create Your Client
For the quickest start, create a memory client:
```ts
import { createMemoryClient } from "tevm";
const client = createMemoryClient();
```
Or, to fork from an existing chain:
```ts
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/chains";
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
// Wait for the node to be ready before using it
await client.tevmReady();
```
A [`MemoryClient`](https://github.com/evmts/tevm-monorepo/blob/main/packages/memory-client/src/createMemoryClient.js) is a batteries included client that includes all `PublicActions`, `WalletActions`, and `TestActions` from viem. It also includes special tevm specific actions prefixed with `tevm*` such as `tevmCall` and `tevmSetAccount`
#### You're Ready!
Start using your client with familiar viem actions:
```ts
// Get the current block number
const blockNumber = await client.getBlockNumber();
console.log(`Current block: ${blockNumber}`);
```
::::
### Complete Example
The following example demonstrates the key capabilities of Tevm with viem:
:::code-group
```ts [Forking Example] showLineNumbers {1-3,8-12,25-29,40,52}
import { createMemoryClient, http } from "tevm"; // [!code focus]
import { optimism } from "tevm/common"; // [!code focus]
import { parseAbi, parseEther } from "viem"; // [!code focus]
// 1. Create a memory client forked from Optimism mainnet
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"), // [!code focus]
common: optimism, // [!code focus]
}, // [!code focus]
}); // [!code focus]
// Wait for the node to be ready
await client.tevmReady();
// 2. Get current block number (from the fork point)
const blockNumber = await client.getBlockNumber();
console.log(`Current block number: ${blockNumber}`);
// Setup addresses and contract interfaces
const account = `0x${"baD60A7".padStart(40, "0")}` as const;
const greeterContractAddress = "0x10ed0b176048c34d69ffc0712de06CbE95730748";
// Define contract interfaces with parseAbi
const greeterAbi = parseAbi([
"function greet() view returns (string)", // [!code focus]
"function setGreeting(string memory _greeting) public", // [!code focus]
]); // [!code focus]
// 3. Modify blockchain state with test actions
// Fund our test account with 1 ETH
await client.setBalance({
address: account,
value: parseEther("1"),
});
// Read the current greeting using viem's readContract
const currentGreeting = await client.readContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "greet",
});
console.log(`Current greeting: ${currentGreeting}`);
// Update the greeting with writeContract
const txHash = await client.writeContract({
// [!code focus]
account,
address: greeterContractAddress,
abi: greeterAbi,
functionName: "setGreeting",
args: ["Hello from Tevm!"],
chain: optimism,
});
console.log(`Transaction sent: ${txHash}`);
// 4. Mine a new block to include our transaction
await client.mine({ blocks: 1 }); // [!code focus]
// Verify the greeting was updated
const updatedGreeting = await client.readContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "greet",
});
console.log(`Updated greeting: ${updatedGreeting}`);
```
```ts [Contract Deployment] showLineNumbers
import { createMemoryClient } from "tevm";
import { parseAbi, parseEther, encodeAbiParameters } from "viem";
// Create a memory client (isolated environment)
const client = createMemoryClient();
// 1. Create a test account with funds
const deployerAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
await client.setBalance({
address: deployerAddress,
value: parseEther("10"),
});
// 2. Define a simple contract
const counterAbi = parseAbi([
"function increment() public",
"function count() view returns (uint256)",
]);
// Counter contract bytecode (pre-compiled)
const counterBytecode =
"0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5fb46adf6ce0cfd90fa4324ffd8c48b0fc6fb6c4cac9ca2c69c97e25f355c9d64736f6c63430008110033";
// 3. Deploy the contract
const deployHash = await client.sendTransaction({
account: deployerAddress,
to: undefined, // Contract creation
data: counterBytecode,
});
// Mine to include the deployment
await client.mine();
// 4. Get the deployment receipt to find contract address
const receipt = await client.getTransactionReceipt({
hash: deployHash,
});
const contractAddress = receipt.contractAddress;
console.log(`Contract deployed at: ${contractAddress}`);
// 5. Use the contract
const initialCount = await client.readContract({
address: contractAddress,
abi: counterAbi,
functionName: "count",
});
console.log(`Initial count: ${initialCount}`);
// Increment the counter
await client.writeContract({
account: deployerAddress,
address: contractAddress,
abi: counterAbi,
functionName: "increment",
});
// Mine the transaction
await client.mine();
// Read the new count
const newCount = await client.readContract({
address: contractAddress,
abi: counterAbi,
functionName: "count",
});
console.log(`New count: ${newCount}`);
```
:::
:::details[Code Walkthrough]
#### 1. Imports & Client Creation
```ts
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/common";
import { parseAbi, parseEther } from "viem";
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
await client.tevmReady();
```
* We create a client that **forks** from Optimism mainnet
* This gives us a local sandbox with all of mainnet's state
* `client.tevmReady()` ensures the fork is complete before proceeding
#### 2. Contract Interaction
```ts
// Define the contract interface
const greeterAbi = parseAbi([
"function greet() view returns (string)",
"function setGreeting(string memory _greeting) public",
]);
// Read from contract
const currentGreeting = await client.readContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "greet",
});
// Write to contract
const txHash = await client.writeContract({
account,
address: greeterContractAddress,
abi: greeterAbi,
functionName: "setGreeting",
args: ["Hello from Tevm!"],
chain: optimism,
});
```
* The API matches viem exactly - anyone familiar with viem can use this immediately
* Write operations return a transaction hash just like on a real network
#### 3. Mining Control
```ts
// Mine a block to include our transaction
await client.mine({ blocks: 1 });
```
* Unlike real networks, you control exactly when blocks are mined
* This gives you complete determinism for testing and development
:::
### Key Viem-Compatible Features
Tevm's viem client implements the full viem API, maintaining compatibility while adding powerful features:
:::code-group
```ts [Standard viem API]
// These standard viem actions work exactly as expected
await client.getBalance({ address: '0x...' })
await client.getBlockNumber()
await client.readContract({ ... })
await client.writeContract({ ... })
await client.estimateGas({ ... })
await client.sendTransaction({ ... })
// And all other viem actions
```
```ts [Tevm Extensions]
// Tevm adds powerful extensions
await client.tevmReady(); // Wait for fork initialization
await client.setBalance({ address: "0x...", value: 100n });
await client.setCode({ address: "0x...", bytecode: "0x..." });
await client.mine({ blocks: 1 });
await client.reset(); // Reset to initial state
await client.impersonateAccount({ address: "0x..." });
```
```ts [EVM Debugging]
// Advanced EVM debugging and inspection
await client.tevmContract({
address: '0x...',
abi: [...],
functionName: 'transfer',
args: ['0x...', 100n],
// Get step-by-step EVM execution details
onStep: (data, next) => {
console.log(`Opcode: ${data.opcode.name}`)
console.log(`Stack: ${data.stack.join(', ')}`)
next() // Continue execution
}
})
```
:::
### Common patterns and Best Practices
#### Creating multiple clients
It is common to create a viem client and a tevm client side by side.
```typescript
import { createPublicClient, http } from "viem";
import { createMemoryClient } from "tevm";
import { optimism } from "tevm/common";
export const publicClient = createPublicClient({
transport: http("https://mainnet.optimism.io"),
});
export const memoryClient = createMemoryClient({
fork: {
// use your public client as the fork transport
transport: publicClient,
// (comming soon)
rebase: true,
},
});
```
* Generally you will still want to be using normal viem clients while building Tevm applications
* As a best practice use your viem client as the transport so any caching viem does is shared with Tevm
* tevm/common is a superset of a viem chain so it can be used for both
#### Racing JSON-RPC requests
When doing this a pattern you can do to improve the performance of your app is what is called `racing`. This is when you execute a call with `tevm` and `viem` and return the one that returns first.
```typescript
function raceExample() {
const {resolve, reject, promise} = Promise.withResolvers()
// estimateGas with both viem and tevm in parallel resolving the one that finishes first
publicClient.estimateGas(...).then(result => resolve(result))
memoryClient.estimateGas(...).then(result => resolve(result))
return promise
}
```
If the Tevm cache is warm it will finish much faster (nearly instantly) than the remote RPC call will. If the cache is cold the remote rpc call will finish first while Tevm warms the cache in background for next time. Racing allows you to improve the performance of your app.
#### Using the Tevm Bundler
The Tevm Bundler is an optional tool for importing contract abis into TypeScript and it is built for Wagmi, Viem, Ethers and Tevm. Users have reported using it with other tools like `Ponder` as well.
It is common to use the Tevm Bundler even when not using TevmNode as a TevmContract is a library agnostic typesafe instance representing a contract abi.
```typescript
import { MyContract } from "./MyContract.sol";
function useExample() {
return useReadContract({
abi: MyContract.abi,
address: `0x...`,
method: "balanceOf",
args: address,
});
// Alternatively use the typesafe `read.method()` api
return useReadContract(
MyContract.withAddress(`0x...`).read.balanceOf(address),
);
}
```
### Tree-Shakeable API
For production applications, especially in browser environments, you may want to use Tevm's tree-shakeable API to minimize bundle size:
```ts showLineNumbers {1-3,6-11}
import { createClient, http } from "viem";
import { createTevmTransport } from "tevm/transport"; // [!code focus]
import { tevmCall, tevmDumpState } from "tevm/actions"; // [!code focus]
// Create a standard viem client with Tevm transport
const client = createClient({
transport: createTevmTransport({
// [!code focus]
fork: {
// [!code focus]
transport: http("https://mainnet.optimism.io"), // [!code focus]
}, // [!code focus]
}), // [!code focus]
});
// Import only the actions you need
await tevmDumpState(client);
```
To do this you use `createTevmTransport` which takes the same options as a memoryClient but unlike a MemoryClient only supports a `client.request` method.
You should ALWAYS use `createTevmTransport` rather than passing a TevmClient directly in as transport using `custom(TevmNode)`.
:::tip[Tree-Shaking Benefit]
Using the tree-shakeable API can significantly reduce your bundle size when you're using only a subset of Tevm's features.
:::
### Using viem to talk to Tevm over http
By default Tevm runs in memory but it does support running as a traditional http server as well. This can be useful if using Tevm as an anvil-like testing tool.
There are two ways to run tevm as a sever. THe easiest way is using the CLI
```bash
npx tevm serve --fork-url https://mainnet.optimism.io
```
Or you can run Tevm as a Http, Express, Hono, or Next.js server directly in node.js and Bun
```typescript
import { createMemoryClient, http } from "tevm";
import { createServer } from "tevm/server";
const memoryClient = createMemoryClient();
const server = createServer(memoryClient);
server.listen(8545, () => {
console.log("server started on port 8545");
// test a request vs server
http("http://localhost:8545")({})
.request({
method: "eth_blockNumber",
})
.then(console.log)
.catch(console.error);
});
```
Once you start Tevm as a server you can talk to it using viem `http` as normal.
### Tevm-Specific Actions
Tevm extends viem with specialized actions that provide enhanced capabilities:
| Action | Description | Use Case |
| ---------------- | -------------------------------------------- | --------------------------------------- |
| `tevmCall` | Low-level EVM call with execution hooks | Deep inspection of contract execution |
| `tevmContract` | Enhanced contract interaction with EVM hooks | Detailed debugging of contract calls |
| `tevmDeploy` | Deploy with execution hooks | Understanding deployment execution flow |
| `tevmMine` | Control block mining | Precise transaction inclusion control |
| `tevmSetAccount` | Modify account state | Test different account scenarios |
| `tevmGetAccount` | Read detailed account state | Inspect nonce, code, storage |
| `tevmDumpState` | Export full EVM state | State persistence and analysis |
| `tevmLoadState` | Import saved EVM state | Restore a specific state for testing |
| `tevmReady` | Wait for fork to initialize | Ensure node is ready before use |
:::info
All tevm-specific actions are also available as individual imports from `tevm/actions` for tree-shaking.
:::
### Hook into the EVM
One of Tevm's most powerful features is the ability to hook directly into EVM execution using the `tevmCall` and `tevmContract` actions:
```ts showLineNumbers {7-16}
await client.tevmContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "setGreeting",
args: ["Hello!"],
// onStep is called for each EVM operation // [!code focus]
onStep: (stepInfo, next) => {
// [!code focus]
console.log(`Executing: ${stepInfo.opcode.name} at PC=${stepInfo.pc}`); // [!code focus]
console.log(`Stack: ${stepInfo.stack.map((val) => val.toString())}`); // [!code focus]
console.log(`Memory: ${stepInfo.memory.toString("hex")}`); // [!code focus]
// You can also modify EVM state here if needed // [!code focus]
// Call next() to continue execution // [!code focus]
next?.(); // [!code focus]
}, // [!code focus]
// You can also access the detailed result after execution
onResult: (result) => {
console.log(`Gas used: ${result.executionGasUsed}`);
console.log(`Return value: 0x${result.returnValue?.toString("hex")}`);
},
});
```
This enables advanced use cases like:
* **Visual Debuggers**: Create step-by-step transaction debuggers
* **Educational Tools**: Explain EVM execution for learning purposes
* **Custom Instrumentation**: Profile and analyze contract execution
* **Intercepting Execution**: Modify execution behavior for testing
### Next Steps
Now that you're familiar with using Tevm with viem, you can:
:::steps
#### Explore More Tevm Features
Dive deeper into Tevm's powerful capabilities:
* [Forking capabilities](/core/forking) to simulate production chains
* [State management](/core/managing-state) for manipulating the blockchain
* [Mining modes](/core/mining-modes) for controlling transaction inclusion
* [Direct Solidity imports](/getting-started/bundler) with the Tevm Bundler
#### Check Out Examples
See how Tevm solves real-world problems:
* [Forking mainnet](/examples/forking-mainnet) for production simulation
* [Building a debugger UI](/examples/debugger-ui) with EVM insights
* [Local testing flows](/examples/local-testing) for development
#### Advanced API Usage
Master the Tevm API for more sophisticated applications:
* [EVM events & hooks](/api/evm-events) for detailed execution analysis
* [Custom precompiles](/advanced/custom-precompiles) for extending the EVM
* [Transaction pool management](/advanced/txpool) for pending transaction control
* [Contract Bundler](/reference/bundler) for importing Solidity files directly
:::
import { Callout, Steps, Button } from "vocs/components";
import { TabGroup, FileTree, Card } from "../../components";
## Tevm Architecture Overview
This page covers the internal architecture of Tevm and is intended for
advanced users, contributors, or those looking to understand how Tevm works
under the hood. If you're just getting started, we recommend the [viem API
guide](../getting-started/viem) instead.
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](https://viem.sh/), enables tree-shaking and a composable API. This pattern was originally created by [`viem`](https://viem.sh) and Tevm follows it even for it's internals to be maximally compatable with Viem.
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
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:
```ts showLineNumbers {1-2,5,8,11} filename="action-example.ts"
import { createTevmNode } from "tevm";
import { getAccountHandler } from "tevm/actions"; // [!code focus]
// 1. Create the node object
const node = createTevmNode(); // [!code focus]
// 2. Create a handler function by passing the node to the action
const getAccount = getAccountHandler(node); // [!code focus]
// 3. Use the handler function with specific parameters
const account = await getAccount({
// [!code focus]
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:
```ts showLineNumbers {1,4-8,11-13,16-19} filename="memory-client.ts"
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({ // [!code focus]
address: '0x1234567890123456789012345678901234567890' // [!code focus]
}) // [!code focus]
// Use Tevm-specific actions (prefixed with 'tevm')
const state = await client.tevmDumpState() // [!code focus]
const balance = await client.getBalance({ // [!code focus]
address: '0x1234567890123456789012345678901234567890' // [!code focus]
}) // [!code focus]
```
The MemoryClient approach is great for quick prototyping and applications where bundle size isn't critical.
```ts showLineNumbers {1-4,7-12,15-16,19-22} filename="tree-shakable.ts"
import { createClient, http } from 'viem'
import { createTevmTransport } from 'tevm/transport'
import { getBlock, getBlockNumber } from 'viem/actions' // [!code focus]
import { tevmSetAccount, tevmMine } from 'tevm/actions' // [!code focus]
// 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) // [!code focus]
const block = await getBlock(client) // [!code focus]
// Tevm-specific actions
await tevmSetAccount(client, { // [!code focus]
address: '0x1234...', // [!code focus]
balance: 1000000000000000000n // [!code focus]
}) // [!code focus]
await tevmMine(client, { blocks: 1 }) // [!code focus]
```
This approach is ideal for production applications and browser environments where bundle size matters.
โ Easy to get started
โ All methods available immediately
โ Less code to write
โ Larger bundle size
โ 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](https://wagmi.sh/react/guides/viem).
### Core Architecture Components
Tevm's modular architecture comprises several key components that work together to provide a complete Ethereum execution environment:
:::steps
#### 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.
```ts
// 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.
```ts
// 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.
```ts
// 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.
```ts
// 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
Preview transaction outcomes before sending to mainnet
Step through transactions with full state visibility
Build apps that work offline with optimistic updates
Create interactive EVM learning experiences
Test smart contracts in continuous integration pipelines
Analyze contract gas usage patterns with precision
Run Ethereum nodes in serverless or edge computing environments
Create, save, and restore blockchain state at precise points
For detailed examples of these use cases, see the [examples section](../examples/viem.mdx).
### API Interfaces
Tevm provides multiple API layers to suit different needs and programming
styles.
| 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 |
```ts showLineNumbers {1,3,6-7,10-13} filename="client-api.ts"
import { createMemoryClient } from 'tevm'
const client = createMemoryClient()
// Standard viem actions
const balance = await client.getBalance({ address: '0x123...' }) // [!code focus]
const blockNumber = await client.getBlockNumber() // [!code focus]
// Tevm-specific actions
await client.tevmSetAccount({ // [!code focus]
address: '0x123...', // [!code focus]
balance: 1000000000000000000n // [!code focus]
}) // [!code focus]
await client.tevmMine()
```
This is the most developer-friendly API, perfect for most application development.
```ts showLineNumbers {1-2,4-5,8-12,15-22,25} filename="low-level-api.ts"
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) => { // [!code focus]
// Inspect execution at each EVM step // [!code focus]
console.log(data.opcode.name, data.stack) // [!code focus]
next?.() // [!code focus]
}) // [!code focus]
// Direct state management
await vm.stateManager.putAccount( // [!code focus]
createAddress('0x123...'), // [!code focus]
{ // [!code focus]
nonce: 0n, // [!code focus]
balance: 10000000000000000000n, // [!code focus]
storageRoot: '0x...', // [!code focus]
codeHash: '0x...' // [!code focus]
} // [!code focus]
) // [!code focus]
// Direct blockchain control
const block = await vm.blockchain.getBlockByNumber(1n)
```
This API provides maximum control but requires deeper understanding of EVM internals.
For component API details, see:
* [State Manager](/reference/state)
* [Transaction Pool](/reference/txpool)
* [Blockchain](/reference/blockchain)
* [EVM](/reference/evm)
* [Receipt Manager](/reference/receipt-manager)
### Advanced Features
Tevm includes several powerful features that enable advanced use cases:
:::steps
#### Contract Utilities
Tevm's Type-safe contract interactions with TypeScript support are actually Tevm agnostic. They are built to work with any library including Viem Ethers and Wagmi. It is possible we push to upstream this abstraction to Viem in future.
```ts
import { createContract } from "tevm/contract";
// Define a contract interface
const erc20Contract = createContract({
abi: [
"function balanceOf(address owner) view returns (uint256)",
"function transfer(address to, uint256 amount) returns (bool)",
"event Transfer(address indexed from, address indexed to, uint256 value)",
],
address: "0x123...",
});
// Type-safe read and write operations
const balance = await erc20Contract.read.balanceOf("0x456...");
const txHash = await erc20Contract.write.transfer("0x789...", 1000n);
```
:::
For Tevm Bundler users, directly import Solidity with full type safety:
```ts
// 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
:::details[Node Extension API]
Tevm's plugin system allows adding new functionality to nodes:
```ts
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
| Resource | Description |
| ----------------------------------------------------------- | -------------------------------------------------- |
| [TevmNode Interface Reference](/reference/node) | Detailed API reference for the core node interface |
| [GitHub Repository](https://github.com/evmts/tevm-monorepo) | Source code and contributions |
| [Custom Precompiles Guide](../advanced/custom-precompiles) | Learn how to extend the EVM |
| [Performance Profiling](../advanced/performance-profiler) | Optimize your Ethereum applications |
import { Callout, Steps, Button } from "vocs/components";
### What is Tevm Node?
:::tip[Ethereum in JavaScript]
Tevm Node is a complete Ethereum execution environment implemented entirely in JavaScript, providing full EVM functionality without any native dependencies. It brings the entire Ethereum stack to any JavaScript environment.
:::
:::tip[Ethereum in JavaScript]
Tevm Node also includes powerful buildtime tooling for importing evm contracts into TypeScript
:::
Tevm Node is a **full Ethereum execution environment** that runs natively in JavaScript. It provides the complete functionality of an Ethereum node without requiring any blockchain client installation.
Conceptually, it works very similar to Anvil and Hardhat but with more powerful TypeScript native interop.
:::tip[You might already know most of the Tevm API!]
If you already know how to use `viem` or `ethers`, you can start using Tevm Node right away with your existing knowledge! Tevm extends your library of choice.
:::
### What Makes Tevm Unique?
Unlike traditional development environments like [Hardhat](https://hardhat.org/) or [Anvil](https://book.getfoundry.sh/anvil/), Tevm Node offers several distinct advantages:
* **Cross-Platform Compatibility** โ The same code works everywhere JavaScript runs, with zero native dependencies including your applications in the browser
* **Fine-grained EVM Control** โ Access the execution environment at any level of detail, from high-level transactions to individual EVM opcodes
* **Enhanced User Experience** โ Enable advanced features like instantaneous gas estimation, optimistic UI updates, and sophisticated transaction simulation
* **Type-safe Interactions** โ Full TypeScript support throughout the entire API surface powered by `abitype`
* **Direct Solidity Imports** โ The powerful ability to write solidity code that interops with your TypeScript code in a typesafe way via the [Tevm Bundler](/getting-started/bundler)
### Universal JavaScript Compatibility
Tevm's key innovation is bringing the Ethereum execution environment to **every JavaScript runtime**:
Node.js
For local development, testing, and CI/CD pipelines
Browser
For advanced dApps with offline capability and real-time simulation
Any JS Runtime
Works in Deno, Bun, Edge Functions, or any modern JavaScript environment
### Integration With Popular Libraries
Tevm works with the libraries you already know and love including `viem` `wagmi` and `ethers`:
:::code-group
```ts [viem]
import { createMemoryClient, http } from "tevm";
const client = createMemoryClient();
// Use standard viem actions
const balance = await client.getBalance({ address: "0x..." });
const blockNumber = await client.getBlockNumber();
// Plus Tevm-specific actions
await client.tevmMine({ blocks: 1 });
await client.tevmSetAccount({
address: "0x...",
balance: 100000000000000000n, // 0.1 ETH
});
```
```ts [ethers]
import { createMemoryClient } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { BrowserProvider, Wallet } from "ethers";
// Create client and enable EIP-1193 provider interface
const client = createMemoryClient();
client.transport.tevm.extend(requestEip1193());
// Use with ethers.js
const provider = new BrowserProvider(client.transport.tevm);
const signer = Wallet.createRandom().connect(provider);
// Read chain data
const blockNumber = await provider.getBlockNumber();
// Don't forget to mine after transactions
await client.mine({ blocks: 1 });
```
:::
### How Tevm Compares
| Feature | Tevm | Anvil | Hardhat | Ganache |
| --------------------------------- | --------------- | -------- | --------------- | ---------- |
| **Language** | JavaScript/Wasm | Rust | JavaScript/Rust | JavaScript |
| **Browser Compatible** | โ | โ | โ | โ |
| **Minimal Dependencies** | โ | โ | โ | โ |
| **Mainnet Forking** | โ | โ | โ | โ |
| **EVM Event Hooks** | โ | โ | โ | โ |
| **Custom Precompiles** | โ | โ | โ | โ |
| **viem Integration** | Native | Some | Minimal | Minimal |
| **ethers Integration** | Native | Minimal | Minimal | Some |
| **Debugging** | Advanced | Advanced | Advanced | Basic |
| **TypeScript Support** | Full | Limited | Full | Full |
| **Serverless Compatible** | โ | โ | โ | โ |
| **Optimized forking performance** | โ | โ | โ | โ |
### Library Compatibility
| Library | Support Level | Notes |
| ------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [**viem**](../getting-started/viem.mdx) | Native | First-class native integration with all viem features |
| [**ethers.js**](../getting-started/ethers.mdx) | Full | Both v5 and v6 via EIP-1193 provider |
| [**web3.js**](https://github.com/web3/web3.js) | Full | Via EIP-1193 provider |
| [**wagmi**](https://wagmi.sh/) | Full | Works as a wagmi connector |
| [**thirdweb**](https://thirdweb.com/) | Full | Compatible with thirdweb's SDK |
| [**Ponder**](https://ponder.sh/) | Full | Can be used to do advanced tracing in ponder handlers and import contracts into ponder config |
| [**ethereumjs**](https://github.com/ethereumjs/ethereumjs-monorepo) | Native | Implements the same interfaces as Ethereumjs. Ethereumjs users can take advantage of Tevm StateManager to get Tevm like features such as forking in ethereumjs |
| Any EIP-1193 library | Full | Standard provider interface |
### Next Steps
import { Callout, Steps, Button } from "vocs/components";
import { Card, TabGroup, FileTree } from "../../components";
## Why Run An Ethereum Node in JavaScript?
> You know what would make solving all these problems trivially easy? If we just were able to use Foundry in the browser
This is what [Fucory](https://x.com/fucory) thought the day he started building Tevm. Fucory created Tevm to solve all UX and Devx issues blocking the delivery of great blockchain apps. The original idea was to put the foundry API in the browser and the library evolved from there. Because of Tevm's wide ambitious scope Tevm's use case is simply TypeScript+EVM. If you are building on the EVM and you are using TypeScript Tevm will help you. And here is why.
> We believe every TypeScript user of the EVM who installs Viem will also install Tevm alongside it in the future
๐ Enhanced Performance
Execute transactions locally with near-zero latency for gas estimation,
transaction simulation, and debugging.
๐ป Browser Compatibility
Enable sophisticated dApp features like offline capabilities, optimistic UI
updates, and real-time simulations.
๐ Debug Superpowers
Step through EVM execution opcode by opcode to understand exactly what's
happening in your smart contracts.
๐ ๏ธ Familiar Developer Experience
Works seamlessly with the libraries you already know - viem, ethers, or
any EIP-1193 compatible tool.
:::info[Did you know?]
Tevm Node is part of a larger ecosystem that also includes [Tevm Bundler](https://tevm.sh/bundler), which allows for direct Solidity imports into JavaScript/TypeScript.
:::
### Performance & Efficiency
Tevm is benchmarked to perform better than even Anvil at executing calls in `forked` mode. This is possible via more efficient storage slot retrieval.
Running the EVM locally eliminates round-trip delays to remote nodes,
enabling near-instantaneous transaction simulations and gas estimations.
Imagine not having to show a user a loading spinner when estimating gas
while saving on RPC credits.
Simulate multiple transactions plugging directly into the evm with JS or
even writing custom contracts in JS.
### Optimistic updates
Oftentimes you want to show the user what the state of their account is expected to be. Possibly we want to show them the expected state changes from their transaction or series of transactions before they submit. Or maybe we are trying to build snappy UI so we want to show them the updated state right away with a pending icon. Tevm makes implementing optimistic state simple.
```typescript
// create a client in rebase mode so it updates optimistic state as new blocks come in
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
rebase: true,
},
common: optimism,
});
// When we send a transaction to the network send it to Tevm too
// We do not mine the transaction as we want it to just be in our mempool
const txHash = await client.sendRawTransaction(tx);
client.waitForTransactionReceipt({ hash: txHash }).then(() => {
// remove the tx from optimistic state after it is included in chain
const mempool = await client.transport.tevm.getTxPool();
await mempool.removeTxByHash(txHash);
});
// continue to query the latest state by default
await client.getBalance({ address, blockTag: "latest" });
// or query optimistic state with 'pending' block tag
await client.getBalance({ address, blockTag: "pending" });
```
#### Real-World Performance Benefits
The `estimate_gas` example is the easiest example to illustrate this.
Tevm's local execution provides instantanious gas estimation!
```typescript showLineNumbers {1-4,8-11} filename="performance-comparison.ts"
const gasEstimate0 = await client.estimateGas({ ... }) // ~200ms as it fetches state (unless you prewarmed the cache)
const gasEstimate0 = await client.estimateGas({ ... }) // ~Instant on future estimations with cache saved
const gasEstimate0 = await client.estimateGas({ ... }) // ~Instant on future estimations with cache saved
```
Note: because Tevm can plug directly into wagmi this works as well via `useGasEstimate`
### Enhanced User Experiences
JavaScript-based EVM execution enables entirely new categories of dApp features:
Tevm gives you complete control including it's deep internals in a way no other node does. Almost all use cases can be supported.
Show users the likely outcome of transactions before they're mined on-chain.
Tevm has near 100% test coverage and a history of fixing most reported bugs
in under 24 hours.
Simulate complex interactions and preview results before sending
transactions.
Process sensitive data locally without sending it to external services.
### Top Tier Devx
#### Use the tools you already know
Tevm users report the library having great devx. A lot of this is owed to it's abilility to plug directly into the tools you are already using like `wagmi`, `viem`, and `ethers`.
#### Interop with contracts effortlessly
We cannot talk about developer experience in Tevm without bringing up the `Tevm Bundler`.
The Tevm Bundler is an optional addon to Tevm that makes TypeScript aware of how to process and compile Solidity. It is a tool in the same category as the `Wagmi CLI` or `Typechain` but even more powerful and ergonomic.
The devx using the bundler is optimized in following ways:
* Natspec on hover
* Typesafe contract
* TRPC like experience. You will see red underlines before you even save a solidity file
* No need to set up external build tools. Plugs directly into your existing js pipeline
* Supports reading foundry config for remappings and lib
##### Import solidity directly
The Tevm Bundler doesn't require compiling your contracts as it has a compiler built in. You can import solidity directly.
```typescript
import { MyContract } from "./MyContract.sol";
console.log(MyContract.abi);
```
You can also import from node\_modules or foundry projects. Tevm supports remappings, lib, and other advanced options. Unlike Foundry Tevm supports node\_module resolution by default.
##### Use contracts via address
If you know your contract address and it's already deployed you don't need to manually specify anything. You can simply reference it by address and Tevm will pull the ABI at build time. This even works for unverified contracts.
```typescript
// create a macro file for your contracts
import { client } from "./clients.js";
export const MyContract = await client.whatsabi(`0x...`);
```
```typescript
// import your macro using tevm and Tevm will fetch your contracts at buildtime
import {MyContract} from './MyContract.js' as {type: 'tevm'}
```
#### Low level control of the EVM
Most tools like `anvil` run in a seperate process and you communicate over HTTP. Tevm runs in memory and gives you access directly to the node. This allows for powerful programmability not possible with any other tool.
##### Run callbacks on every EVM step ๐ฌ
Step through EVM execution opcode by opcode, inspect memory and stack, and see exactly what happens in your contracts.
```typescript filename="debug-example.ts"
// Listen to every EVM instruction
vm.evm.events.on("step", (data, next) => {
console.log(
`${data.pc.toString().padStart(5)}:`,
`${data.opcode.name.padEnd(10)}`,
`gas: ${data.gasLeft.toString().padStart(8)}`,
`stack: ${data.stack.join(", ")}`,
);
next();
});
```
You can even modify what the EVM is doing as it executes if you choose to.
##### Mock EVM contracts with JavaScript contracts
Tevm offers the advanced ability to write a contract in JavaScript which can be powerful in advanced use cases.
Precompiles are similar to foundry cheat codes but rather than a standard library of cheat codes Tevm lets you write arbitrary JavaScript to do whatever you need to do.
It works very nicely with the Tevm Bundler
```typescript
import {
defineCall,
definePrecompile,
createContract,
createMemoryClient,
} from "tevm";
import { readFile } from "fs/promises";
const contract = createContract({
address: `0x${"1234".repeat(10)}`,
humanReadableAbi: ["function readFile(string fileName) returns string"],
});
const { precompile } = definePrecompile({
contract,
call: defineCall(contract.abi, {
readFile: ({ args }) => {
return {
data: await readFile(args.fileName, "utf8"),
gasUsed: 0n,
};
},
}),
});
// then add your precompile to the evm
const memoryClient = createMemoryClient({
precompiles: [precompile()],
});
```
#### Deterministic Testing ๐งช
While tevm is primarily built for applicaiton development it just so happens to be great at testing too.
Create fully reproducible environments for testing with complete control over blockchain state, time, and mining.
### Solidity Imports
Tevm Bundler (optional feature) creates the best devx for working with solidity files in TypeScript
```typescript
// Import solidity files directly into TS files
import { MyContract } from "./MyContract.sol";
```
### JavaScript Ecosystem Integration
Running Ethereum in JavaScript means you can leverage the entire JavaScript
ecosystem effortlessly.
Type-safe contract interactions with full IntelliSense support
React, Vue, Svelte and other frontend libraries
Vite, Webpack, ESBuild and other bundlers
Vitest support via Vite
Node.js, browsers, Electron, serverless functions
Access to millions of packages and libraries in the npm registry
Integration with browser storage, WebSockets, service workers, and more
### Ready to Get Started?
## @tevm/actions
The `@tevm/actions` package provides a comprehensive set of actions for interacting with the Tevm client. It includes both standard Ethereum JSON-RPC methods and Tevm-specific functionality.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/actions/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) folder.
### Installation
```bash
npm install @tevm/actions
```
### Overview
The `@tevm/actions` package provides handlers for:
* Executing EVM calls and contract interactions
* Managing blockchain state and accounts
* Standard Ethereum JSON-RPC methods
* Testing and development utilities (Anvil-compatible)
* Debugging and tracing functionality
### API Reference
#### Error Classes
* [BlobGasLimitExceededError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/BlobGasLimitExceededError.md) - Error thrown when blob gas limit is exceeded
* [MissingAccountError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/MissingAccountError.md) - Error thrown when an account doesn't exist
* [NoForkUrlSetError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/NoForkUrlSetError.md) - Error thrown when fork URL is required but not set
#### Core Types
* [Address](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Address.md) - Ethereum address type
* [Abi](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Abi.md) - Contract ABI type
* [Block](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Block.md) - Ethereum block type
* [BlockTag](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/BlockTag.md) - Block reference tag (latest, earliest, etc)
#### Base Actions
##### Call Actions
* [CallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallHandler.md) - Handler for executing EVM calls
* [CallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallParams.md) - Parameters for call operations
* [CallResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallResult.md) - Result of call execution
* [BaseCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/BaseCallParams.md) - Common parameters for all call operations
##### Contract Actions
* [ContractHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractHandler.md) - Handler for contract interactions
* [ContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractParams.md) - Parameters for contract calls
* [ContractResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractResult.md) - Result of contract execution
##### Deploy Actions
* [DeployHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployHandler.md) - Handler for contract deployment
* [DeployParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployParams.md) - Parameters for deployment
* [DeployResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployResult.md) - Result of deployment
#### Validation Functions
* [validateBaseCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateBaseCallParams.md) - Validate base call parameters
* [validateCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateCallParams.md) - Validate call parameters
* [validateContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateContractParams.md) - Validate contract parameters
* [validateGetAccountParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateGetAccountParams.md) - Validate get account parameters
* [validateLoadStateParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateLoadStateParams.md) - Validate load state parameters
* [validateMineParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateMineParams.md) - Validate mine parameters
* [validateSetAccountParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateSetAccountParams.md) - Validate set account parameters
#### JSON-RPC Procedures
* [anvilImpersonateAccountJsonRpcProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/anvilImpersonateAccountJsonRpcProcedure.md) - Impersonate account procedure
* [callProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/callProcedure.md) - Call procedure
* [getAccountProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/getAccountProcedure.md) - Get account procedure
* [mineProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/mineProcedure.md) - Mine procedure
* [requestProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/requestProcedure.md) - Request procedure
* [requestBulkProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/requestBulkProcedure.md) - Bulk request procedure
#### Internal Utilities
* [forkAndCacheBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/forkAndCacheBlock.md) - Fork and cache block utility
* [handlePendingTransactionsWarning](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/handlePendingTransactionsWarning.md) - Handle pending transactions warning
* [shouldCreateTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/shouldCreateTransaction.md) - Check if transaction should be created
#### Ethereum JSON-RPC Actions
##### Account & Network
* [EthAccountsHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthAccountsHandler.md) - List available accounts
* [EthChainIdHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthChainIdHandler.md) - Get current chain ID
* [EthCoinbaseHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthCoinbaseHandler.md) - Get coinbase address
* [EthGasPriceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGasPriceHandler.md) - Get current gas price
* [EthBlockNumberHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthBlockNumberHandler.md) - Get current block number
##### State Reading
* [EthGetBalanceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBalanceHandler.md) - Get account balance
* [EthGetCodeHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetCodeHandler.md) - Get contract code
* [EthGetStorageAtHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetStorageAtHandler.md) - Get storage at position
* [EthCallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthCallHandler.md) - Execute call without state changes
##### Block Operations
* [EthGetBlockByHashHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBlockByHashHandler.md) - Get block by hash
* [EthGetBlockByNumberHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBlockByNumberHandler.md) - Get block by number
#### Anvil (Testing & Development) Actions
##### State Manipulation
* [AnvilSetBalanceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetBalanceHandler.md) - Set account balance
* [AnvilSetCodeHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetCodeHandler.md) - Set contract code
* [AnvilSetNonceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetNonceHandler.md) - Set account nonce
* [AnvilSetStorageAtHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetStorageAtHandler.md) - Set storage at position
##### Mining & Chain Control
* [AnvilMineHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilMineHandler.md) - Mine blocks
* [AnvilSetChainIdHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetChainIdHandler.md) - Set chain ID
* [AnvilResetHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilResetHandler.md) - Reset to initial state
#### Debug Actions
* [DebugTraceCallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DebugTraceCallHandler.md) - Trace a call execution
* [DebugTraceTransactionHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DebugTraceTransactionHandler.md) - Trace a transaction execution
### Usage Examples
#### Basic Call Example
```typescript
import { createTevmNode } from 'tevm/node'
import { callHandler } from '@tevm/actions'
const client = createTevmNode()
const call = callHandler(client)
const result = await call({
to: '0x123...',
data: '0x456...',
value: 1000n
})
```
#### Contract Interaction Example
```typescript
import { contractHandler } from '@tevm/actions'
const contract = contractHandler(client)
const result = await contract({
to: '0x123...',
abi: [...],
function: 'transfer',
args: ['0x456...', 1000n]
})
```
#### Deployment Example
```typescript
import { deployHandler } from '@tevm/actions'
const deploy = deployHandler(client)
const result = await deploy({
bytecode: '0x...',
abi: [...],
args: ['constructor arg']
})
```
#### JSON-RPC Example
```typescript
import { ethCallHandler } from '@tevm/actions'
const ethCall = ethCallHandler(client)
const result = await ethCall({
to: '0x123...',
data: '0x456...'
})
```
### Error Handling
All actions support a `throwOnFail` parameter to control error handling:
```typescript
const result = await call({
to: '0x123...',
throwOnFail: false // Return errors in result instead of throwing
})
```
### See Also
* [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/)
* [Anvil Documentation](https://book.getfoundry.sh/reference/anvil/)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/globals.md)
## @tevm/address
The `@tevm/address` package provides utilities for handling Ethereum addresses. It wraps the functionality from [`@ethereumjs/utils` address](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/util/src/address.ts) with a Tevm-style API.
### Installation
```bash
npm install @tevm/address
```
### Overview
This package provides a set of utilities for working with Ethereum addresses, including:
* Creating and validating addresses
* Generating contract addresses (both regular and CREATE2)
* Converting between different address formats
* Handling checksummed addresses
### API Reference
#### Classes
##### `Address`
A utility class for Ethereum addresses that extends `EthjsAddress`. It provides a more user-friendly API and ensures checksummed address output.
```typescript
import { createAddress } from '@tevm/address'
// Create from hex string
const address = createAddress(`0x${'00'.repeat(20)}`)
// Create from number or bigint
const address2 = createAddress(0n)
// Create from bytes
const address3 = createAddress(new Uint8Array(20))
// Create from non-hex string
const address4 = createAddress('55'.repeat(20))
```
**Methods:**
* `toString()`: Returns the checksummed address as a string
* `toBytes()`: Returns the address as a Uint8Array
* `equals(address: Address)`: Checks if two addresses are equal
* `isZero()`: Checks if the address is zero
* `isPrecompileOrSystemAddress()`: Checks if the address is a precompile or system address
#### Functions
##### `createAddress(address: string | number | bigint | Address | Uint8Array): Address`
Creates an `Address` instance from various input types.
```typescript
import { createAddress } from '@tevm/address'
// From hex string
const address = createAddress('0x1234...')
// From number
const address2 = createAddress(123)
// From bytes
const address3 = createAddress(new Uint8Array(20))
```
**Parameters:**
* `address`: The input to create an address from (hex string, number, bigint, Address instance, or Uint8Array)
**Returns:**
* An `Address` instance
**Throws:**
* `InvalidAddressError` if the input is not a valid address
##### `createContractAddress(from: Address, nonce: bigint): Address`
Generates an address for a newly created contract using the standard CREATE operation.
```typescript
import { createAddress, createContractAddress } from '@tevm/address'
const from = createAddress('0x1234...')
const nonce = 0n
const contractAddress = createContractAddress(from, nonce)
```
**Parameters:**
* `from`: The address of the account creating the contract
* `nonce`: The nonce of the account creating the contract
**Returns:**
* The generated contract address
**Throws:**
* `InvalidAddressError` if the 'from' parameter is not a valid address
##### `create2ContractAddress(from: Address, salt: string, code: string): Address`
Generates an address for a contract created using CREATE2 (EIP-1014).
```typescript
import { createAddress, create2ContractAddress } from '@tevm/address'
const from = createAddress('0x1234...')
const salt = `0x${'00'.repeat(32)}`
const code = '0x...' // Contract creation code
const contractAddress = create2ContractAddress(from, salt, code)
```
**Parameters:**
* `from`: The address creating the contract
* `salt`: A 32-byte salt value as a hex string
* `code`: The contract creation code
**Returns:**
* The generated contract address
**Throws:**
* `InvalidSaltError` if the salt is not 32 bytes
* `InvalidAddressError` if inputs are invalid
### Error Handling
The package provides specific error types for different validation scenarios:
* `InvalidAddressError`: Thrown when an invalid address is provided
* `InvalidSaltError`: Thrown when an invalid salt is provided for CREATE2
* `Create2ContractAddressError`: Union type of both error types above
### Examples
#### Basic Address Creation and Validation
```typescript
import { createAddress } from '@tevm/address'
// Create an address
const address = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')
// Get checksummed string representation
console.log(address.toString())
// '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
// Check if it's a zero address
console.log(address.isZero())
// false
// Compare addresses
const address2 = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')
console.log(address.equals(address2))
// true
```
#### Contract Address Generation
```typescript
import { createAddress, createContractAddress, create2ContractAddress } from '@tevm/address'
// Generate regular contract address
const from = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')
const nonce = 1n
const contractAddress = createContractAddress(from, nonce)
// Generate CREATE2 contract address
const salt = `0x${'00'.repeat(32)}`
const code = '0x608060405234801561001057600080fd5b506101...' // Contract bytecode
const create2Address = create2ContractAddress(from, salt, code)
```
### See Also
* [Ethereum Address Format (EIP-55)](https://eips.ethereum.org/EIPS/eip-55)
* [CREATE2 Opcode (EIP-1014)](https://eips.ethereum.org/EIPS/eip-1014)
* [@tevm/utils Documentation](https://tevm.sh/reference/tevm/utils/)
## @tevm/block
The `@tevm/block` package provides functionality for working with Ethereum blocks. It includes classes and utilities for handling block headers, block data, and block-related operations.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/block/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs) folder.
### Installation
```bash
npm install @tevm/block
```
### Overview
The `@tevm/block` package provides:
* Block creation and manipulation
* Block header management
* RLP serialization/deserialization
* JSON-RPC block formatting
* Verkle tree support
### API Reference
#### Core Classes
* [Block](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/Block.md) - Main class for Ethereum block operations
* [BlockHeader](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/BlockHeader.md) - Class for managing block headers
* [ClRequest](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/ClRequest.md) - Class for client requests
#### Interfaces
* [BlockData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/BlockData.md) - Block data structure
* [BlockOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/BlockOptions.md) - Options for block creation
* [HeaderData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/HeaderData.md) - Block header data structure
* [JsonBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonBlock.md) - JSON representation of a block
* [JsonHeader](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonHeader.md) - JSON representation of a header
* [JsonRpcBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonRpcBlock.md) - JSON-RPC block format
#### Verkle Tree Types
* [VerkleExecutionWitness](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleExecutionWitness.md) - Verkle execution witness
* [VerkleProof](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleProof.md) - Verkle proof structure
* [VerkleStateDiff](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleStateDiff.md) - Verkle state difference
#### Block Types
* [BlockBodyBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockBodyBytes.md) - Block body byte representation
* [BlockBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockBytes.md) - Full block byte representation
* [BlockHeaderBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockHeaderBytes.md) - Block header byte representation
* [ExecutionPayload](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/ExecutionPayload.md) - Execution payload structure
* [BeaconPayloadJson](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BeaconPayloadJson.md) - Beacon chain payload JSON
#### Utility Functions
* [blockFromRpc](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/blockFromRpc.md) - Create block from RPC response
* [executionPayloadFromBeaconPayload](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/executionPayloadFromBeaconPayload.md) - Convert beacon payload to execution payload
* [getDifficulty](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/getDifficulty.md) - Calculate block difficulty
* [valuesArrayToHeaderData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/valuesArrayToHeaderData.md) - Convert array to header data
### Usage Examples
#### Creating a New Block
```typescript
import { Block } from '@tevm/block'
import { createCommon } from '@tevm/common'
import { mainnet } from 'viem/chains'
// Create a new block
const block = new Block({
common: createCommon({ ...mainnet })
})
```
#### Creating a Block from Data
```typescript
import { Block } from '@tevm/block'
import { createCommon } from '@tevm/common'
import { mainnet } from 'viem/chains'
import { EthjsAddress } from '@tevm/utils'
const common = createCommon({ ...mainnet })
const blockData = {
header: {
parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
uncleHash: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
coinbase: EthjsAddress.fromString('0x0000000000000000000000000000000000000000'),
stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
transactionsTrie: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
receiptTrie: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
difficulty: 0n,
number: 0n,
gasLimit: 30000000n,
gasUsed: 0n,
timestamp: BigInt(Math.floor(Date.now() / 1000)),
extraData: '0x',
mixHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
nonce: '0x0000000000000000',
baseFeePerGas: 1000000000n
}
}
const block = Block.fromBlockData(blockData, { common })
```
#### Working with Block Headers
```typescript
// Get block hash
const hash = block.hash()
// Serialize block
const serialized = block.serialize()
// Convert to JSON
const json = block.toJSON()
```
#### Creating from RLP Data
```typescript
import { Block } from '@tevm/block'
import { createCommon } from '@tevm/common'
import { mainnet } from 'viem/chains'
const common = createCommon({ ...mainnet })
const serializedBlock = new Uint8Array([/* ... */])
const block = Block.fromRLPSerializedBlock(serializedBlock, { common })
```
### See Also
* [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/)
* [Block Structure](https://ethereum.org/en/developers/docs/blocks/)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/globals.md)
## @tevm/blockchain
The `@tevm/blockchain` package provides a custom implementation of the Ethereum blockchain, extending the functionality of `ethereumjs/blockchain`. It's responsible for managing blocks, handling chain reorganizations, and maintaining the blockchain state.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/blockchain/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/blockchain/docs) folder.
### Installation
```bash
npm install @tevm/blockchain
```
### Overview
The blockchain package provides:
* Block management and validation
* Chain reorganization handling
* Support for forking from live networks
* Block iteration and traversal
* Chain state management
### API Reference
#### Core Types
##### `Chain`
The main blockchain interface that provides methods for interacting with the blockchain.
```typescript
import { createChain } from '@tevm/blockchain'
import { createCommon } from '@tevm/common'
const chain = await createChain({
common: createCommon({ /* chain config */ }),
})
```
**Methods:**
* `putBlock(block: Block)`: Adds a block to the blockchain
* `delBlock(blockHash: Uint8Array)`: Deletes a block and its children
* `getBlock(blockId: number | bigint | Uint8Array)`: Gets a block by hash or number
* `getBlockByTag(blockTag: BlockTag)`: Gets a block by tag (latest, earliest, etc.)
* `getCanonicalHeadBlock()`: Gets the latest block in the canonical chain
* `validateHeader(header: BlockHeader)`: Validates a block header
* `iterator(name: string, onBlock: OnBlock)`: Iterates through blocks
* `deepCopy()`: Creates a deep copy of the blockchain
* `shallowCopy()`: Creates a shallow copy sharing state with original
##### `ChainOptions`
Configuration options for creating a blockchain instance.
```typescript
type ChainOptions = {
common: Common // Chain configuration
loggingLevel?: LogLevel // Logging configuration
genesisBlock?: Block // Custom genesis block
genesisStateRoot?: Uint8Array // Custom genesis state
fork?: { // Fork configuration
transport: { request: EIP1193RequestFn }
blockTag?: BlockTag | bigint | `0x${string}`
}
}
```
#### Functions
##### `createChain(options: ChainOptions): Promise`
Creates a new blockchain instance.
```typescript
import { createChain } from '@tevm/blockchain'
import { createCommon } from '@tevm/common'
import { mainnet } from '@tevm/common'
const chain = await createChain({
common: createCommon({ ...mainnet }),
loggingLevel: 'debug',
fork: {
transport: { request: /* EIP-1193 provider */ },
blockTag: 'latest'
}
})
```
##### `createBaseChain(options: ChainOptions): BaseChain`
Creates the internal blockchain implementation used by `createChain`.
#### Block Operations
##### Getting Blocks
```typescript
// Get by number
const block = await chain.getBlock(1234n)
// Get by hash
const block = await chain.getBlock(blockHash)
// Get by tag
const latest = await chain.getBlockByTag('latest')
const pending = await chain.getBlockByTag('pending')
```
##### Adding & Removing Blocks
```typescript
// Add a block
await chain.putBlock(block)
// Delete a block and its children
await chain.delBlock(blockHash)
```
##### Block Validation
```typescript
// Validate a block header
await chain.validateHeader(header)
```
#### Chain Traversal
##### Block Iterator
```typescript
// Iterate through blocks
await chain.iterator('vm', async (block, reorg) => {
// Process each block
console.log(block.header.number)
}, maxBlocks)
```
##### Iterator Head Management
```typescript
// Get iterator head
const head = await chain.getIteratorHead('vm')
// Set iterator head
await chain.setIteratorHead('vm', blockHash)
```
#### Forking
The blockchain can be forked from a live network:
```typescript
import { createChain } from '@tevm/blockchain'
import { http } from 'viem'
const chain = await createChain({
common: createCommon({ /* chain config */ }),
fork: {
transport: {
request: http('https://mainnet.infura.io/v3/YOUR-KEY')
},
blockTag: 'latest' // or specific block number/hash
}
})
```
#### Error Handling
The package throws specific error types for different scenarios:
* `BlockNotFoundError`: When a requested block doesn't exist
* `InvalidBlockError`: When block validation fails
* `InvalidHeaderError`: When header validation fails
* `InvalidChainError`: When chain configuration is invalid
### Examples
#### Basic Chain Management
```typescript
import { createChain } from '@tevm/blockchain'
import { createCommon } from '@tevm/common'
import { Block } from '@tevm/block'
// Create chain
const chain = await createChain({
common: createCommon({ /* chain config */ })
})
// Add blocks
await chain.putBlock(block1)
await chain.putBlock(block2)
// Get latest block
const head = await chain.getCanonicalHeadBlock()
// Validate headers
await chain.validateHeader(newBlock.header)
```
#### Chain Forking
```typescript
import { createChain } from '@tevm/blockchain'
import { http } from 'viem'
const chain = await createChain({
common: createCommon({ /* chain config */ }),
fork: {
transport: {
request: http('https://mainnet.infura.io/v3/YOUR-KEY')
},
blockTag: 15000000n // Fork from specific block
}
})
// Work with forked chain
const block = await chain.getBlock(15000000n)
```
#### Chain Iteration
```typescript
import { createChain } from '@tevm/blockchain'
const chain = await createChain({ /* options */ })
// Process blocks sequentially
await chain.iterator('vm', async (block, reorg) => {
if (reorg) {
console.log('Chain reorganization detected')
}
// Process block
console.log(`Processing block ${block.header.number}`)
}, 1000) // Process up to 1000 blocks
```
### See Also
* [EthereumJS Blockchain Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/blockchain)
* [Block Package Documentation](https://tevm.sh/reference/tevm/block/)
* [Common Package Documentation](https://tevm.sh/reference/tevm/common/)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/blockchain/docs/globals.md)
## Bundler Plugins
Tevm provides optional integration with popular JavaScript bundlers through a suite of plugins. These plugins enable direct importing of Solidity files into your TypeScript/JavaScript code, automatically compiling those contracts into type-safe Tevm contract instances.
:::note
Tevm Node is the primary product. Using the bundler is optional. You can also generate contract types with `npx tevm gen` (see [Codegen Approach](#codegen-approach)).
:::
### Available Plugins
Tevm provides plugins for most popular JavaScript bundlers:
| Bundler | Plugin Import Path | Repository |
| ------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
| Vite | `tevm/bundler/vite-plugin` | [@tevm/vite-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/vite) |
| Webpack | `tevm/bundler/webpack-plugin` | [@tevm/webpack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/webpack) |
| Rollup | `tevm/bundler/rollup-plugin` | [@tevm/rollup-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rollup) |
| ESBuild | `tevm/bundler/esbuild-plugin` | [@tevm/esbuild-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/esbuild) |
| Bun | `tevm/bundler/bun-plugin` | [@tevm/bun-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/bun) |
| Rspack | `tevm/bundler/rspack-plugin` | [@tevm/rspack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rspack) |
All plugins share a similar usage pattern and a single configuration interface.
### Prerequisites & Key Points
#### Optional Bundler
You can compile contracts manually or use `npx tevm gen`. The plugins are purely optional if you prefer a different workflow.
#### TypeScript Integration
For best results, add the Tevm TS plugin to your tsconfig.json. This allows your editor to resolve Solidity imports, provide NatSpec docs on hover, go-to-definition on solidity methods, and more.
#### .s.sol for Bytecode
By default, Tevm only generates bytecode for Solidity files ending in `.s.sol`. Regular `.sol` files still compile for ABIs, but omit deployable bytecode. This helps differentiate purely interface-like contracts from ones you intend to deploy or call as scripts.
#### Tevm Cache
Compiled artifacts and metadata are stored in a `.tevm` folder. It is strongly recommended to `.gitignore` this directory.
#### Foundry/Remappings
If you have a Foundry setup or custom remappings, you can enable them in a `tevm.config.json`.
#### Next.js
Next.js can conflict with the TS plugin's type-checking. If you wish to keep type-checking turned on for Next.js, consider the [Codegen Approach](#codegen-approach). Otherwise, you may disable type-checking in your Next config.
### Plugin Configuration
All Tevm bundler plugins accept a similar configuration object:
```ts
type PluginOptions = {
// (Optional) solc compiler version
solc?: '0.8.19' | '0.8.20' | /* other supported solc versions */
}
```
For more complex, global settings (e.g., Foundry integration, custom libs, or remappings), place them in `tevm.config.json` (see [Configuration with tevm.config.json](#configuration-with-tevmconfigjson)).
### Bundler-Specific Setup
#### 1. Vite Example
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { vitePluginTevm } from 'tevm/bundler/vite-plugin'
export default defineConfig({
plugins: [
react(),
vitePluginTevm({
solc: '0.8.20', // optional
}),
],
})
```
#### 2. Webpack Example
```js
// webpack.config.js
const { webpackPluginTevm } = require('tevm/bundler/webpack-plugin')
module.exports = {
// other config...
plugins: [
webpackPluginTevm({
solc: '0.8.20', // optional
}),
],
}
```
#### 3. Rollup Example
```js
// rollup.config.js
import { rollupPluginTevm } from 'tevm/bundler/rollup-plugin'
export default {
// other Rollup options...
plugins: [
rollupPluginTevm({
solc: '0.8.20',
}),
],
}
```
#### 4. ESBuild Example
```js
// build.js
const { build } = require('esbuild')
const { esbuildPluginTevm } = require('tevm/bundler/esbuild-plugin')
build({
entryPoints: ['src/index.js'],
outdir: 'dist',
bundle: true,
plugins: [
esbuildPluginTevm({
solc: '0.8.20',
}),
],
})
```
#### 5. Bun Example
```ts
// plugins.ts
import { plugin } from 'bun'
import { tevmBunPlugin } from 'tevm/bundler/bun-plugin'
plugin(tevmBunPlugin({
solc: '0.8.20',
}))
```
```toml
# bunfig.toml
preload = ["./plugins.ts"]
[test]
preload = ["./plugins.ts"]
```
#### 6. Rspack Example
```js
// rspack.config.js
const { rspackPluginTevm } = require('tevm/bundler/rspack-plugin')
module.exports = {
// other Rspack config...
plugins: [
rspackPluginTevm({
solc: '0.8.20',
}),
],
}
```
### TypeScript Plugin
Alongside the bundler plugin, configure your TypeScript environment to benefit from editor-level Solidity integration (NatSpec hovers, jump-to-definition, type-checking, etc.):
```json
{
"compilerOptions": {
"plugins": [
{ "name": "@tevm/ts-plugin" }
]
// ...rest of your TS options
}
}
```
**VSCode**: Make sure you use the "workspace version" of TypeScript to see accurate IntelliSense.
### How the Bundler Works
All Tevm bundler plugins share a unified core (`@tevm/base-bundler`):
#### Import Detection & Resolution
* Scans for `.sol` imports; merges in tsconfig.json paths, foundry remappings, and your tevm.config.json.
#### Compilation
* Uses solc to compile the full dependency graph.
* If the file ends in `.s.sol`, bytecode is generated; otherwise only ABI is produced.
#### Code Generation
* Creates a TypeScript module that exports a Tevm Contract instance (with `.read` and `.write` methods, abi, and optionally bytecode).
#### Caching
* Stores results in `.tevm` to avoid unnecessary recompilation. Consider ignoring `.tevm` in your `.gitignore`.
#### LSP & TS Plugin
* The `@tevm/ts-plugin` references bundler outputs, enabling advanced IDE features like contract-level auto-completion, hover docs, and go-to-definition directly into Solidity code.
### Configuration with tevm.config.json
Create a `tevm.config.json` in your project root for advanced bundler behaviors:
```json
{
"foundryProject": true,
"libs": ["./lib", "./node_modules"],
"remappings": {
"foo": "vendored/foo"
},
"debug": false,
"cacheDir": ".tevm",
"jsonAsConst": ["**/*.abi.json"]
}
```
| Option | Type | Description |
| ---------------- | ------------------------ | ---------------------------------------------------------------- |
| `foundryProject` | `boolean \| string` | Enable Foundry integration (`true`) or path to foundry.toml |
| `libs` | `string[]` | Library paths for Solidity imports (used alongside Foundry libs) |
| `remappings` | `Record` | Custom import remappings |
| `debug` | `boolean` | Output extra debug logs and files in `.tevm` |
| `cacheDir` | `string` | Location for compiled artifacts (default: `.tevm`) |
| `jsonAsConst` | `string \| string[]` | Glob patterns for JSON files to be typed as const in TS |
### Codegen Approach
If you prefer not to rely on bundler-based transformations (for instance, if you're using Next.js with strict type-checking enabled), Tevm also supports an offline code generation approach:
```bash
npx tevm gen
```
* Generates `.ts` files next to each `.sol` file (or wherever configured).
* You can commit these `.ts` files to source control.
* The result is effectively the same as bundler output but doesn't require hooking into your build process.
This is especially helpful when:
* You have a framework that tightly controls its build pipeline (e.g., Next.js with enforced type-checking).
* You prefer explicit, committed TypeScript artifacts for contracts.
* You want a stable CI pipeline or want to avoid runtime resolution.
### Troubleshooting
#### 1. Missing or Red Underlines in Editor
* Confirm you have `"plugins": [{ "name": "@tevm/ts-plugin" }]` in tsconfig.json.
* In VSCode, switch to the "workspace version" of TypeScript.
#### 2. Type-check Errors with Next.js
* Next's built-in type checker might not handle dynamic `.sol` imports well.
* Either disable type-checking in next.config.mjs or use [Codegen Approach](#codegen-approach).
#### 3. "File Not Found" Errors
* Check that your libraries or local imports are accounted for in libs or remappings.
* If you're using Foundry, ensure `foundryProject: true` is set in tevm.config.json.
#### 4. Cache Stale Issues
* If a contract's changes don't appear, remove the `.tevm` folder and rebuild.
### Examples and Further Reading
* [Examples in Monorepo](https://github.com/evmts/tevm-monorepo/tree/main/examples): Vite, Next, Bun, ESBuild
* [Tevm Contract Reference](/reference/contract): Usage docs for contract reads, writes, events
* [Wagmi + Tevm](https://github.com/evmts/tevm-monorepo/tree/main/examples): See WagmiReads.tsx and WagmiWrites.tsx examples for real usage
* [Inline Solidity (Coming Soon)](#): ` sol` \`\` inline code snippet approach
* [Globals.md](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/base-bundler/docs/globals.md): Core reference for bundler internals
:::tip
Remember: Tevm's bundler is optionalโit's there to make importing Solidity as straightforward as possible. If your project setup or environment constraints make the bundler difficult, consider the `tevm gen` codegen workflow or a purely manual approach to contract artifacts.
:::
## Tevm Contract Bundler
The Tevm Contract Bundler allows you to import Solidity files directly into your TypeScript code, enabling a seamless development experience with type safety and IDE integration.
:::note
This page has been split into smaller, more manageable sections. Please visit the new [Bundler Reference](/reference/bundler/) for the updated documentation.
:::
### Quick Navigation
The bundler documentation is now split into several sections for better organization:
* **[Overview](/reference/bundler/overview)** - Introduction, key benefits, available plugins
* **[Internals](/reference/bundler/internals)** - How the bundler works under the hood
* **[Methods & Exports](/reference/bundler/methods)** - Key APIs for advanced usage
* **[Troubleshooting](/reference/bundler/troubleshooting)** - Common issues and solutions
### What is the Tevm Bundler?
The Tevm bundler transforms Solidity `.sol` files into TypeScript modules at build time. When you import a contract, the bundler:
1. Reads and compiles the Solidity source code
2. Extracts the ABI, bytecode, and other contract information
3. Generates a TypeScript module with a [Tevm Contract](/reference/contract) instance
This means you can interact with your contracts in a fully type-safe way, with editor features like auto-completion, go-to-definition, and inline documentation.
### Getting Started
For a quick introduction to using the bundler, see the [Bundler Quickstart](/getting-started/bundler) guide.
If you're looking for detailed reference information, explore the sections linked above.
### Coming Soon Features
Tevm is actively developing new bundler features:
* **Inline Solidity with `sol` Tag** - Define contracts directly in your JS/TS using template literals
* **CAIP-10 Contract Imports** - Import contracts from any chain using standardized identifiers
Learn more about these upcoming features in the [Upcoming Features](/reference/bundler/overview#coming-soon-features) section.
## Tevm CLI Reference
The Tevm CLI provides comprehensive tooling for Ethereum development, testing, and interaction. This reference documents all available commands, options, and usage patterns.
### Command Structure
The CLI follows a command-based structure:
```bash
tevm [subcommand] [options]
```
Commands are organized by functionality, with the most commonly used commands available at the top level and more specialized commands grouped into categories.
### Core Commands
#### `create`
[Source: cli/src/commands/create.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/create.tsx)
Creates a new Ethereum project with smart contract support.
```bash
tevm create [name] [options]
```
**Options:**
* `--template` - Project template to use (react, vue, vanilla, etc.)
* `--skip-prompts` - Skip interactive prompts and use default values
When run without options, launches an interactive wizard that guides you through project setup.
#### `serve`
[Source: cli/src/commands/serve.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/serve.tsx)
Starts a local Ethereum JSON-RPC server with Tevm features.
```bash
tevm serve [options]
```
**Options:**
* `--port ` - Port to listen on (default: 8545)
* `--host ` - Host to bind to (default: "localhost")
* `--fork ` - URL of network to fork from
* `--chainId ` - Chain ID to use (default: "900")
* `--forkBlockNumber ` - Block number to fork from (default: "latest")
* `--loggingLevel ` - Logging level (default: "info")
* `--verbose` - Enable verbose logging of JSON-RPC requests
#### `call`
[Source: cli/src/commands/call.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/call.tsx)
Executes a raw EVM call against a contract or address.
```bash
tevm call [options]
```
**Options:**
* `--to ` - Contract address to call
* `--from ` - Address to send the transaction from
* `--data ` - Transaction data (hex encoded)
* `--value ` - ETH value to send in wei
* `--run` - Run directly without interactive parameter editing
* `--rpc ` - RPC endpoint (default: "[http://localhost:8545](http://localhost:8545)")
* `--code ` - The encoded code to deploy (with constructor args)
* `--deployedBytecode ` - Deployed bytecode to put in state before call
* `--gas ` - Gas limit for the transaction
* `--gasPrice ` - Gas price in wei
* `--createTrace` - Return a complete trace with the call
* `--createAccessList` - Return an access list of storage access
* `--createTransaction ` - Whether to update state (on-success, always, never)
* `--formatJson` - Format output as JSON
#### `generate`
[Source: cli/src/commands/generate.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/generate.tsx)
Generates TypeScript types from Solidity contracts.
```bash
tevm generate contract [name] [options]
```
**Options:**
* `--force` - Overwrite existing files
* `--dir ` - Working directory (default: current directory)
* `--include ` - Glob pattern(s) for Solidity files (default: "src/\*\*/\*.sol")
* `--output ` - Custom output directory for generated files
* `--verbose` - Show verbose output
### State Management Commands
#### `getAccount`
[Source: cli/src/commands/getAccount.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getAccount.tsx)
Retrieves account information, including balance, nonce, and code.
```bash
tevm getAccount [options]
```
**Options:**
* `--address ` - Account address to query
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `setAccount`
[Source: cli/src/commands/setAccount.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/setAccount.tsx)
Sets or updates account state.
```bash
tevm setAccount [options]
```
**Options:**
* `--address ` - Account address to modify
* `--balance ` - New balance in wei
* `--nonce ` - New nonce value
* `--code ` - New contract code (hex encoded)
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `dumpState`
[Source: cli/src/commands/dumpState.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/dumpState.tsx)
Dumps the entire state for backup or debugging.
```bash
tevm dumpState [options]
```
**Options:**
* `--output ` - Output file path (default: stdout)
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `loadState`
[Source: cli/src/commands/loadState.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/loadState.tsx)
Loads state from a previously dumped state file.
```bash
tevm loadState [options]
```
**Options:**
* `--input ` - Input file path
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Contract Interaction Commands
#### `deploy`
[Source: cli/src/commands/deploy.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/deploy.tsx)
Deploys a contract to the network.
```bash
tevm deploy [options]
```
**Options:**
* `--code ` - Contract bytecode to deploy
* `--from ` - Address to deploy from
* `--value ` - ETH value to send with deployment
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `readContract`
[Source: cli/src/commands/readContract.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/readContract.tsx)
Reads data from a deployed contract using its ABI.
```bash
tevm readContract [options]
```
**Options:**
* `--address ` - Contract address
* `--abi ` - Contract ABI (as JSON string or file path)
* `--function ` - Function name to call
* `--args ` - Function arguments as JSON array
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `contract`
[Source: cli/src/commands/contract.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/contract.tsx)
Comprehensive contract operations including deployment, interaction, and event monitoring.
```bash
tevm contract [options]
```
**Options:**
* `--address ` - Contract address
* `--abi ` - Contract ABI (as JSON string or file path)
* `--bytecode ` - Contract bytecode for deployment
* `--function ` - Function name to call
* `--args ` - Function arguments as JSON array
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Network Commands
#### `mine`
[Source: cli/src/commands/mine.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/mine.tsx)
Mines new blocks on the local node.
```bash
tevm mine [options]
```
**Options:**
* `--blocks ` - Number of blocks to mine (default: 1)
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getBlockNumber`
[Source: cli/src/commands/getBlockNumber.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getBlockNumber.tsx)
Gets the current block number.
```bash
tevm getBlockNumber [options]
```
**Options:**
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getBlock`
[Source: cli/src/commands/getBlock.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getBlock.tsx)
Gets information about a specific block.
```bash
tevm getBlock [options]
```
**Options:**
* `--blockNumber ` - Block number to query
* `--blockHash ` - Block hash to query
* `--includeTransactions` - Include full transactions
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getChainId`
[Source: cli/src/commands/getChainId.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getChainId.tsx)
Gets the current chain ID.
```bash
tevm getChainId [options]
```
**Options:**
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Storage & Code Commands
#### `getStorageAt`
[Source: cli/src/commands/getStorageAt.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getStorageAt.tsx)
Gets the value from a storage slot at a contract address.
```bash
tevm getStorageAt [options]
```
**Options:**
* `--address ` - Contract address
* `--slot ` - Storage slot (hex string)
* `--blockTag ` - Block tag or number
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `setStorageAt`
[Source: cli/src/commands/setStorageAt.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/setStorageAt.tsx)
Sets the value for a storage slot at a contract address.
```bash
tevm setStorageAt [options]
```
**Options:**
* `--address ` - Contract address
* `--slot ` - Storage slot (hex string)
* `--value ` - New value (hex string)
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getBytecode`
[Source: cli/src/commands/getBytecode.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getBytecode.tsx)
Gets the bytecode at a contract address.
```bash
tevm getBytecode [options]
```
**Options:**
* `--address ` - Contract address
* `--blockTag ` - Block tag or number
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `setCode`
[Source: cli/src/commands/setCode.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/setCode.tsx)
Sets the code at a contract address.
```bash
tevm setCode [options]
```
**Options:**
* `--address ` - Contract address
* `--code ` - New contract code
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Transaction Commands
#### `getTransaction`
[Source: cli/src/commands/getTransaction.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getTransaction.tsx)
Gets transaction details by hash.
```bash
tevm getTransaction [options]
```
**Options:**
* `--hash ` - Transaction hash
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `estimateGas`
[Source: cli/src/commands/estimateGas.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/estimateGas.tsx)
Estimates gas for a transaction.
```bash
tevm estimateGas [options]
```
**Options:**
* `--to ` - Target address
* `--from ` - Sender address
* `--data ` - Transaction data
* `--value ` - Transaction value
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getGasPrice`
[Source: cli/src/commands/getGasPrice.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getGasPrice.tsx)
Gets the current gas price.
```bash
tevm getGasPrice [options]
```
**Options:**
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `estimateFeesPerGas`
[Source: cli/src/commands/estimateFeesPerGas.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/estimateFeesPerGas.tsx)
Estimates EIP-1559 gas fees.
```bash
tevm estimateFeesPerGas [options]
```
**Options:**
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Advanced Commands
#### `compile`
[Source: cli/src/commands/compile.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/compile.tsx)
Compiles Solidity contracts.
```bash
tevm compile [options]
```
**Options:**
* `--input ` - Input file or directory
* `--output ` - Output directory
* `--solc-version ` - Solidity compiler version
* `--run` - Run directly without interactive parameter editing
#### `multicall`
[Source: cli/src/commands/multicall.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/multicall.tsx)
Executes multiple calls in a single transaction.
```bash
tevm multicall [options]
```
**Options:**
* `--calls ` - JSON array of call objects
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `createBlockFilter`
[Source: cli/src/commands/createBlockFilter.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/createBlockFilter.tsx)
Creates a filter for new blocks.
```bash
tevm createBlockFilter [options]
```
**Options:**
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `createEventFilter`
[Source: cli/src/commands/createEventFilter.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/createEventFilter.tsx)
Creates a filter for events.
```bash
tevm createEventFilter [options]
```
**Options:**
* `--address ` - Contract address
* `--topics ` - Event topics as JSON array
* `--fromBlock ` - Start block
* `--toBlock ` - End block
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `createContractEventFilter`
[Source: cli/src/commands/createContractEventFilter.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/createContractEventFilter.tsx)
Creates a filter for contract events using ABI.
```bash
tevm createContractEventFilter [options]
```
**Options:**
* `--address ` - Contract address
* `--abi ` - Contract ABI
* `--event ` - Event name
* `--args ` - Event arguments
* `--fromBlock ` - Start block
* `--toBlock ` - End block
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Specialized Viem Action Commands
The Tevm CLI includes commands for all Viem client actions in the `action` directory. These commands provide direct access to Viem's client actions with the familiar Tevm CLI interface:
* `tevm action createAccessList`
* `tevm action getBalance`
* `tevm action sendRawTransaction`
* `tevm action simulateCalls`
[Source: cli/src/commands/action/](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/action/)
### ENS Commands
#### `getEnsAddress`
[Source: cli/src/commands/getEnsAddress.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getEnsAddress.tsx)
Resolves ENS name to address.
```bash
tevm getEnsAddress [options]
```
**Options:**
* `--name ` - ENS name
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getEnsName`
[Source: cli/src/commands/getEnsName.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getEnsName.tsx)
Resolves address to ENS name.
```bash
tevm getEnsName [options]
```
**Options:**
* `--address ` - Ethereum address
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
#### `getEnsText`
[Source: cli/src/commands/getEnsText.tsx](https://github.com/evmts/tevm-monorepo/blob/main/cli/src/commands/getEnsText.tsx)
Gets ENS text record.
```bash
tevm getEnsText [options]
```
**Options:**
* `--name ` - ENS name
* `--key ` - Text record key
* `--rpc ` - RPC endpoint
* `--run` - Run directly without interactive parameter editing
### Environment Variables
The Tevm CLI supports configuration via environment variables for all commands. Environment variables take precedence over default values but are overridden by explicit command-line options.
Common environment variables include:
* `TEVM_RPC` - Default RPC endpoint
* `TEVM_FROM` - Default sender address
* `TEVM_TO` - Default target address
* `TEVM_GAS` - Default gas limit
* `TEVM_GAS_PRICE` - Default gas price
* `TEVM_VALUE` - Default transaction value
* `TEVM_FORMAT_JSON` - Whether to format output as JSON
* `TEVM_RUN` - Whether to run in non-interactive mode
* `EDITOR` - Preferred editor for interactive mode
Environment variables follow the naming pattern `TEVM_` followed by the option name in uppercase, with dashes converted to underscores.
### Interactive Mode
Most Tevm CLI commands support an interactive mode that opens your default text editor with a template for the command parameters. This is especially useful for complex operations where you need to carefully construct parameters.
The interactive editor opens a temporary file with:
* JSON template for all command parameters
* Comments explaining each parameter
* Default values pre-filled
After you save and close the editor, the command executes with your customized parameters.
To skip interactive mode and execute commands directly, use the `--run` flag.
### Exit Codes
The Tevm CLI uses standard exit codes:
* `0` - Success
* `1` - General error
* `2` - Invalid usage
* `3` - External error (e.g., network issues)
* `4` - Internal error (unexpected conditions)
### Command-Line Help
Every command supports the `--help` flag to display usage information:
```bash
tevm --help
tevm --help
```
This displays information about the command, available options, and examples.
### See Also
* [CLI Quickstart Guide](/getting-started/cli)
* [Tevm Node API Reference](/reference/actions)
* [Bundler Documentation](/reference/bundler)
## @tevm/common
The `@tevm/common` package provides chain-specific configuration and utilities for Tevm clients. It extends the functionality of `ethereumjs/common` and integrates with Viem's chain definitions.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/common/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs) folder.
### Installation
```bash
npm install @tevm/common
```
### Overview
The `@tevm/common` package provides:
* Chain configuration management
* Hardfork settings and EIP support
* Consensus algorithm configuration
* Network-specific settings
* Extensive chain definitions
### API Reference
#### Enumerations
* [ConsensusAlgorithm](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/enumerations/ConsensusAlgorithm.md) - Supported consensus algorithms
* [ConsensusType](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/enumerations/ConsensusType.md) - Types of consensus mechanisms
#### Interfaces
* [CustomCrypto](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/CustomCrypto.md) - Custom cryptographic implementations
* [EvmStateManagerInterface](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/EvmStateManagerInterface.md) - EVM state manager interface
* [StorageDump](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/StorageDump.md) - Storage dump format
* [StorageRange](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/StorageRange.md) - Storage range specification
#### Type Aliases
* [AccountFields](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/AccountFields.md) - Account field definitions
* [CliqueConfig](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/CliqueConfig.md) - Clique consensus configuration
* [Common](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/Common.md) - Main chain configuration type
* [CommonOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/CommonOptions.md) - Configuration options
* [Hardfork](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/Hardfork.md) - Supported hardforks
* [MockKzg](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/MockKzg.md) - Mock KZG implementation
#### Functions
* [createCommon](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/functions/createCommon.md) - Create a new Common instance
* [createMockKzg](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/functions/createMockKzg.md) - Create a mock KZG implementation
#### Supported Networks
The package includes configurations for numerous networks, including:
##### Layer 1 Networks
* [mainnet](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/mainnet.md) - Ethereum Mainnet
* [sepolia](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/sepolia.md) - Sepolia Testnet
* [goerli](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/goerli.md) - Goerli Testnet
* [holesky](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/holesky.md) - Holesky Testnet
##### Layer 2 Networks
* [arbitrum](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/arbitrum.md) - Arbitrum One
* [optimism](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/optimism.md) - Optimism
* [base](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/base.md) - Base
* [zksync](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/zksync.md) - zkSync Era
* [polygon](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/polygon.md) - Polygon PoS
* [polygonZkEvm](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/polygonZkEvm.md) - Polygon zkEVM
##### Alternative Networks
* [bsc](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/bsc.md) - BNB Smart Chain
* [avalanche](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/avalanche.md) - Avalanche C-Chain
* [fantom](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/fantom.md) - Fantom Opera
* [gnosis](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/gnosis.md) - Gnosis Chain
##### Development Networks
* [hardhat](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/hardhat.md) - Hardhat Network
* [foundry](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/foundry.md) - Foundry Network
* [anvil](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/anvil.md) - Anvil Network
* [localhost](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/localhost.md) - Local Network
### Usage Examples
#### Basic Configuration
```typescript
import { createCommon } from '@tevm/common'
import { mainnet } from '@tevm/common'
const common = createCommon({
...mainnet,
hardfork: 'shanghai'
})
```
#### Custom Chain Configuration
```typescript
import { createCommon } from '@tevm/common'
const common = createCommon({
name: 'Custom Chain',
chainId: 1337,
networkId: 1337,
defaultHardfork: 'shanghai',
consensus: {
type: 'poa',
algorithm: 'clique',
clique: {
period: 15,
epoch: 30000
}
}
})
```
#### Using with Custom Crypto
```typescript
import { createCommon, createMockKzg } from '@tevm/common'
const common = createCommon({
...mainnet,
customCrypto: {
kzg: createMockKzg()
}
})
```
#### Network-Specific Configuration
```typescript
import { createCommon, optimism, arbitrum } from '@tevm/common'
// Optimism configuration
const optimismCommon = createCommon({
...optimism,
hardfork: 'bedrock'
})
// Arbitrum configuration
const arbitrumCommon = createCommon({
...arbitrum,
hardfork: 'nitro'
})
```
### Error Handling
The package throws specific errors for invalid configurations:
```typescript
try {
const common = createCommon({
chainId: -1 // Invalid chain ID
})
} catch (error) {
if (error.code === 'INVALID_CHAIN_ID') {
console.error('Invalid chain ID provided')
}
}
```
### See Also
* [EthereumJS Common Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/common)
* [Viem Chains Documentation](https://viem.sh/docs/chains/introduction.html)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/globals.md)
## @tevm/contract
The `@tevm/contract` package provides powerful utilities for working with Ethereum smart contracts, offering type-safe contract interactions and simplified deployment workflows.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/contract/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs) folder.
### Installation
```bash
npm install @tevm/contract
```
### Overview
The contract package provides:
* Type-safe contract interactions
* Support for human-readable and JSON ABIs
* Contract deployment utilities
* Read and write method handling
* Event filtering and subscription
* Pre-built contract templates (ERC20, ERC721)
### API Reference
#### Type Aliases
* [Contract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/Contract.md) - Core contract type definition
* [CreateContractFn](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/CreateContractFn.md) - Contract creation function type
* [CreateContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/CreateContractParams.md) - Parameters for contract creation
* [EventActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/EventActionCreator.md) - Event action creator type
* [MaybeExtractEventArgsFromAbi](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/MaybeExtractEventArgsFromAbi.md) - Event args extraction utility
* [ReadActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/ReadActionCreator.md) - Read action creator type
* [ValueOf](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/ValueOf.md) - Utility type for value extraction
* [WriteActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/WriteActionCreator.md) - Write action creator type
#### Functions
* [createContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/functions/createContract.md) - Create a new contract instance
#### Pre-built Contracts
* [ERC20](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/ERC20.md) - Standard ERC20 token implementation
* [ERC721](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/ERC721.md) - Standard ERC721 NFT implementation
* [SimpleContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/SimpleContract.md) - Basic contract for testing
### Usage Examples
#### Creating a Contract Instance
```typescript
import { createContract } from '@tevm/contract'
// Using human-readable ABI
const contract = createContract({
// Use as const for type safety
humanReadableAbi: [
'function balanceOf(address) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)',
'event Transfer(address indexed from, address indexed to, uint256 value)',
] as const,
name: 'MyToken',
})
// Type-safe read actions
const readAction = contract.read.balanceOf('0x...')
// Type-safe write actions
const writeAction = contract.write.transfer('0x...', 1000n)
```
#### Contract with Address
```typescript
const token = contract.withAddress('0x1234...')
// Now includes address in all actions
const balanceAction = token.read.balanceOf('0x...')
// balanceAction.to will be set to the token address
```
#### Using Standard Contracts
```typescript
import { ERC20, ERC721 } from '@tevm/contract'
// ERC20 token with all standard methods
const token = ERC20.withAddress('0x...')
// Read token info
const nameAction = token.read.name()
const symbolAction = token.read.symbol()
const supplyAction = token.read.totalSupply()
// Transfer tokens
const transferAction = token.write.transfer('0x...', 1000n)
// ERC721 NFT contract
const nft = ERC721.withAddress('0x...')
const ownerAction = nft.read.ownerOf(1n)
```
#### Deployless Scripts
```typescript
import { ERC20 } from '@tevm/contract'
// Create a script that deploys and initializes a token
const script = ERC20.script({
bytecode: '0x...', // Contract bytecode
args: ['MyToken', 'MTK', 1000000n], // Constructor args
})
// Use with any compatible client
const name = await client.contract(script.read.name())
const symbol = await client.contract(script.read.symbol())
```
#### Event Handling
```typescript
// Create event filter
const filter = contract.events.Transfer({
fromBlock: 'latest',
})
// Process events
client.watchEvent(filter, (event) => {
console.log('Transfer:', {
from: event.args.from,
to: event.args.to,
value: event.args.value,
})
})
```
### Best Practices
#### 1. Type Safety
Always use `as const` with ABIs to get full type inference:
```typescript
const abi = [
'function example(uint256 value) returns (bool)',
] as const
const contract = createContract({
humanReadableAbi: abi,
name: 'Example',
})
// contract.write.example will have proper types
```
#### 2. Error Handling
Handle contract errors appropriately:
```typescript
try {
const result = await client.contract(contract.write.transfer('0x...', 1000n))
} catch (e) {
if (e.message.includes('insufficient balance')) {
// Handle specific error case
}
throw e
}
```
#### 3. Gas Management
Consider gas costs in write operations:
```typescript
const tx = contract.write.complexOperation('0x...', {
gas: 500000n, // Set gas limit
maxFeePerGas: 30000000000n, // Set max fee
})
```
### Contract Types
The package exports useful types for contract development:
```typescript
import type {
Contract,
CreateContractFn,
CreateContractParams,
EventActionCreator,
ReadActionCreator,
WriteActionCreator,
} from '@tevm/contract'
// Use with your own contracts
type MyContract = Contract
```
### See Also
* [ERC-20 Token Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/)
* [ERC-721 NFT Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/)
* [Solidity Documentation](https://docs.soliditylang.org/)
* [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/globals.md)
## @tevm/decorators
The `@tevm/decorators` package provides action decorators and utilities for extending Tevm clients with additional functionality. It includes EIP-1193 compatible providers, Ethereum JSON-RPC methods, and Tevm-specific actions.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/decorators/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs) folder.
### Installation
```bash
npm install @tevm/decorators
```
### Overview
The `@tevm/decorators` package provides:
* EIP-1193 compatible provider decorators
* Ethereum JSON-RPC method implementations
* Tevm-specific action decorators
* Type-safe request and response handling
* Chain and network utilities
### API Reference
#### Core Functions
##### [`ethActions()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/ethActions.md)
Creates an extension providing standard Ethereum JSON-RPC methods.
##### [`requestEip1193()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/requestEip1193.md)
Creates an EIP-1193 compatible provider extension.
##### [`tevmActions()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/tevmActions.md)
Creates an extension providing Tevm-specific actions.
##### [`tevmSend()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/tevmSend.md)
Creates an extension for sending Tevm JSON-RPC requests.
#### Core Types
##### Provider Types
* [`Eip1193RequestProvider`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/Eip1193RequestProvider.md) - EIP-1193 compatible provider interface
* [`EIP1193Parameters`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193Parameters.md) - Parameters for EIP-1193 requests
* [`EIP1193RequestFn`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193RequestFn.md) - Request function type for EIP-1193
* [`EIP1193RequestOptions`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193RequestOptions.md) - Options for EIP-1193 requests
##### Action Types
* [`EthActionsApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/EthActionsApi.md) - Standard Ethereum JSON-RPC actions
* [`TevmActionsApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TevmActionsApi.md) - Tevm-specific actions
* [`TevmSendApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TevmSendApi.md) - API for sending Tevm requests
##### RPC Schema Types
* [`RpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/RpcSchema.md) - Base RPC schema type
* [`RpcSchemaOverride`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/RpcSchemaOverride.md) - Schema override type
* [`DerivedRpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/DerivedRpcSchema.md) - Derived schema type
* [`JsonRpcSchemaPublic`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaPublic.md) - Public JSON-RPC schema
* [`JsonRpcSchemaTevm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaTevm.md) - Tevm-specific JSON-RPC schema
* [`JsonRpcSchemaWallet`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaWallet.md) - Wallet JSON-RPC schema
* [`TestRpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TestRpcSchema.md) - Test-specific RPC schema
##### Ethereum Types
* [`AddEthereumChainParameter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/AddEthereumChainParameter.md) - Parameters for adding a chain
* [`WatchAssetParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WatchAssetParams.md) - Parameters for watching assets
* [`WalletPermission`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WalletPermission.md) - Wallet permission type
* [`WalletPermissionCaveat`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WalletPermissionCaveat.md) - Wallet permission caveat type
##### Utility Types
* [`Hash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/Hash.md) - Ethereum hash type
* [`LogTopic`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/LogTopic.md) - Log topic type
* [`NetworkSync`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/NetworkSync.md) - Network sync status type
* [`Quantity`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/Quantity.md) - Ethereum quantity type
### Usage Examples
#### Creating an EIP-1193 Provider
```typescript
import { requestEip1193 } from '@tevm/decorators'
const provider = requestEip1193()
const client = createClient({
transport: provider
})
// Make EIP-1193 requests
const result = await client.request({
method: 'eth_call',
params: [{
to: '0x...',
data: '0x...'
}]
})
```
#### Using Ethereum Actions
```typescript
import { ethActions } from '@tevm/decorators'
const eth = ethActions()
const client = createClient({
transport: eth
})
// Use standard Ethereum methods
const balance = await client.eth.getBalance({
address: '0x...'
})
const code = await client.eth.getCode({
address: '0x...'
})
```
#### Using Tevm Actions
```typescript
import { tevmActions } from '@tevm/decorators'
const tevm = tevmActions()
const client = createClient({
transport: tevm
})
// Use Tevm-specific actions
const result = await client.transport.tevm.call({
to: '0x...',
data: '0x...'
})
const state = await client.transport.tevm.dumpState()
```
#### Error Handling
```typescript
try {
const result = await client.request({
method: 'eth_call',
params: [{
to: '0x...',
data: '0x...'
}]
})
} catch (error) {
if (error.code === -32000) {
// Handle execution error
}
throw error
}
```
### Best Practices
#### 1. Type Safety
Always leverage TypeScript types for request parameters and responses:
```typescript
import type { EIP1193RequestFn } from '@tevm/decorators'
const request: EIP1193RequestFn = async (params) => {
// Type-safe parameters and return value
}
```
#### 2. Error Handling
Handle both standard JSON-RPC errors and Tevm-specific errors:
```typescript
try {
await client.request(...)
} catch (error) {
if (error.code === 4001) {
// User rejected request
} else if (error.code === -32000) {
// Execution error
}
}
```
#### 3. Chain Management
Use proper chain parameters when adding new chains:
```typescript
const chainParams: AddEthereumChainParameter = {
chainId: '0x1',
chainName: 'Ethereum Mainnet',
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18
},
rpcUrls: ['https://...']
}
```
### See Also
* [JSON-RPC API](/api/json-rpc)
* [Actions Guide](/reference/actions)
* [EIP-1193 Specification](https://eips.ethereum.org/EIPS/eip-1193)
* [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/)
## @tevm/evm
The `@tevm/evm` package provides a custom implementation of the Ethereum Virtual Machine (EVM), extending the functionality of `ethereumjs/evm`. It's responsible for executing Ethereum bytecode and managing the execution environment.
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/evm/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs) folder.
### Installation
```bash
npm install @tevm/evm
```
### API Reference
#### Enumerations
* [EvmErrorMessage](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/enumerations/EvmErrorMessage.md) - EVM error message types
#### Core Classes
* [Evm](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/Evm.md) - Main EVM implementation class
* [EthjsMessage](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/EthjsMessage.md) - EVM message handling
* [EvmError](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/EvmError.md) - EVM error handling
#### Interfaces
* [EvmResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/EvmResult.md) - Result of EVM execution
* [EvmRunCallOpts](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/EvmRunCallOpts.md) - Options for running calls
* [ExecResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/ExecResult.md) - Execution result details
* [InterpreterStep](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/InterpreterStep.md) - Interpreter step information
* [PrecompileInput](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/PrecompileInput.md) - Input for precompiles
#### Type Aliases
* [CreateEvmOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/CreateEvmOptions.md) - Options for creating an EVM
* [CustomPrecompile](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/CustomPrecompile.md) - Custom precompile definition
* [EVMOpts](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/EVMOpts.md) - EVM configuration options
#### Variables
* [Eof](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/variables/Eof.md) - EOF-related constants
#### Functions
* [createEvm](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/functions/createEvm.md) - Create a new EVM instance
* [getActivePrecompiles](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/functions/getActivePrecompiles.md) - Get active precompiles
### Usage Examples
#### Creating an EVM Instance
```typescript
import { createEvm } from '@tevm/evm'
import { mainnet } from '@tevm/common'
import { createStateManager } from '@tevm/state'
import { createChain } from '@tevm/chain'
const evm = await createEvm({
common: mainnet,
stateManager: createStateManager,
blockchain: await createChain({ common: mainnet }),
customPrecompiles: [], // Optional custom precompiles
profiler: false, // Enable/disable profiling
loggingLevel: 'warn', // Logging configuration
})
```
#### Running EVM Calls
```typescript
const result = await evm.runCall({
to: '0x1234...',
caller: '0x5678...',
data: new Uint8Array([/* bytecode */]),
value: 1000n,
gasLimit: 100000n
})
console.log(result.execResult.returnValue)
console.log(result.execResult.executionGasUsed)
```
#### Custom Precompiles
```typescript
import { definePrecompile, defineCall, createContract, parseAbi } from '@tevm/evm'
import { createAddress } from '@tevm/utils'
const MyContract = createContract({
address: createAddress(2424).toString(),
abi: parseAbi([
'function addTwo(uint256) returns (uint256)',
])
})
const customPrecompile = definePrecompile({
contract: MyContract,
call: defineCall(MyContract.abi, {
addTwo: async ({ args }) => {
return {
returnValue: args[0] + 5n,
executionGasUsed: 0n
}
}
})
})
evm.addCustomPrecompile(customPrecompile.precompile())
```
#### Error Handling
```typescript
try {
const result = await evm.runCall({
to: '0x1234...',
gasLimit: 100n // Very low gas limit
})
} catch (error) {
if (error instanceof EvmError) {
console.log(error.error) // e.g. EvmErrorMessage.OUT_OF_GAS
}
}
```
#### Debugging and Performance
```typescript
// Enable debug logging
const evm = await createEvm({
loggingLevel: 'trace'
})
// Get performance logs
const logs = evm.getPerformanceLogs()
console.log(logs.opcodes) // Opcode execution stats
console.log(logs.precompiles) // Precompile execution stats
```
### See Also
* [EthereumJS EVM Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm)
* [Tevm State Documentation](https://tevm.sh/reference/tevm/state/)
* [Tevm Precompiles Documentation](https://tevm.sh/reference/tevm/precompiles/)
* [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/globals.md)
## Package Reference Documentation
This section contains detailed API reference documentation for all Tevm packages.
* [@tevm/actions](/reference/actions) - Core action handlers for interacting with the EVM
* [@tevm/address](/reference/address) - Ethereum address utilities
* [@tevm/block](/reference/block) - Block creation and manipulation
* [@tevm/blockchain](/reference/blockchain) - Blockchain data structures and utilities
* [@tevm/common](/reference/common) - Shared utilities and constants
* [@tevm/contract](/reference/contract) - Library for using contracts in typesafe way
* [Contract Bundler](/reference/bundler) - Import Solidity files directly in TypeScript
* [@tevm/decorators](/reference/decorators) - Extension decorators
* [@tevm/evm](/reference/evm) - Core EVM implementation
* [@tevm/memory-client](/reference/memory-client) - Batteries included viem client along with tree shakable actions
* [@tevm/receipt-manager](/reference/receipt-manager) - Transaction receipt handling
* [@tevm/state](/reference/state) - State management utilities
* [@tevm/tx](/reference/tx) - Transaction handling utilities
* [@tevm/txpool](/reference/txpool) - Transaction pool management
* [@tevm/utils](/reference/utils) - General utility functions
* [@tevm/vm](/reference/vm) - Virtual Machine implementation
Each package's documentation includes:
* Complete API reference
* Type definitions
* Usage examples
* Configuration options
## @tevm/memory-client
The `@tevm/memory-client` package provides a powerful in-memory Ethereum client implementation for Tevm. It combines the capabilities of viem with Tevm's custom EVM implementation to offer a complete testing and development environment.
### Installation
```bash
npm install @tevm/memory-client
```
### API Reference
#### Type Aliases
* [CreateMemoryClientFn](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/CreateMemoryClientFn.md) - Function type for creating memory clients
* [MemoryClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/MemoryClient.md) - Core memory client type
* [MemoryClientOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/MemoryClientOptions.md) - Configuration options
* [TevmActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmActions.md) - Tevm-specific actions
* [TevmContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmContract.md) - Contract interaction type
* [TevmRpcSchema](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmRpcSchema.md) - RPC schema definition
* [TevmTransport](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmTransport.md) - Transport layer type
* [TevmViemActionsApi](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmViemActionsApi.md) - Viem actions API
#### Actions
* [createClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createClient.md) - Create a new client instance
* [createMemoryClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createMemoryClient.md) - Create a memory client
* [createTevmTransport](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createTevmTransport.md) - Create transport layer
* [publicActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/publicActions.md) - Public action creators
* [testActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/testActions.md) - Test action creators
* [tevmCall](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmCall.md) - Execute EVM call
* [tevmContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmContract.md) - Contract interaction
* [tevmDeal](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmDeal.md) - Add native ETH or ERC20 tokens
* [tevmDeploy](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmDeploy.md) - Deploy contract
* [tevmDumpState](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmDumpState.md) - Export state
* [tevmGetAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmGetAccount.md) - Get account state
* [tevmLoadState](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmLoadState.md) - Import state
* [tevmMine](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmMine.md) - Mine blocks
* [tevmReady](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmReady.md) - Check client readiness
* [tevmSetAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmSetAccount.md) - Set account state
* [tevmViemActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmViemActions.md) - Viem action creators
* [walletActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/walletActions.md) - Wallet action creators
### Overview
The memory client package provides:
* A complete in-memory Ethereum client implementation
* Support for forking existing networks
* Automatic and manual mining modes
* Full compatibility with viem's actions
* Extended functionality through Tevm-specific actions
### Core Components
#### MemoryClient
The main client class that provides Ethereum client functionality with in-memory state management.
```typescript
import { createMemoryClient } from '@tevm/memory-client'
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io")
}
})
```
#### Client Options
```typescript
interface MemoryClientOptions {
// Chain configuration
common?: TCommon
// Fork configuration
fork?: {
transport: Transport
blockTag?: string | number
}
// Client configuration
name?: string
account?: TAccountOrAddress
pollingInterval?: number
cacheTime?: number
}
```
### Features
#### Mining Modes
The client supports two mining modes:
```typescript
// Auto mining (default)
const client = createMemoryClient()
// Manual mining
const client = createMemoryClient({
mining: {
mode: 'manual'
}
})
// Mine blocks manually
await client.mine()
```
#### Network Forking
Fork any EVM-compatible network:
```typescript
import { createMemoryClient, http } from '@tevm/memory-client'
import { optimism } from '@tevm/common'
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435'
},
common: optimism
})
```
#### State Management
```typescript
// Set account state
await client.setAccount({
address: '0x...',
balance: 100n,
nonce: 1n,
deployedBytecode: '0x...',
state: {
'0x...': '0x...'
}
})
// Get account state
const account = await client.getAccount({
address: '0x...',
returnStorage: true
})
```
#### Contract Interactions
```typescript
// Deploy contract
const deployResult = await client.tevmDeploy({
abi: contractAbi,
bytecode: contractBytecode,
args: ['Constructor', 'Args']
})
// Call contract
const result = await client.tevmCall({
to: contractAddress,
data: encodeFunctionData(...)
})
// Contract interaction with high-level API
const contractResult = await client.tevmContract({
contract: myContract,
method: 'myMethod',
args: [arg1, arg2]
})
```
#### Transaction Management
```typescript
// Send transaction
const hash = await client.writeContract({
address: contractAddress,
abi: contractAbi,
functionName: 'myFunction',
args: [arg1, arg2]
})
// Wait for receipt
const receipt = await client.waitForTransactionReceipt({ hash })
```
### Actions API
The client includes several sets of actions:
#### Tevm Actions
* `tevmCall`: Low-level EVM call execution
* `tevmContract`: High-level contract interaction
* `tevmDeploy`: Contract deployment
* `tevmGetAccount`: Account state retrieval
* `tevmSetAccount`: Account state modification
* `tevmDeal`: Add native ETH or ERC20 tokens to an account
* `tevmMine`: Manual block mining
* `tevmDumpState`: State export
* `tevmLoadState`: State import
#### Viem Actions
* Public actions (e.g., `getBlockNumber`, `getBalance`)
* Wallet actions (e.g., `sendTransaction`, `signMessage`)
* Test actions (e.g., `setBalance`, `impersonateAccount`)
### Advanced Features
#### State Persistence
```typescript
import { createMemoryClient, createSyncPersister } from '@tevm/memory-client'
const client = createMemoryClient({
persister: createSyncPersister({
storage: localStorage
})
})
```
#### HTTP Server Integration
```typescript
import { createServer } from '@tevm/server'
import { createMemoryClient } from '@tevm/memory-client'
const client = createMemoryClient()
const server = createServer({
request: client.request
})
server.listen(8545)
```
### Network Support
Officially supported networks:
* Ethereum mainnet
* Standard OP Stack chains
Other EVM-compatible chains may work but are not officially supported.
### EIP Support
Always-enabled EIPs:
* EIP-1559 (Fee Market)
* EIP-4895
* EIP-4844 (Blob Transactions)
* EIP-4788
### Types
The package exports several important types:
```typescript
type MemoryClient
type MemoryClientOptions
type TevmActions
type TevmContract
type TevmRpcSchema
type TevmTransport
```
### License
This package is licensed under the MIT License.
### See Also
* [What is Tevm Node?](../introduction/what-is-tevm-node)
* [Creating a Node](../core/create-tevm-node)
* [Viem Documentation](https://viem.sh)
## @tevm/node API Reference
This documentation is generated from the source code. View the full API documentation on GitHub:
[View @tevm/node API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/globals.md)
### Quick Links
* [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/type-aliases)
* [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/classes)
* [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/functions)
* [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/variables)
### Return to Documentation
* [Back to Getting Started](/getting-started/overview)
* [Package Overview](/reference)
## @tevm/receipt-manager
The `@tevm/receipt-manager` package provides a robust transaction receipt management system for the Tevm blockchain. It handles the storage, retrieval, and management of transaction receipts, logs, and related data.
### Installation
```bash
npm install @tevm/receipt-manager
```
### Overview
The receipt manager package is responsible for:
* Managing transaction receipts and their storage
* Handling transaction logs and bloom filters
* Supporting different receipt types (Pre-Byzantium, Post-Byzantium, EIP4844)
* Providing efficient receipt lookup and retrieval
### Core Components
#### ReceiptsManager
The main class that handles receipt management operations.
```typescript
import { ReceiptsManager } from '@tevm/receipt-manager'
const receiptsManager = new ReceiptsManager(mapDb, chain)
```
##### Key Methods
* `saveReceipts(block, receipts)`: Saves receipts to the database
* `getReceipts(blockHash)`: Retrieves receipts for a given block hash
* `getReceiptByTxHash(txHash)`: Gets a receipt by transaction hash
* `getLogs(from, to, addresses?, topics?)`: Returns logs based on filter criteria
* `deleteReceipts(block)`: Removes receipts for a given block
#### Receipt Types
The package supports multiple receipt types to accommodate different Ethereum protocol versions:
##### BaseTxReceipt
```typescript
interface BaseTxReceipt {
cumulativeBlockGasUsed: bigint
bitvector: Uint8Array
logs: Log[]
}
```
##### PreByzantiumTxReceipt
```typescript
interface PreByzantiumTxReceipt extends BaseTxReceipt {
stateRoot: Uint8Array
}
```
##### PostByzantiumTxReceipt
```typescript
interface PostByzantiumTxReceipt extends BaseTxReceipt {
status: 0 | 1
}
```
##### EIP4844BlobTxReceipt
```typescript
interface EIP4844BlobTxReceipt extends PostByzantiumTxReceipt {
blobGasUsed: bigint
blobGasPrice: bigint
}
```
### Database Management
#### MapDb
The package includes a MapDb implementation for storing receipt data:
```typescript
import { createMapDb } from '@tevm/receipt-manager'
const mapDb = createMapDb({
cache: new Map()
})
```
#### Configuration Options
```typescript
interface MetaDBManagerOptions {
cache: Map<`0x${string}`, Uint8Array>
}
```
### Usage Examples
#### Saving and Retrieving Receipts
```typescript
// Save receipts
await receiptsManager.saveReceipts(block, receipts)
// Retrieve receipts by block hash
const receipts = await receiptsManager.getReceipts(blockHash)
// Get receipt by transaction hash
const receipt = await receiptsManager.getReceiptByTxHash(txHash)
```
#### Working with Logs
```typescript
// Query logs with filters
const logs = await receiptsManager.getLogs(
fromBlock,
toBlock,
addresses,
topics
)
```
### Constants and Limits
The ReceiptsManager includes several important limits:
```typescript
GET_LOGS_LIMIT = 10000 // Maximum number of logs to return
GET_LOGS_LIMIT_MEGABYTES = 150 // Size limit for getLogs response
GET_LOGS_BLOCK_RANGE_LIMIT = 2500 // Block range limit for getLogs
```
### Error Handling
The package includes proper error handling for common scenarios:
```typescript
try {
const receipt = await receiptsManager.getReceiptByTxHash(txHash)
if (!receipt) {
// Handle missing receipt
}
} catch (error) {
// Handle errors
}
```
### Types
The package exports several important types:
```typescript
type TxReceipt = PreByzantiumTxReceipt | PostByzantiumTxReceipt | EIP4844BlobTxReceipt
type TxReceiptWithType = PreByzantiumTxReceiptWithType | PostByzantiumTxReceiptWithType
type DbType = "Receipts" | "TxHash" | "SkeletonBlock" | "SkeletonBlockHashToNumber" |
"SkeletonStatus" | "SkeletonUnfinalizedBlockByHash" | "Preimage"
```
### License
This package is licensed under the MIT License. Some files are adapted from [ethereumjs](https://github.com/ethereumjs/ethereumjs-monorepo) and are licensed under the Mozilla Public License 2.0.
## @tevm/rlp API Reference
This documentation is generated from the source code. View the full API documentation on GitHub:
[View @tevm/rlp API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/globals.md)
### Quick Links
* [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/type-aliases)
* [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/classes)
* [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/functions)
* [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/variables)
### Return to Documentation
* [Back to Getting Started](/getting-started/overview)
* [Package Overview](/reference)
## @tevm/state
The `@tevm/state` package provides a robust state management system for Tevm, handling Ethereum account states, contract storage, and state transitions. It offers both synchronous and asynchronous APIs for managing the EVM state.
### Installation
```bash
npm install @tevm/state
```
### Overview
The state package is responsible for:
* Managing account states (balance, nonce, code, storage)
* Handling state transitions and checkpoints
* Caching and persistence of state data
* Supporting forked chain states
* Providing efficient state access and modification
### API Reference
#### Enumerations
* [CacheType](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/enumerations/CacheType.md) - Types of caching mechanisms
#### Core Types
##### State Management
* [StateManager](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/StateManager.md) - Main interface for state operations
* [BaseState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/BaseState.md) - Core state data structure
* [TevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/TevmState.md) - Tevm-specific state representation
* [StateAction](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateAction.md) - Type for state modification actions
* [StateOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateOptions.md) - State configuration options
* [StateRoots](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateRoots.md) - State roots mapping
* [ParameterizedTevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/ParameterizedTevmState.md) - Parameterized state type
##### Storage Types
* [AccountStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/AccountStorage.md) - Account storage structure
* [ParameterizedAccountStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/ParameterizedAccountStorage.md) - Parameterized account storage interface
* [SerializableTevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/SerializableTevmState.md) - Serializable state format
* [ForkOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/ForkOptions.md) - Options for forking state
##### Caching
* [StateCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateCache.md) - State caching structure
* [AccountCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/AccountCache.md) - Account-level cache
* [ContractCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/ContractCache.md) - Contract-level cache
* [StorageCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/StorageCache.md) - Storage-level cache
#### Core Functions
##### State Creation and Management
* [createStateManager](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/createStateManager.md) - Creates a new state manager instance
* [createBaseState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/createBaseState.md) - Creates the core state data structure
* [deepCopy](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/deepCopy.md) - Creates deep copy of state
* [shallowCopy](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/shallowCopy.md) - Creates shallow copy
##### State Operations
* [getAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAccount.md) - Retrieves account state
* [putAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putAccount.md) - Updates account state
* [deleteAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/deleteAccount.md) - Removes an account
* [getContractCode](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getContractCode.md) - Gets contract bytecode
* [putContractCode](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putContractCode.md) - Updates contract bytecode
* [getContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getContractStorage.md) - Gets contract storage
* [putContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putContractStorage.md) - Updates contract storage
* [clearContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/clearContractStorage.md) - Clears contract storage
* [getAccountAddresses](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAccountAddresses.md) - Gets account addresses
* [getAppliedKey](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAppliedKey.md) - Gets applied storage key
* [modifyAccountFields](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/modifyAccountFields.md) - Modifies account fields
##### State Root Management
* [getStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getStateRoot.md) - Gets current state root
* [setStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/setStateRoot.md) - Sets new state root
* [hasStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/hasStateRoot.md) - Checks if state root exists
##### Checkpointing and Committing
* [checkpoint](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/checkpoint.md) - Creates a state checkpoint
* [commit](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/commit.md) - Commits state changes
* [revert](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/revert.md) - Reverts to previous checkpoint
##### Cache Management
* [clearCaches](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/clearCaches.md) - Clears all state caches
* [originalStorageCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/originalStorageCache.md) - Manages original storage cache
##### Genesis and Forking
* [dumpCanonicalGenesis](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpCanonicalGenesis.md) - Dumps canonical genesis state
* [generateCanonicalGenesis](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/generateCanonicalGenesis.md) - Generates canonical genesis
* [getForkBlockTag](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getForkBlockTag.md) - Gets fork block tag
* [getForkClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getForkClient.md) - Gets fork client
##### Storage Operations
* [dumpStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpStorage.md) - Dumps storage state
* [dumpStorageRange](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpStorageRange.md) - Dumps storage range
* [getProof](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getProof.md) - Gets state proof
### Usage Examples
#### Creating a State Manager
```
```
## @tevm/trie API Reference
This documentation is generated from the source code. View the full API documentation on GitHub:
[View @tevm/trie API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/globals.md)
### Quick Links
* [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/type-aliases)
* [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/classes)
* [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/functions)
* [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/variables)
### Return to Documentation
* [Back to Getting Started](/getting-started/overview)
* [Package Overview](/reference)
## @tevm/tx
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/tx/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs) folder.
The `@tevm/tx` package provides a custom implementation of Ethereum transactions, extending the functionality of `@ethereumjs/tx`. It includes support for various transaction types and introduces impersonated transactions unique to Tevm.
### Installation
```bash
npm install @tevm/tx
```
### API Reference
#### Enumerations
* [Capability](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/enumerations/Capability.md) - Transaction capabilities
* [TransactionType](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/enumerations/TransactionType.md) - Supported transaction types
#### Classes
* [AccessListEIP2930Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/AccessListEIP2930Transaction.md) - Type 1 transaction implementation
* [BlobEIP4844Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/BlobEIP4844Transaction.md) - Type 3 transaction implementation
* [FeeMarketEIP1559Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/FeeMarketEIP1559Transaction.md) - Type 2 transaction implementation
* [LegacyTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/LegacyTransaction.md) - Legacy transaction implementation
* [TransactionFactory](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/TransactionFactory.md) - Factory for creating transactions
#### Interfaces
* [EIP1559CompatibleTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/EIP1559CompatibleTx.md) - EIP-1559 transaction interface
* [EIP4844CompatibleTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/EIP4844CompatibleTx.md) - EIP-4844 transaction interface
* [ImpersonatedTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/ImpersonatedTx.md) - Impersonated transaction interface
* [JsonRpcTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/JsonRpcTx.md) - JSON-RPC transaction format
* [JsonTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/JsonTx.md) - JSON transaction format
* [TxData](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/TxData.md) - Transaction data interface
* [TxOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/TxOptions.md) - Transaction options interface
#### Type Aliases
* [AccessList](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/AccessList.md) - Access list type definition
* [AccessListItem](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/AccessListItem.md) - Access list item type
* [TypedTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/TypedTransaction.md) - Union type of all transaction types
#### Functions
* [createImpersonatedTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/createImpersonatedTx.md) - Create an impersonated transaction
* [isAccessListEIP2930Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isAccessListEIP2930Tx.md) - Type guard for EIP-2930 transactions
* [isBlobEIP4844Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isBlobEIP4844Tx.md) - Type guard for EIP-4844 transactions
* [isFeeMarketEIP1559Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isFeeMarketEIP1559Tx.md) - Type guard for EIP-1559 transactions
* [isLegacyTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isLegacyTx.md) - Type guard for legacy transactions
### Main Components
#### Transaction Types
The package supports multiple transaction types:
* **LegacyTransaction**: Pre-EIP-2718 transactions
* **AccessListEIP2930Transaction**: Type 1 transactions with access lists (EIP-2930)
* **FeeMarketEIP1559Transaction**: Type 2 transactions with fee market (EIP-1559)
* **BlobEIP4844Transaction**: Type 3 transactions with blob data (EIP-4844)
* **ImpersonatedTx**: Tevm-specific transaction type for impersonating accounts
#### TransactionFactory
A utility class for creating transactions from various data formats:
```typescript
import { TransactionFactory } from '@tevm/tx'
// Create from serialized data
const tx = TransactionFactory.fromSerializedTx(serializedData)
// Create from RPC data
const tx = await TransactionFactory.fromRPC(rpcTxData)
// Create from block body data
const tx = TransactionFactory.fromBlockBodyData(blockData)
```
#### Impersonated Transactions
A unique feature of Tevm that allows simulating transactions as if they were sent from any address:
```typescript
import { createImpersonatedTx } from '@tevm/tx'
const tx = createImpersonatedTx({
impersonatedAddress: address,
to: recipient,
value: value,
data: data,
// ... other EIP-1559 transaction fields
})
```
### Transaction Types
#### Legacy Transactions
Pre-EIP-2718 transactions with basic fields:
```typescript
interface LegacyTxData {
nonce: bigint
gasPrice: bigint
gasLimit: bigint
to?: Address
value: bigint
data: Uint8Array
v?: bigint
r?: bigint
s?: bigint
}
```
#### EIP-2930 Transactions
Type 1 transactions with access lists:
```typescript
interface AccessListEIP2930TxData extends LegacyTxData {
chainId: bigint
accessList: AccessList
}
```
#### EIP-1559 Transactions
Type 2 transactions with fee market:
```typescript
interface FeeMarketEIP1559TxData extends AccessListEIP2930TxData {
maxFeePerGas: bigint
maxPriorityFeePerGas: bigint
}
```
#### EIP-4844 Transactions
Type 3 transactions with blob data:
```typescript
interface BlobEIP4844TxData extends FeeMarketEIP1559TxData {
maxFeePerBlobGas: bigint
blobVersionedHashes: Uint8Array[]
blobs?: Uint8Array[]
kzgCommitments?: Uint8Array[]
kzgProofs?: Uint8Array[]
}
```
### Common Operations
#### Creating Transactions
```typescript
import { TransactionFactory, LegacyTransaction } from '@tevm/tx'
// Using factory
const tx = TransactionFactory.fromTxData({
nonce: 0n,
gasPrice: 20000000000n,
gasLimit: 21000n,
to: '0x...',
value: 1000000000000000000n,
data: new Uint8Array()
})
// Direct instantiation
const legacyTx = new LegacyTransaction({
nonce: 0n,
gasPrice: 20000000000n,
gasLimit: 21000n,
to: '0x...',
value: 1000000000000000000n,
data: new Uint8Array()
})
```
#### Signing Transactions
```typescript
const signedTx = tx.sign(privateKey)
```
#### Transaction Methods
All transaction types provide common methods:
* `hash()`: Get transaction hash
* `getBaseFee()`: Get minimum required gas
* `getDataFee()`: Get gas cost for data
* `getUpfrontCost()`: Get total required balance
* `isSigned()`: Check if transaction is signed
* `serialize()`: Get RLP encoded transaction
* `toJSON()`: Get JSON representation
### Error Handling
The package includes custom error types:
```typescript
import { InvalidGasLimitError } from '@tevm/tx'
try {
const tx = createImpersonatedTx(txData)
} catch (e) {
if (e instanceof InvalidGasLimitError) {
// Handle invalid gas limit
}
}
```
### See Also
* [EIP-2718: Typed Transaction Envelope](https://eips.ethereum.org/EIPS/eip-2718)
* [EIP-2930: Optional access lists](https://eips.ethereum.org/EIPS/eip-2930)
* [EIP-1559: Fee market change](https://eips.ethereum.org/EIPS/eip-1559)
* [EIP-4844: Shard Blob Transactions](https://eips.ethereum.org/EIPS/eip-4844)
## @tevm/txpool
The `@tevm/txpool` package provides a transaction pool (mempool) implementation for Tevm, managing pending transactions and their lifecycle within the Ethereum Virtual Machine.
### Installation
```bash
npm install @tevm/txpool
```
### Overview
The transaction pool is responsible for:
* Managing pending transactions
* Validating transaction requirements
* Ordering transactions by price and nonce
* Handling transaction replacement
* Cleaning up stale transactions
* Supporting transaction lifecycle management
### API Reference
#### Core Class
##### TxPool
[`TxPool`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts) - The main transaction pool class with the following key features:
##### Constructor
```typescript
new TxPool(options: TxPoolOptions)
```
##### Properties
* `BLOCKS_BEFORE_TARGET_HEIGHT_ACTIVATION`: Number of blocks before chain head to start tx pool preparation
* `POOLED_STORAGE_TIME_LIMIT`: Number of minutes to keep txs in the pool
* `HANDLED_CLEANUP_TIME_LIMIT`: Number of minutes to forget about handled txs
* `pool`: The central pool dataset mapping addresses to transactions
* `running`: Boolean indicating if the pool is running
* `txsInPool`: Number of transactions currently in the pool
##### Methods
* [`add`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L310) - Adds a transaction to the pool
* [`addUnverified`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L280) - Adds an unverified transaction to the pool
* [`getByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L320) - Retrieves transactions by their hashes
* [`removeByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L341) - Removes a transaction by its hash
* [`txsByPriceAndNonce`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L477) - Returns transactions sorted by price and nonce
* [`cleanup`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L374) - Performs pool cleanup
* [`open`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L141) - Opens the transaction pool
* [`close`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L581) - Closes the transaction pool
* [`start`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L153) - Starts transaction processing
* [`stop`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L570) - Stops transaction processing
* [`deepCopy`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L128) - Creates a deep copy of the pool
### Usage Examples
#### Creating and Managing a Transaction Pool
```typescript
import { TxPool } from '@tevm/txpool'
import { createCommon } from '@tevm/common'
const common = createCommon({ chain: 'mainnet' })
const txPool = new TxPool({
common,
maxPoolSize: 5000,
minGasPriceBump: 10 // 10% price bump for replacement
})
// Start the pool
txPool.open()
txPool.start()
```
#### Adding Transactions
```typescript
// Add a new transaction
await txPool.add(transaction, {
requireSignature: true,
skipBalance: false
})
// Add an unverified transaction
await txPool.addUnverified(transaction)
```
#### Retrieving Transactions
```typescript
// Get transactions by hash
const txs = txPool.getByHash(txHashes)
// Get transactions by sender
const senderTxs = await txPool.getBySenderAddress(senderAddress)
// Get transactions ordered by price and nonce
const orderedTxs = await txPool.txsByPriceAndNonce({
baseFee: 1000000000n,
allowedBlobs: 3
})
```
#### Managing Transaction Lifecycle
```typescript
// Remove a transaction
txPool.removeByHash(txHash)
// Remove transactions included in new blocks
txPool.removeNewBlockTxs(newBlocks)
// Perform cleanup of stale transactions
txPool.cleanup()
```
#### Pool Lifecycle Management
```typescript
// Start the pool
txPool.open()
txPool.start()
// Stop the pool
txPool.stop()
txPool.close()
// Create a copy of the pool
const poolCopy = txPool.deepCopy(options)
```
### Configuration
The transaction pool can be configured with various options:
```typescript
interface TxPoolOptions {
common: Common
maxPoolSize?: number
minGasPriceBump?: number
minFeeBump?: number
maxPendingTotal?: number
maxPendingPerAccount?: number
maxQueuedTotal?: number
maxQueuedPerAccount?: number
minPendingNodeBalance?: bigint
minRemainingGasLimit?: bigint
}
```
#### Configuration Options
* `maxPoolSize`: Maximum number of transactions in the pool
* `minGasPriceBump`: Minimum price bump percentage for transaction replacement
* `minFeeBump`: Minimum fee bump for transaction replacement
* `maxPendingTotal`: Maximum number of pending transactions
* `maxPendingPerAccount`: Maximum pending transactions per account
* `maxQueuedTotal`: Maximum number of queued transactions
* `maxQueuedPerAccount`: Maximum queued transactions per account
* `minPendingNodeBalance`: Minimum balance required for pending transactions
* `minRemainingGasLimit`: Minimum gas limit for remaining transactions
### Error Handling
The transaction pool throws specific errors for various failure scenarios:
```typescript
try {
await txPool.add(transaction)
} catch (error) {
if (error.code === 'POOL_FULL') {
console.error('Transaction pool is full')
} else if (error.code === 'UNDERPRICED') {
console.error('Transaction is underpriced')
} else if (error.code === 'NONCE_TOO_LOW') {
console.error('Transaction nonce is too low')
}
}
```
### Best Practices
1. **Regular Cleanup**: Call `cleanup()` periodically to remove stale transactions
2. **Transaction Replacement**: Use appropriate gas price bumps for replacement transactions
3. **Pool Size Management**: Monitor and adjust pool size limits based on network conditions
4. **Error Handling**: Implement proper error handling for transaction additions and removals
5. **Lifecycle Management**: Properly manage pool lifecycle with `open()`, `start()`, `stop()`, and `close()`
### Related Packages
* [@tevm/vm](./vm) - Virtual Machine implementation
* [@tevm/state](./state) - State management
* [@tevm/common](./common) - Chain configuration
### License
MIT
## Utilities & Addresses
Tevm exports a set of lightweight utility functions and classes. Many of these build upon lower-level packages like [`tevm/utils`](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils) and [`viem`](https://viem.sh) while standardizing usage for Tevm Node. Below are the most commonly used.
### createAddress
Creates an [Ethereum address](https://ethereum.org/en/developers/docs/accounts/#account-creation) from various input formats:
```ts
import { createAddress } from 'tevm/address'
// Creates a TEVM-style Address object from various input forms
let addr = createAddress(`0x${"00".repeat(20)}`)
// from a hex string
addr = createAddress(420n)
// from a bigint
addr = createAddress(new Uint8Array(20))
// from a 20-byte array
```
#### Signature
```ts
declare function createAddress(
address: number | bigint | string | Uint8Array | EthjsAddress
): Address
```
#### Behavior & Notes
* Accepts various input types: [`0x`-prefixed hex strings](https://ethereum.org/en/developers/docs/data-structures-and-encoding/hex/), unprefixed hex, numbers, bigints, `Uint8Array`s, or `EthjsAddress`.
* Throws `InvalidAddressError` if it can't parse a valid 20-byte address from the input.
### Address Class
A thin wrapper around [`EthjsAddress`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util) offering a simple, consistent shape for Tevm Node. Created by `createAddress` or by forging your own:
```ts
import { Address } from 'tevm/address'
const a = new Address(Uint8Array.from([ /* 20 bytes */ ]))
console.log(a.bytes) // a raw 20-byte address
console.log(a.toString()) // 0x....
```
### createContractAddress
Creates a contract address following [EIP-1014](https://eips.ethereum.org/EIPS/eip-1014):
```ts
import { createContractAddress } from 'tevm/address'
// from an existing address + nonce, produce the CREATE address
const from = createAddress("0x1111...1111")
const nonce = 1n
const contractAddr = createContractAddress(from, nonce)
```
#### Usage
1. Follows standard Ethereum's [`keccak256(rlp([senderAddress, nonce]))[-20..]`](https://ethereum.org/en/developers/docs/smart-contracts/deploying/#how-to-deploy-a-smart-contract).
2. Ideal for simulating `CREATE` addresses in test or dev flows.
#### Error Handling
* Throws `InvalidAddressError` if the `from` address is invalid.
### Common Errors
* **InvalidAddressError**
Thrown when a string/bytes input fails to parse as a valid 20-byte address.
* **UnreachableCodeError**
Thrown internally if a code path was unexpectedly reached. Generally wrapped into a more descriptive error.
### Other Handy Utilities
Some additional lower-level utility re-exports from [`tevm/utils`](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils) or [`viem`](https://viem.sh):
* `hexToBytes(hex: string): Uint8Array`
Convert a hex string to raw bytes (with optional size checks).
* `keccak256(data: Uint8Array | HexString, 'bytes' | 'hex')`
A standard [keccak256](https://ethereum.org/en/developers/docs/smart-contracts/security/#keccak256-and-sha-3) hasher.
* `encodeFunctionData(...)`, `toRlp(...)`, etc.
Various encoding helpers used by Tevm Node internally.
> **Tip:** If you are building higher-level code in the browser or Node, you may prefer [`viem`](https://viem.sh)'s standardized utilities for bytes conversions, hashing, and ABIs. Tevm re-exports many of these for convenience.
## @tevm/utils
> **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/utils/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils/docs) folder.
The `@tevm/utils` package provides a comprehensive collection of utility functions and types for working with Ethereum data structures, encoding/decoding, and common operations. It combines functionality from various Ethereum libraries and adds Tevm-specific utilities.
### Installation
```bash
npm install @tevm/utils
```
### Main Components
#### Data Types and Encoding
##### Hex and Bytes Conversion
```typescript
import {
bytesToHex,
hexToBytes,
bytesToBigInt,
bytesToNumber,
hexToBigInt,
hexToNumber,
numberToHex,
stringToHex,
hexToString
} from '@tevm/utils'
// Convert bytes to hex
const hex = bytesToHex(new Uint8Array([1, 164])) // '0x01a4'
// Convert hex to bytes
const bytes = hexToBytes('0x01a4') // Uint8Array([1, 164])
// Convert to/from numbers
const num = hexToNumber('0x01a4') // 420
const hex2 = numberToHex(420) // '0x01a4'
// String conversion
const str = hexToString('0x48656c6c6f') // 'Hello'
const hex3 = stringToHex('Hello') // '0x48656c6c6f'
```
##### Type Checking
```typescript
import { isHex, isBytes, isAddress } from '@tevm/utils'
isHex('0x123') // true
isBytes(new Uint8Array()) // true
isAddress('0x123...') // true
```
#### Ethereum Specific
##### Unit Conversion
```typescript
import {
formatEther,
parseEther,
formatGwei,
parseGwei
} from '@tevm/utils'
// Convert wei to ether
formatEther(1000000000000000000n) // '1.0'
// Convert ether to wei
parseEther('1.0') // 1000000000000000000n
// Work with gwei
formatGwei(1000000000n) // '1.0'
parseGwei('1.0') // 1000000000n
```
##### Cryptographic Functions
```typescript
import {
keccak256,
ecrecover,
ecsign,
randomBytes
} from '@tevm/utils'
// Generate keccak256 hash
const hash = keccak256('0x1234')
// Sign data
const signature = ecsign(messageHash, privateKey)
// Recover address from signature
const address = ecrecover(messageHash, v, r, s)
// Generate random bytes
const random = randomBytes(32)
```
#### ABI Encoding/Decoding
```typescript
import {
encodeAbiParameters,
decodeAbiParameters,
encodeFunctionData,
decodeFunctionData,
encodeEventTopics,
decodeEventLog
} from '@tevm/utils'
// Encode function data
const data = encodeFunctionData({
abi: [...],
functionName: 'transfer',
args: [address, amount]
})
// Decode function data
const result = decodeFunctionData({
abi: [...],
data: '0x...'
})
// Work with events
const topics = encodeEventTopics({
abi: [...],
eventName: 'Transfer',
args: [from, to, null]
})
```
#### RLP Encoding/Decoding
```typescript
import { toRlp, fromRlp } from '@tevm/utils'
// Encode to RLP
const rlp = toRlp(['0x123', '0x456'])
// Decode from RLP
const decoded = fromRlp(rlp)
```
#### Memory Database
```typescript
import { createMemoryDb } from '@tevm/utils'
// Create an in-memory database
const db = createMemoryDb()
// Initialize with existing data
const initialData = new Map()
const db2 = createMemoryDb(initialData)
```
#### Event Emitter
```typescript
import { AsyncEventEmitter } from '@tevm/utils'
const emitter = new AsyncEventEmitter()
// Add listener
emitter.on('event', async (data) => {
// Handle event
})
// Emit event
await emitter.emit('event', data)
```
### Types
#### Basic Types
```typescript
import type {
Address,
Hex,
BlockTag,
BlockNumber,
BytesLike,
BigIntLike
} from '@tevm/utils'
// Example type usage
const address: Address = '0x...'
const hex: Hex = '0x...'
const blockTag: BlockTag = 'latest'
```
#### ABI Types
```typescript
import type {
Abi,
AbiFunction,
AbiEvent,
AbiConstructor,
ParseAbi,
FormatAbi
} from '@tevm/utils'
// Parse ABI
type ParsedAbi = ParseAbi
// Format ABI
type FormattedAbi = FormatAbi
```
#### Contract Types
```typescript
import type {
ContractFunctionName,
ContractConstructorArgs,
ExtractAbiFunction,
ExtractAbiEvent
} from '@tevm/utils'
// Extract function from ABI
type TransferFunction = ExtractAbiFunction
// Extract event from ABI
type TransferEvent = ExtractAbiEvent
```
### Constants
```typescript
import {
GWEI_TO_WEI,
KECCAK256_RLP,
KECCAK256_RLP_ARRAY
} from '@tevm/utils'
// Common conversion factors and constants
console.log(GWEI_TO_WEI) // 1000000000n
```
### Error Handling
The package uses the `@tevm/errors` package for standardized error handling:
```typescript
import { invariant } from '@tevm/utils'
// Assert conditions
invariant(condition, 'Error message')
```
### See Also
* [Viem Documentation](https://viem.sh/docs/utilities/fromBytes)
* [EthereumJS Util Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util)
* [ABI Specification](https://docs.soliditylang.org/en/latest/abi-spec.html)
## @tevm/vm
The `@tevm/vm` package provides a high-performance Ethereum Virtual Machine (EVM) implementation specifically designed for Tevm. It extends the functionality of the base EVM with additional features for testing, debugging, and development purposes.
### Installation
```bash
npm install @tevm/vm
```
### Overview
The VM package is a core component of Tevm that handles the execution of EVM bytecode, transaction processing, and block building. It provides a robust set of tools for:
* Executing EVM bytecode and smart contracts
* Processing transactions and blocks
* Managing state transitions
* Supporting various hardforks and EIPs
* Debugging and profiling execution
### API Reference
#### Core Types
##### Vm
The main VM type that extends the base VM with Tevm-specific functionality:
* [`Vm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/Vm.ts#L6) - Core VM type with methods for block building, transaction execution, and state management
##### Block Building
* [`BlockBuilder`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BlockBuilder.ts) - Class for building and managing blocks
* [`BuildBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/buildBlock.ts#L5) - Function type for block building operations
* [`BuildBlockOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BuildBlockOpts.ts) - Options for block building
* [`BuildStatus`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BuildStatus.ts) - Enumeration of block building states
##### Transaction Processing
* [`RunTx`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/runTx.ts) - Function type for transaction execution
* [`RunTxOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunTxOpts.ts) - Options for transaction execution
* [`RunTxResult`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunTxResult.ts) - Result of transaction execution
##### Block Processing
* [`RunBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/runBlock.ts) - Function type for block execution
* [`RunBlockOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunBlockOpts.ts) - Options for block execution
* [`RunBlockResult`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunBlockResult.ts) - Result of block execution
##### Events
* [`AfterTxEvent`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/events/AfterTxEvent.ts) - Event emitted after transaction execution
* [`AfterBlockEvent`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/events/AfterBlockEvent.ts) - Event emitted after block execution
* [`VMEvents`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/utils/VMEvents.ts) - VM event types and handlers
#### Core Functions
##### VM Creation and Management
* [`createVm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/createVm.js#L11) - Creates a new VM instance
* [`deepCopy`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/deepCopy.js#L20) - Creates a deep copy of a VM instance
##### Block Operations
* [`applyBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/applyBlock.ts#L24) - Applies a block to the current state
* [`buildBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/buildBlock.ts#L8) - Creates a new block builder instance
* [`genTxTrie`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/genTxTrie.ts#L6) - Generates transaction trie for a block
##### Transaction Operations
* [`validateRunTx`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/validateRunTx.js#L10) - Validates transaction parameters before execution
##### State Management
* [`applyDAOHardfork`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/applyDAOHardfork.js) - Applies the DAO hardfork state changes
* [`execHardfork`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/execHardfork.js#L14) - Executes hardfork-specific operations
### Usage Examples
#### Creating a VM Instance
```typescript
import { createVm } from '@tevm/vm'
import { Common } from '@tevm/common'
const common = new Common({ chain: 'mainnet' })
const vm = createVm({ common })
```
#### Building and Executing a Block
```typescript
import { createVm, BlockBuilder } from '@tevm/vm'
const vm = createVm({ /* options */ })
const blockBuilder = await vm.buildBlock({
parentBlock: block,
blockOpts: { /* options */ }
})
// Add transactions to the block
await blockBuilder.addTransaction(tx)
// Build and execute the block
const block = await blockBuilder.build()
const result = await vm.runBlock({ block })
```
#### Executing a Transaction
```typescript
import { createVm } from '@tevm/vm'
const vm = createVm({ /* options */ })
const txResult = await vm.runTx({ tx })
console.log('Gas used:', txResult.gasUsed.toString())
console.log('Return value:', txResult.execResult.returnValue)
```
### Error Handling
The VM package throws specific error types for different failure scenarios:
* Transaction execution errors (invalid nonce, insufficient balance, etc.)
* Block validation errors (invalid state root, gas limit, etc.)
* VM execution errors (out of gas, invalid opcode, etc.)
Example error handling:
```typescript
try {
await vm.runTx({ tx })
} catch (error) {
if (error.code === 'INVALID_OPCODE') {
console.error('Invalid operation in contract code')
} else if (error.code === 'OUT_OF_GAS') {
console.error('Transaction ran out of gas')
}
}
```
### Configuration
The VM can be configured with various options through the `VMOpts` interface:
```typescript
const vm = createVm({
common, // Chain configuration
stateManager, // Custom state manager
blockchain, // Custom blockchain
activatePrecompiles: true, // Activate precompiled contracts
// ... other options
})
```
### Related Packages
* [@tevm/state](./state) - State management for the VM
* [@tevm/common](./common) - Chain configuration and utilities
* [@tevm/blockchain](./blockchain) - Blockchain management
### License
MIT
## Tevm Contract Bundler
The Tevm Contract Bundler allows you to import Solidity files directly into your TypeScript code, enabling a seamless development experience with type safety and IDE integration.
:::note
Tevm Node is the primary product. Using the bundler is optional. You can also generate contract types with `npx tevm gen` (see [Codegen Approach](/reference/bundler/troubleshooting#codegen-approach)).
:::
### Quick Navigation
The bundler documentation is split into several sections for better organization:
* **[Overview](/reference/bundler/overview)** - Introduction, key benefits, available plugins
* **[Internals](/reference/bundler/internals)** - How the bundler works under the hood
* **[Methods & Exports](/reference/bundler/methods)** - Key APIs for advanced usage
* **[Troubleshooting](/reference/bundler/troubleshooting)** - Common issues and solutions
### What is the Tevm Bundler?
The Tevm bundler transforms Solidity `.sol` files into TypeScript modules at build time. When you import a contract, the bundler:
1. Reads and compiles the Solidity source code
2. Extracts the ABI, bytecode, and other contract information
3. Generates a TypeScript module with a [Tevm Contract](/reference/contract) instance
This means you can interact with your contracts in a fully type-safe way, with editor features like auto-completion, go-to-definition, and inline documentation.
### Getting Started
For a quick introduction to using the bundler, see the [Bundler Quickstart](/getting-started/bundler) guide.
If you're looking for detailed reference information, explore the sections above.
### Coming Soon Features
Tevm is actively developing new bundler features:
* **Inline Solidity with `sol` Tag** - Define contracts directly in your JS/TS using template literals
* **CAIP-10 Contract Imports** - Import contracts from any chain using standardized identifiers
Learn more about these upcoming features in the [Upcoming Features](/reference/bundler/overview#coming-soon-features) section.
## Bundler Internals
This page explains the internal workings of the Tevm bundler to help you understand how it transforms Solidity contracts into TypeScript modules.
### Architecture Overview
All Tevm bundler plugins share a unified core (`@tevm/base-bundler`). This architecture ensures consistent behavior across different build tools while allowing each plugin to adapt to its specific environment.
The bundler pipeline consists of several key stages:
### 1. Import Detection & Resolution
When the bundler encounters a `.sol` import, it:
* Parses the import statement using regex to extract the import path
* Converts relative paths to absolute paths
* Applies custom remappings from various sources (tsconfig.json paths, foundry remappings, tevm.config.json)
* Handles node\_modules resolution for npm packages
* Supports special import formats like `.s.sol` for bytecode inclusion
The import resolution mechanism merges multiple sources of configuration:
```
User Import โ Node.js Resolution + Foundry Remappings โ Absolute File Path
```
This allows the bundler to support both traditional JavaScript module resolution and Solidity/Foundry-style import resolution simultaneously.
### 2. Compilation
Once all imports are resolved, the bundler:
* Creates a dependency graph of all imported Solidity files
* Passes the full source code and import structure to solc (the Solidity compiler)
* If the file ends in `.s.sol`, both ABI and bytecode are generated; otherwise only ABI is produced
* Extracts metadata, NatSpec documentation, and optionally the AST (Abstract Syntax Tree)
The compilation step handles error reporting and version management for the Solidity compiler.
### 3. Code Generation
After compilation, the bundler:
* Generates a TypeScript (or JavaScript) module that exports a Tevm Contract instance
* Creates type definitions for all contract methods, events, and errors
* Maps Solidity types to TypeScript types (uint256 โ bigint, address โ string, etc.)
* Adds metadata, ABI, and optionally bytecode as properties of the exported contract
* Generates `.read` and `.write` method interfaces for type-safe contract interaction
The code generation step preserves NatSpec documentation as JSDoc comments, allowing editors to display contract documentation on hover.
### 4. Caching
To improve build performance, the bundler:
* Stores compiled artifacts in a `.tevm` directory
* Caches based on file content hashes, not just timestamps
* Avoids unnecessary recompilation when source files haven't changed
* Stores intermediate artifacts (ABI, bytecode) separately from generated code
* Handles cache invalidation when dependencies or compiler settings change
The caching system balances build speed with correctness by selectively invalidating only affected parts of the cache.
### 5. LSP & TS Plugin Integration
The final piece of the bundler architecture is the TypeScript Language Service Plugin:
* The `@tevm/ts-plugin` hooks into the TypeScript compiler
* References bundler outputs to provide type information for `.sol` imports
* Enables advanced IDE features:
* Contract-level auto-completion
* Go-to-definition for contract methods
* Hover documentation showing NatSpec comments
* Type checking for contract method arguments and return values
The plugin bridges the gap between the build-time bundler process and the development-time editor experience.
### Internal Implementation Details
#### Module Factory
The bundler uses a module factory pattern to create a dependency graph of all Solidity files:
```ts
// Internal implementation (simplified)
function moduleFactory(entry, source, remappings, libs) {
const modules = new Map()
const queue = [{ path: entry, source }]
while (queue.length > 0) {
const { path, source } = queue.shift()
if (modules.has(path)) continue
// Find all imports in the source
const imports = resolveImports(path, source, remappings, libs)
// Add to module map
modules.set(path, {
id: path,
source,
imports: imports.map(i => i.path)
})
// Queue imports for processing
for (const imp of imports) {
if (!modules.has(imp.path)) {
queue.push({ path: imp.path, source: readFile(imp.path) })
}
}
}
return modules
}
```
#### Solidity Compiler Integration
The bundler interfaces with the Solidity compiler through a standardized API:
```ts
// Internal implementation (simplified)
function compile(sources, settings) {
const input = {
language: 'Solidity',
sources: Object.fromEntries(
Array.from(sources.entries()).map(
([path, { content }]) => [path, { content }]
)
),
settings: {
outputSelection: {
'*': {
'*': ['abi', 'evm.bytecode', 'evm.deployedBytecode', 'metadata', 'userdoc', 'devdoc']
}
},
...settings
}
}
return solc.compile(JSON.stringify(input), { import: resolveImport })
}
```
#### Contract Instance Generation
The bundler generates a TypeScript module that exports a Tevm Contract instance:
```ts
// Generated code (simplified)
import { createContract } from 'tevm/contract'
export const MyContract = createContract({
abi: [...], // ABI from compiler
bytecode: '0x...', // Optional, only for .s.sol files
deployedBytecode: '0x...', // Optional, only for .s.sol files
})
// Strongly typed methods available via:
// MyContract.read.methodName
// MyContract.write.methodName
```
### Further Reading
* [Methods & Exports](/reference/bundler/methods) - Learn about the key APIs exposed by the bundler
* [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler
## Notable Methods & Exports
Beyond the high-level bundler plugins, Tevm exposes several internal utility methods and functions for advanced use cases. These are useful if you're building custom bundling workflows, debugging compilation issues, or extending Tevm's functionality.
### Core Bundler (`@tevm/base-bundler`)
The base bundler handles the complete process of transforming Solidity files into JavaScript/TypeScript modules:
```ts
import { bundler } from '@tevm/base-bundler'
import { createCache } from '@tevm/bundler-cache'
import { createSolc } from '@tevm/solc'
import { loadConfig } from '@tevm/config'
// Create a bundler instance
const tevmBundler = bundler(
config, // from loadConfig()
console, // logger
fileAccessObj, // file system access
solcInstance, // from createSolc()
cacheInstance, // from createCache()
)
// Convert a Solidity file to TypeScript
const { code, modules } = await tevmBundler.resolveTsModule(
'MyContract.s.sol',
process.cwd(),
false, // includeAst
true // includeBytecode
)
```
The `bundler()` factory creates an object with methods for different output formats:
* `resolveTsModule` - Generates TypeScript
* `resolveEsmModule` - Generates ES modules
* `resolveCjsModule` - Generates CommonJS
* `resolveDts` - Generates TypeScript declarations
Each method also has a synchronous version with `Sync` suffix (e.g., `resolveTsModuleSync`).
[Source: bundler.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/base-bundler/src/bundler.js)
### Import Resolution (`@tevm/resolutions`)
The resolutions package provides tools for parsing and resolving Solidity import statements:
```ts
import { moduleFactory, resolveImports } from '@tevm/resolutions'
// Find all imports in a Solidity file
const imports = await resolveImports(
'/path/to/Contract.sol', // absolute path
contractSourceCode, // raw source code
{ '@openzeppelin/': 'node_modules/@openzeppelin/' }, // remappings
['lib', 'node_modules'], // library search paths
false // async mode
)
// Build a module dependency graph
const modules = await moduleFactory(
'/path/to/Contract.sol',
contractSourceCode,
remappings,
libraryPaths,
fileAccessObj,
false // async mode
)
```
* `resolveImports()` - Parses import statements and resolves them to absolute file paths
* `moduleFactory()` - Builds a complete dependency graph of Solidity modules
[Source: moduleFactory.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/resolutions/src/moduleFactory.js) |
[Source: resolveImports.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/resolutions/src/resolveImports.js)
### Compilation (`@tevm/compiler`)
The compiler package handles the interaction with the Solidity compiler:
```ts
import { resolveArtifacts } from '@tevm/compiler'
// Compile a Solidity file and generate artifacts
const { artifacts, modules, asts } = await resolveArtifacts(
'MyContract.s.sol', // entry point
process.cwd(), // base directory
console, // logger
config, // compiler config
true, // include AST
true, // include bytecode
fileAccessObj, // file access
solcInstance // solc compiler instance
)
// Result includes:
console.log(artifacts) // Contract artifacts with ABI, bytecode, etc.
console.log(modules) // Module dependency graph
console.log(asts) // Abstract Syntax Tree (if requested)
```
* `resolveArtifacts()` - Compiles a Solidity file and its dependencies into ABI, bytecode, and AST
* Also has a synchronous version: `resolveArtifactsSync()`
[Source: resolveArtifacts.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/compiler/src/resolveArtifacts.js)
### Caching (`@tevm/bundler-cache`)
The cache system improves build performance by storing compiled artifacts:
```ts
import { createCache } from '@tevm/bundler-cache'
// Create a cache instance
const cache = createCache(
'.tevm', // cache directory
fileAccessObj, // file system access
process.cwd() // current working directory
)
// Read/write artifacts and generated code
await cache.writeArtifacts(contractPath, compiledContracts)
const artifacts = await cache.readArtifacts(contractPath)
await cache.writeDts(contractPath, dtsContent)
const dts = await cache.readDts(contractPath)
```
The cache provides methods for reading and writing various types of files:
* `readArtifacts`/`writeArtifacts` - Compiled contract artifacts (ABI, bytecode, etc.)
* `readDts`/`writeDts` - TypeScript declaration files
* `readMjs`/`writeMjs` - ESM JavaScript files
* `readCjs`/`writeCjs` - CommonJS JavaScript files
Each method also has a synchronous version with `Sync` suffix.
[Source: createCache.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/bundler-cache/src/createCache.js)
### Configuration (`@tevm/config`)
The config package handles loading and merging configuration from various sources:
```ts
import { loadConfig } from '@tevm/config'
// Load configuration from tevm.config.json, tsconfig.json, foundry.toml, etc.
const config = await loadConfig(process.cwd())
console.log(config.remappings) // Merged remappings from all sources
console.log(config.libs) // Library search paths
console.log(config.cacheDir) // Cache directory
```
* `loadConfig()` - Loads and merges configuration from multiple sources
* Handles Foundry remappings, TypeScript config, and tevm.config.json
[Source: loadConfig.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/config/src/loadConfig.js)
### Runtime (`@tevm/runtime`)
The runtime package generates the actual TypeScript/JavaScript code for compiled Solidity contracts:
```ts
import { generateTevmBody, generateTevmBodyDts } from '@tevm/runtime'
// Generate TypeScript implementation
const tsCode = generateTevmBody(
'MyContract', // contract name
artifacts, // compilation artifacts
'tevm/contract', // import path for createContract
true, // include bytecode
)
// Generate TypeScript declaration
const dtsCode = generateTevmBodyDts(
'MyContract', // contract name
artifacts, // compilation artifacts
'tevm/contract', // import path for createContract
true, // include bytecode
)
```
* `generateTevmBody()` - Generates TypeScript/JavaScript implementation
* `generateTevmBodyDts()` - Generates TypeScript declaration file (.d.ts)
[Source: generateTevmBody.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/runtime/src/generateTevmBody.js) |
[Source: generateTevmBodyDts.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/runtime/src/generateTevmBodyDts.js)
### Creating a Custom Bundler
If you need to create a custom bundler pipeline, you can combine these utilities:
```ts
import { loadConfig } from '@tevm/config'
import { createSolc } from '@tevm/solc'
import { createCache } from '@tevm/bundler-cache'
import { bundler } from '@tevm/base-bundler'
import { resolveArtifacts } from '@tevm/compiler'
import fs from 'node:fs/promises'
// Step 1: Create a file access object
const fileAccess = {
readFile: (path, enc) => fs.readFile(path, { encoding: enc }),
writeFile: fs.writeFile,
// Add other required methods...
}
// Step 2: Load configuration
const config = await loadConfig(process.cwd())
// Step 3: Create a cache
const cache = createCache('.tevm', fileAccess, process.cwd())
// Step 4: Create a solc instance
const solc = await createSolc()
// Step 5: Create a bundler
const myBundler = bundler(config, console, fileAccess, solc, cache)
// Step 6: Process a Solidity file
const result = await myBundler.resolveTsModule(
'path/to/Contract.sol',
process.cwd(),
false,
true
)
// Step 7: Use the generated code
console.log(result.code) // TypeScript code for Contract
```
### Further Reading
* [Bundler Internals](/reference/bundler/internals) - Learn how the bundler works under the hood
* [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler
* [GitHub Repository](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages) - Source code for all bundler packages
## Bundler Overview
### Key Benefits
The Tevm bundler offers numerous advantages for Ethereum developers:
* **Direct Imports**: Import Solidity files just like any other TypeScript module
* **Type Safety**: Full TypeScript type checking for contract methods, arguments, and return values
* **IDE Integration**: Go-to-definition, auto-completion, and hover documentation
* **NatSpec Support**: Contract documentation appears in your editor
* **Hot Module Reloading**: Changes to Solidity files update immediately during development
* **Framework Integration**: Works with popular bundlers like Vite, Webpack, Next.js, Bun, and more
### Available Plugins
Tevm provides plugins for most popular JavaScript bundlers:
| Bundler | Plugin Import Path | Repository |
| ------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
| Vite | `tevm/bundler/vite-plugin` | [@tevm/vite-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/vite) |
| Webpack | `tevm/bundler/webpack-plugin` | [@tevm/webpack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/webpack) |
| Rollup | `tevm/bundler/rollup-plugin` | [@tevm/rollup-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rollup) |
| ESBuild | `tevm/bundler/esbuild-plugin` | [@tevm/esbuild-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/esbuild) |
| Bun | `tevm/bundler/bun-plugin` | [@tevm/bun-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/bun) |
| Rspack | `tevm/bundler/rspack-plugin` | [@tevm/rspack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rspack) |
All plugins share a similar usage pattern and a single configuration interface. For detailed plugin configuration, see the [Quickstart Guide](/getting-started/bundler).
### Prerequisites & Key Points
#### Optional Bundler
You can compile contracts manually or use the `tevm generate contract` command. The plugins are purely optional if you prefer a different workflow.
```bash
# Generate TypeScript types from all Solidity files
tevm generate contract
# Generate types for specific contracts
tevm generate contract "ERC20Token"
# Specify custom include pattern and output directory
tevm generate contract --include "contracts/**/*.sol" --output "types/"
```
#### TypeScript Integration
For best results, add the Tevm TS plugin to your tsconfig.json. This allows your editor to resolve Solidity imports, provide NatSpec docs on hover, go-to-definition on solidity methods, and more.
#### .s.sol for Bytecode
By default, Tevm only generates bytecode for Solidity files ending in `.s.sol`. Regular `.sol` files still compile for ABIs, but omit deployable bytecode. This helps differentiate purely interface-like contracts from ones you intend to deploy or call as scripts.
#### Tevm Cache
Compiled artifacts and metadata are stored in a `.tevm` folder. It is strongly recommended to `.gitignore` this directory. The cache directory is ephemeral - you can safely delete it if you encounter stale builds or caching issues.
#### Foundry/Remappings
If you have a Foundry setup or custom remappings, you can enable them in a `tevm.config.json`. For detailed configuration, see the [Bundler Configuration](#bundler-configuration) section.
#### Next.js
Next.js can conflict with the TS plugin's type-checking. If you wish to keep type-checking turned on for Next.js, consider the [Codegen Approach](/reference/bundler/troubleshooting#codegen-approach). Otherwise, you may disable type-checking in your Next config.
### Basic Usage
Once configured, you can import Solidity contracts directly:
```ts
// Import a contract directly from a .sol file
import { Counter } from './Counter.sol'
import { createMemoryClient } from 'tevm'
// Create a client to interact with the contract
const client = createMemoryClient()
// Deploy the contract
const deployed = await client.deployContract(Counter)
// Call read methods
const count = await deployed.read.count()
// Call write methods
const tx = await deployed.write.increment()
await client.mine({ blocks: 1 })
// Check the updated state
const newCount = await deployed.read.count()
```
The imported `Counter` object is a [Tevm Contract](/reference/contract) instance that provides:
* Type-safe access to all contract methods
* Contract ABI and bytecode
* Deployment helpers
* Read and write method interfaces
* Event filters
### Importing Dependencies
You can import contracts from:
* Local `.sol` files in your project
* npm packages like OpenZeppelin contracts
* External libraries
```ts
// Local import
import { MyToken } from './contracts/MyToken.sol'
// npm package import
import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'
```
### Integration with Viem and Ethers
Tevm contracts work seamlessly with popular Ethereum libraries:
#### Viem Example
```ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { MyToken } from './contracts/MyToken.sol'
const client = createPublicClient({
chain: mainnet,
transport: http(),
})
// Read methods
const balance = await client.readContract({
...MyToken.read.balanceOf('0x123...'),
address: '0x456...',
})
// Write methods
const hash = await client.writeContract({
...MyToken.write.transfer('0x789...', 1000n),
address: '0x456...',
})
```
#### Ethers Example
```ts
import { ethers } from 'ethers'
import { MyToken } from './contracts/MyToken.sol'
const provider = new ethers.BrowserProvider(window.ethereum)
const signer = await provider.getSigner()
// Create contract instance
const tokenContract = new ethers.Contract(
'0x456...',
MyToken.abi,
signer
)
// Call methods
const balance = await tokenContract.balanceOf('0x123...')
const tx = await tokenContract.transfer('0x789...', 1000n)
```
### Bundler Configuration
For complex projects, create a `tevm.config.json` file in your project root:
```json
{
"foundryProject": true,
"libs": ["lib", "node_modules"],
"remappings": {
"@openzeppelin/": "node_modules/@openzeppelin/"
},
"debug": false,
"cacheDir": ".tevm",
"jsonAsConst": ["**/*.abi.json"]
}
```
#### Configuration Options
| Option | Type | Description |
| ---------------- | ------------------------ | ---------------------------------------------------------------- |
| `foundryProject` | `boolean \| string` | Enable Foundry integration (`true`) or path to foundry.toml |
| `libs` | `string[]` | Library paths for Solidity imports (used alongside Foundry libs) |
| `remappings` | `Record` | Custom import remappings |
| `debug` | `boolean` | Output extra debug logs and files in `.tevm` |
| `cacheDir` | `string` | Location for compiled artifacts (default: `.tevm`) |
| `jsonAsConst` | `string \| string[]` | Glob patterns for JSON files to be typed as const in TS |
### Coming Soon Features
#### Inline Solidity with `sol` Tag
:::info
This feature is coming soon and is not yet available in the current release.
:::
For quick prototyping or one-off contracts, you'll soon be able to use the `sol` tag to define Solidity code inline:
```ts
import { sol } from 'tevm'
// Define contract inline
const { bytecode, abi } = sol`
pragma solidity ^0.8.19;
contract Counter {
uint256 private count = 0;
function increment() public {
count += 1;
}
function count() public view returns (uint256) {
return count;
}
}
`
// Use with client.readContract, writeContract, etc.
```
#### Network Imports via Macros
:::info
This feature is coming soon and is not yet available in the current release.
:::
Tevm will soon support importing contract ABIs from any blockchain network using macros. This build-time solution offers several advantages over runtime imports, including stronger type safety and better performance:
```ts
// Create a contract macro function
// contract-macros.js
import { createMemoryClient } from 'tevm'
import { http } from 'viem'
import { mainnet } from 'viem/chains'
import { loadContract } from 'tevm'
import { SourcifyABILoader, EtherscanABILoader } from 'tevm/whatsabi'
// For hermetic builds, use a memory client with fixed block height
const client = createMemoryClient({
fork: {
transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY'),
blockNumber: 19000000n // Pin to specific block for reproducible builds
}
})
// Export a function that returns a contract - one function per contract
export async function wethContract() {
// Uses Contract Loader to fetch and analyze contract data
return loadContract(client, {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
followProxies: true,
loaders: [
new SourcifyABILoader(),
new EtherscanABILoader({ apiKey: 'YOUR_ETHERSCAN_KEY' })
]
})
}
// Import the contract with import attributes
// app.js
import { wethContract } from './contract-macros.js' with { type: 'macro' }
```
Macros must be enabled in your `tevm.config.json` file for security reasons:
```json
{
"macros": true,
"foundryProject": true,
"libs": ["lib", "node_modules"]
}
```
For complete documentation about this feature, see the [Contract Loader](/api/whatsabi-integration#network-imports-via-macros) guide.
### Further Reading
* [Bundler Internals](/reference/bundler/internals) - Learn how the bundler works under the hood
* [Methods & Exports](/reference/bundler/methods) - Advanced APIs for custom implementations
* [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler
## Bundler Troubleshooting
This guide addresses common issues that may arise when using the Tevm bundler.
### Common Issues
#### 1. Missing or Red Underlines in Editor
**Symptoms**: Your editor shows red underlines for Solidity imports, or auto-completion doesn't work.
**Solution**:
* Confirm you have `"plugins": [{ "name": "@tevm/ts-plugin" }]` in tsconfig.json
* In VS Code or Cursor, switch to the "workspace version" of TypeScript:
1. Open Command Palette (Ctrl+Shift+P or Cmd+Shift+P)
2. Type "TypeScript: Select TypeScript Version"
3. Select "Use Workspace Version"
* For Vim, Neovim, and other editors, ensure they're using the workspace TypeScript version
#### 2. Type-check Errors with Next.js
**Symptoms**: Your build works, but Next.js type-checking fails with errors about `.sol` imports.
**Solution**:
* Next's built-in type checker might not handle dynamic `.sol` imports well
* Option 1: Disable type-checking in next.config.mjs:
```js
// next.config.mjs
export default {
typescript: {
// Typechecking will only be available after the LSP is migrated to volar
// Until then typechecking will work in editor but not during a next.js build
ignoreBuildErrors: true,
}
}
```
* Option 2: Use the [Codegen Approach](#codegen-approach) (recommended for Next.js)
#### 3. "File Not Found" Errors
**Symptoms**: Bundler can't find imported Solidity files, especially with custom paths.
**Solution**:
* Check that your libraries or local imports are accounted for in libs or remappings
* If you're using Foundry, ensure `foundryProject: true` is set in tevm.config.json
* For npm packages, verify they're installed and the import path is correct
* For complex import structures, add explicit remappings:
```json
// tevm.config.json
{
"remappings": {
"@customlib/": "node_modules/@customlib/",
"local/": "./contracts/"
}
}
```
#### 4. Cache Stale Issues
**Symptoms**: Changes to Solidity files don't appear to take effect.
**Solution**:
* If a contract's changes don't appear to be recognized, remove the `.tevm` folder and rebuild
* The `.tevm` directory is ephemeral - you can safely delete it at any time
* Add `.tevm` to your `.gitignore` to prevent caching issues in version control
#### 5. No Bytecode Available
**Symptoms**: Contract deployment fails with errors about missing bytecode.
**Solution**:
* Check that you're using the `.s.sol` extension for contracts you want to deploy
* Regular `.sol` files only generate ABIs, not deployable bytecode
* Rename your file from `Contract.sol` to `Contract.s.sol` if you need bytecode
#### 6. Deployment Errors
**Symptoms**: Contract deployment fails even with bytecode available.
**Solution**:
* Make sure you're calling `.deploy()` with any required constructor arguments:
```ts
// Incorrect (when contract has constructor args)
const deployed = await client.deployContract(MyToken)
// Correct
const deployed = await client.deployContract(
MyToken.deploy("TokenName", "TKN", 18)
)
```
#### 7. Test Runner Issues
**Symptoms**: Tests using `.sol` imports fail in Jest or other test runners.
**Solution**:
* Most test runners (Vitest) work out-of-the-box once the bundler plugin is configured
* For Jest, you might need extra configuration or use the codegen approach
* Consider using the bundler that matches your test environment (esbuild for Vitest, etc.)
### Codegen Approach
If you're encountering persistent bundler-related issues, particularly with frameworks like Next.js, or if you prefer to have explicit TypeScript files for your contracts, the codegen approach may be better suited for your needs.
#### What is Codegen?
The codegen approach generates `.ts` files from your `.sol` files ahead of time, rather than during the build process. This results in regular TypeScript files that can be imported normally.
#### Using Codegen
To generate TypeScript files for your Solidity contracts using the Tevm CLI:
```bash
# Generate TypeScript from all Solidity files
tevm generate contract
# Generate types for specific contracts
tevm generate contract "ERC20Token"
# Customize input and output patterns
tevm generate contract --include "contracts/**/*.sol" --output "types/"
# Force overwrite existing files
tevm generate contract --force
```
This will:
* Generate `.ts` files next to each `.sol` file (or wherever configured)
* Create TypeScript files that you can commit to source control
* Produce the same output as the bundler, but in a pre-build step
These generated `.ts` files can be imported directly in your code:
```ts
// Import the generated TypeScript file
import { MyContract } from './contracts/MyContract.sol.ts'
```
For full details on the available options, run:
```bash
tevm generate contract --help
```
#### When to Use Codegen
Codegen is especially helpful when:
* You have a framework that tightly controls its build pipeline (e.g., Next.js with enforced type-checking)
* You prefer explicit, committed TypeScript artifacts for contracts
* You want a stable CI pipeline or want to avoid runtime resolution
* You're using Jest or another test runner that has difficulty with dynamic imports
#### Codegen Configuration
You can configure the codegen by using command-line options, or by adding options to your `tevm.config.json`:
```json
{
"gen": {
"outDir": "./generated", // Where to generate files
"patterns": ["contracts/**/*.sol"], // Which files to process
"exclude": ["**/node_modules/**"] // Which files to ignore
}
}
```
Alternatively, you can specify options directly on the command line:
```bash
tevm generate contract --include "contracts/**/*.sol" --output "./generated" --verbose
```
### Additional Resources
If you're still experiencing issues:
* Check the [GitHub Issues](https://github.com/evmts/tevm-monorepo/issues) for similar problems and solutions
* Look at the [Examples](https://github.com/evmts/tevm-monorepo/tree/main/examples) for working configurations
* For Next.js specific issues, see the [Next.js example](https://github.com/evmts/tevm-monorepo/tree/main/examples/next) in the Tevm repository
### Further Reading
* [Bundler Overview](/reference/bundler/overview) - Key benefits and features
* [Bundler Internals](/reference/bundler/internals) - How the bundler works
* [Methods & Exports](/reference/bundler/methods) - Advanced APIs and utilities