Getting Started with Viem
This guide will help you integrate Tevm with viem, 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
Install Dependencies
First, install Tevm along with viem as a peer dependency:
npm install tevm viem@latest
Create Your Client
For the quickest start, create a memory client:
import { createMemoryClient } from "tevm";
const client = createMemoryClient();
Or, to fork from an existing chain:
import { createMemoryClient } from "tevm";
import { optimism } from "tevm/chains";
import { http } from "viem";
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();
You're Ready!
Start using your client with familiar viem actions:
// 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:
import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/common";
import { parseAbi, parseEther } from "viem";
// 1. Create a memory client forked from Optimism mainnet
const client = createMemoryClient({
fork: {
transport: http("https://mainnet.optimism.io"),
common: optimism,
},
});
// 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)",
"function setGreeting(string memory _greeting) public",
]);
// 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({
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 });
// Verify the greeting was updated
const updatedGreeting = await client.readContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "greet",
});
console.log(`Updated greeting: ${updatedGreeting}`);
Code Walkthrough
1. Imports & Client Creation
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
// 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
// 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:
// 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
Tree-Shakeable API
For production applications, especially in browser environments, you may want to use Tevm's tree-shakeable API to minimize bundle size:
import { createClient, http } from "viem";
import { createTevmTransport } from "tevm/transport";
import { tevmCall, tevmDumpState } from "tevm/actions";
// Create a standard viem client with Tevm transport
const client = createClient({
transport: createTevmTransport({
fork: {
transport: http("https://mainnet.optimism.io"),
},
}),
});
// Import only the actions you need
await tevmDumpState(client);
EVM Execution Hooks
One of Tevm's most powerful features is the ability to hook directly into EVM execution:
await client.tevmContract({
address: greeterContractAddress,
abi: greeterAbi,
functionName: "setGreeting",
args: ["Hello!"],
// onStep is called for each EVM operation
onStep: (stepInfo, next) => {
console.log(`Executing: ${stepInfo.opcode.name} at PC=${stepInfo.pc}`);
console.log(`Stack: ${stepInfo.stack.map((val) => val.toString())}`);
console.log(`Memory: ${stepInfo.memory.toString("hex")}`);
// You can also modify EVM state here if needed
// Call next() to continue execution
next?.();
},
// 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
Tevm-Specific Actions
Tevm extends viem with powerful specialized actions, all prefixed with tevm
. These actions provide direct access to Tevm's enhanced Ethereum Virtual Machine capabilities, giving you unprecedented control and visibility into blockchain execution.
The Power of tevmContract
Among all Tevm actions, tevmContract
stands out as the most versatile and powerful. It can do almost anything with the Tevm EVM, combining the simplicity of high-level contract interactions with the depth of low-level EVM control:
- Complete EVM Access: Hook into any phase of EVM execution at the opcode level
- Rich Result Data: Get detailed execution results beyond just the return value
- Execution Visibility: Capture traces, access lists, gas usage, and more
- Flexible Contract Interface: Work with contracts using address+ABI or directly with bytecode
- Layer 2 Awareness: Get L1 fee calculations and gas usage for L2 chains
When you need maximum control and insight into contract execution, tevmContract
is your go-to action. It seamlessly handles everything from simple function calls to complex debugging scenarios.
Complete Action Reference
Here's the full list of Tevm-specific actions:
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 |
tevmEnableTracing | Enable detailed execution tracing | Capture full execution traces |
tevmReset | Reset node to initial state | Start fresh for new test scenarios |
Next Steps
Now that you're familiar with using Tevm with viem, you can:
Explore More Tevm Features
Dive deeper into Tevm's powerful capabilities:
- Forking capabilities to simulate production chains
- State management for manipulating the blockchain
- Mining modes for controlling transaction inclusion
- Direct Solidity imports with the Tevm Bundler
Check Out Examples
See how Tevm solves real-world problems:
- Forking mainnet for production simulation
- Building a debugger UI with EVM insights
- Local testing flows for development
Advanced API Usage
Master the Tevm API for more sophisticated applications:
- EVM events & hooks for detailed execution analysis
- Custom precompiles for extending the EVM
- Transaction pool management for pending transaction control
- Contract Bundler for importing Solidity files directly