Skip to content

Using with Viem

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:

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)

A more convenient approach when bundle size isn't a primary concern is to use createMemoryclient:

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()

Core Functionality

Public Actions

Use viem's public actions to query your local Tevm environment:

// 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

Tevm supports all of viem's wallet actions with built-in prefunded accounts:

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
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

All of viem's test actions are supported for comprehensive testing capabilities:

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

Contract Interactions

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

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

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,
});

Inside the Memory Client

A MemoryClient is essentially a viem client with Tevm's functionality added. Here's how you could build one from scratch:

// 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

Block & Transaction

Account & Chain

Test Actions - Manipulate blockchain state

Block & Mining

Account & State

State Management

  • snapshot - Create a snapshot of the current state
  • revert - Revert to a previous snapshot
  • reset - Reset the fork to a fresh state
  • dumpState - Export the current state
  • loadState - Import a previously exported state
Wallet Actions - Send transactions and interact with accounts

Account Management

Transaction Operations

Signing Operations

Chain Management

Permissions & Assets

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

Using with Ethers.js

Learn how to integrate Tevm with ethers.js

Forking Mainnet

Create a local fork of mainnet for testing

Local Testing

Set up a comprehensive local testing environment

TevmNode Interface

Explore the low-level node interface