Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Using with Viem

Tevm integrates with viem as a local Ethereum environment with viem's familiar API surface.

Integration Options

Two approaches:

Tree-shakable (Recommended)
// For production frontends — minimize bundle size
import { createTevmTransport, tevmDumpState } from 'tevm'
import { createClient } from 'viem'
import { getBlockNumber } from 'viem/actions'
 
const tevmTransport = createTevmTransport()
 
const client = createClient({ transport: tevmTransport })
 
// Import viem actions individually
await getBlockNumber(client)
 
// Import tevm actions individually
await tevmDumpState(client)

Core Functionality

  • Public Actions — read blockchain state, query contracts, estimate gas (below)
  • Wallet Actions — send transactions, sign messages (below)
  • Test Actions — manipulate state for testing (below)
  • Tevm Actions — Tevm-specific EVM extensions (below)

Public Actions

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

import { createMemoryClient } from "tevm";
import { ERC20 } from "tevm/contract";
import { parseAbi } from "viem";
 
const client = createMemoryClient();
const tokenAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
const holderAddress = "0x1234567890123456789012345678901234567890";
 
const block = await client.getBlock();
console.log(`Block number: ${block.number}`);
 
const balance = await client.getBalance({
  address: holderAddress,
});
 
const nonce = await client.getTransactionCount({
  address: holderAddress,
});
 
await client.setCode({
  address: tokenAddress,
  bytecode: ERC20.deployedBytecode,
});
 
const result = await client.readContract({
  address: tokenAddress,
  abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
  functionName: "balanceOf",
  args: [holderAddress],
});

Wallet Actions

Tevm supports all viem wallet actions with prefunded accounts:

import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { parseEther } from "viem";
 
const client = createMemoryClient({
  account: PREFUNDED_ACCOUNTS[0], // 10000 ETH
});
 
const hash = await client.sendTransaction({
  to: "0x1234567890123456789012345678901234567890",
  value: parseEther("1"),
});
 
const receipt = await client.waitForTransactionReceipt({ hash });
 
const { contractAddress } = await client.deployContract({
  abi: parseAbi([
    "function greet() view returns (string)",
    "function setGreeting(string) returns ()",
  ]),
  bytecode: "0x608060405234801561...",
});
Working with Custom Accounts
import { createMemoryClient } from "tevm";
import { privateKeyToAccount } from "viem/accounts";
 
const account = privateKeyToAccount(
  "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
);
 
const client = createMemoryClient({ account });
 
await client.setBalance({ address: account.address, value: parseEther("10") });
 
const hash = await client.sendTransaction({
  to: "0x1234567890123456789012345678901234567890",
  value: parseEther("1"),
});

Test Actions

All viem test actions are supported (Anvil/Hardhat compatible):

import { createMemoryClient } from "tevm";
import { parseEther } from "viem";
 
const client = createMemoryClient();
 
await client.mine({ blocks: 5 });
 
await client.setBalance({
  address: "0x1234567890123456789012345678901234567890",
  value: parseEther("100"),
});
 
await client.setNextBlockTimestamp(1695311333n);
await client.mine({ blocks: 1 });
 
// Snapshot and revert
const snapshotId = await client.snapshot();
 
await client.setBalance({
  address: "0x1234567890123456789012345678901234567890",
  value: parseEther("999"),
});
 
await client.revert({ id: snapshotId });
 
const balance = await client.getBalance({
  address: "0x1234567890123456789012345678901234567890",
});

Tevm Actions

Tevm-specific actions for enhanced EVM control.

Contract Interactions

import { createMemoryClient } from "tevm";
import { ERC20 } from "tevm/contract";
import { parseAbi } from "viem";
 
const client = createMemoryClient();
const tokenAddress = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48";
const holderAddress = "0x1234567890123456789012345678901234567890";
 
const result = await client.tevmContract({
  abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
  address: tokenAddress,
  deployedBytecode: ERC20.deployedBytecode,
  functionName: "balanceOf",
  args: [holderAddress],
});
 
// Low-level call
const callResult = await client.tevmCall({
  to: tokenAddress,
  deployedBytecode: ERC20.deployedBytecode,
  data:
    "0x70a08231000000000000000000000000" +
    holderAddress.slice(2),
});

Account Management

import { createMemoryClient } from "tevm";
import { parseEther } from "viem";
 
const client = createMemoryClient();
 
const account = await client.tevmGetAccount({
  address: "0x1234567890123456789012345678901234567890",
});
 
// Set complex account state (EOA or contract)
await client.tevmSetAccount({
  address: "0xabcdef1234567890abcdef1234567890abcdef12",
  balance: parseEther("100"),
  nonce: 5n,
  deployedBytecode: "0x6080604052348015600e575f80fd5b00",
  state: {
    "0x0000000000000000000000000000000000000000000000000000000000000000":
      "0x0000000000000000000000000000000000000000000000000000000000000001",
    "0x0000000000000000000000000000000000000000000000000000000000000001":
      "0x0000000000000000000000000000000000000000000000000000000000000002",
  },
});
 
// Native ETH
await client.tevmDeal({
  account: "0x1234567890123456789012345678901234567890",
  amount: parseEther("10"),
});
 
// ERC20
await client.tevmDeal({
  erc20: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
  account: "0x1234567890123456789012345678901234567890",
  amount: 1000000n, // 1 USDC (6 decimals)
});

State Management

import { createMemoryClient } from "tevm";
 
const client = createMemoryClient();
 
const state = await client.tevmDumpState();
 
const savedState = await client.tevmDumpState();
 
await client.setBalance({
  address: "0x1234567890123456789012345678901234567890",
  value: 123456789n,
});
 
await client.tevmLoadState({ state: savedState });
 
await client.tevmMine({ blocks: 5 });

Inside the Memory Client

A MemoryClient is a viem client with Tevm functionality. Build one from scratch:

import { createTevmNode } from "tevm";
import { requestEip1193 } from "tevm/decorators";
import { tevmViemActions } from "tevm/memory-client";
import { http, custom, createClient, publicActions, testActions, walletActions } from "viem";
 
const rpcUrl = process.env.OPTIMISM_RPC_URL ?? "https://mainnet.optimism.io";
const forkTransport = http(rpcUrl)({});
 
const node = createTevmNode({
  fork: { transport: forkTransport },
}).extend(requestEip1193());
 
const memoryClient = createClient({ transport: custom(node) })
  .extend(tevmViemActions())
  .extend(publicActions)
  .extend(walletActions)
  .extend(testActions({ mode: "anvil" }));

Key architectural components:

  1. EIP-1193 Compatibility Layer — standard Ethereum provider interface
  2. In-Memory EVM — full Ethereum Virtual Machine locally
  3. Viem Integration — extends viem 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

Wallet Actions — Send transactions, interact with accounts

Account Management

Transaction Operations

Signing

Chain Management

Permissions & Assets

Tevm Actions — Enhanced EVM capabilities
  • tevmCall — Low-level EVM call
  • tevmContract — Contract call with detailed EVM info
  • tevmDeploy — Deploy with detailed results
  • tevmGetAccount — Detailed account info
  • tevmSetAccount — Set complex account state
  • tevmDeal — Add native ETH or ERC20 tokens
  • tevmDumpState — Export complete EVM state
  • tevmLoadState — Import complete EVM state
  • tevmMine — Mine blocks with options

Next Steps