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 applications that need to minimize bundle size:
import { createTevmNode } from 'tevm/node'
import { requestEip1193 } from 'tevm/decorators'
import { createClient, custom } from 'viem'
// Create Tevm Node with EIP-1193 support
const node = createTevmNode().extend(requestEip1193())
// Create Viem client
const client = createClient({
// Use Tevm node as the viem transport
transport: custom(node),
})
// 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:
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 capabilitiesPublic 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
}
})
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 statetevmDumpState
- 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