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
Read blockchain state, query contracts, and estimate gas
💼 Wallet Actions
Send transactions, sign messages, and interact with accounts
🧪 Test Actions
Manipulate blockchain state for testing and development
⚡ Tevm Actions
Tevm-specific extensions for enhanced EVM capabilities
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:
- EIP-1193 Compatibility Layer: Tevm implements the standard Ethereum provider interface
- In-Memory EVM: Tevm runs a complete Ethereum Virtual Machine locally
- Viem Integration: Tevm extends viem's functionality with EVM-specific capabilities
Complete Action Reference
Public Actions - Read blockchain state
Contract Interactions
call
- Call a contract method without sending a transactionreadContract
- Read a contract's constant/view methodsimulateContract
- Simulate a contract write without executingestimateContractGas
- Estimate gas for a contract callestimateGas
- Estimate gas for a transactiongetBytecode
- Get a contract's bytecode
Block & Transaction
getBlock
- Get a block by number or hashgetBlockNumber
- Get the latest block numbergetBlockTransactionCount
- Get the transaction count for a blockgetTransaction
- Get transaction details by hashgetTransactionCount
- Get the transaction count (nonce) for an addressgetTransactionReceipt
- Get a transaction receipt by hashwaitForTransactionReceipt
- Wait for a transaction to be mined
Account & Chain
getBalance
- Get the balance of an addressgetChainId
- Get the chain IDgetGasPrice
- Get the current gas priceestimateFeesPerGas
- Estimate fees per gas unitgetStorageAt
- Get the value from a storage slot
Test Actions - Manipulate blockchain state
Block & Mining
mine
- Mine a number of blockssetAutomine
- Enable/disable automatic miningsetIntervalMining
- Set mining to occur at intervalssetBlockGasLimit
- Set the block gas limitsetBlockTimestampInterval
- Set increment for timestampssetNextBlockBaseFeePerGas
- Set the base fee for the next blocksetNextBlockTimestamp
- Set the timestamp for the next block
Account & State
setBalance
- Set an address's balancesetCode
- Set contract bytecode at an addresssetNonce
- Set the nonce for an addresssetStorageAt
- Set a storage slot's valuesetCoinbase
- Set the block miner addresssetMinGasPrice
- Set the minimum gas price
State Management
Wallet Actions - Send transactions and interact with accounts
Account Management
getAddresses
- Get available addressesrequestAddresses
- Request permission to view addresses
Transaction Operations
prepareTransactionRequest
- Prepare a transactionsendTransaction
- Send a transactionsendRawTransaction
- Send a signed transactionsignTransaction
- Sign a transaction
Signing Operations
signMessage
- Sign a messagesignTypedData
- Sign typed data (EIP-712)
Chain Management
addChain
- Add a chain to the walletswitchChain
- Switch to a different chain
Permissions & Assets
getPermissions
- Get wallet permissionsrequestPermissions
- Request wallet permissionswatchAsset
- Add a token to the wallet
Tevm Actions - Enhanced EVM capabilities
tevmCall
- Low-level EVM calltevmContract
- Call a contract method with detailed EVM infotevmDeploy
- Deploy a contract with detailed resultstevmGetAccount
- Get detailed account informationtevmSetAccount
- Set up a complex account statetevmDeal
- Add native ETH or ERC20 tokens to an accounttevmDumpState
- Export complete EVM statetevmLoadState
- Import complete EVM statetevmMine
- 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