Getting Started
Install Tevm
First, install Tevm in your project:
npm install tevm
Choose Your Client
Tevm offers different client types depending on your needs
In-memory client
You can spin up an empty ethereum node
import { createMemoryClient } from "tevm";
const memoryClient = createMemoryClient();
const blockNumber = await memoryClient.getBlockNumber();
How it works
- We create a
memoryClient
that runs an Ethereum node entirely in memory built with TypeScript and Wasm - We use the
viem
api to query it.
Fork client
You can fork an existing chain similar to Anvil and Hardhat.
import { createMemoryClient } from "tevm";
import { http } from "viem"; // http is available in tevm package as well for convenience
import { mainnet } from "tevm/chains";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
},
});
(coming soon) You can give this client out a try right now using the Tevm CLI
bunx tevm tevmCall --rpc=https://mainnet.optimism.io
How it works
- We create a
memoryClient
like before but this time we pass in a transport - Tevm will fetch the latest block upon creation
- As Tevm executes it will lazily fetch state from the fork url
- Tevm has optimizations that make it more efficient at fetching state than alternatives like Anvil or Hardhat
Rebasing client (coming soon)
This powerful client will not only fork an existing chain but it will also listen for new blocks. As new blocks come in it will rebase it's state on the existing chain.
import { createMemoryClient } from "tevm";
import { http } from "viem";
import { mainnet } from "tevm/chains";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
rebase: true,
},
});
How it works
- We create a
memoryClient
like our forked client but this time we pass inrebase: true
- Tevm will fork just like before
- Tevm will update it's fork as "latest" tag changes on the forked chain making sure to efficiently invalidate any state it needs to.
Tree-shakable client
In addition to using createMemoryClient
Tevm supports the Viem tree-shakable api.
import { createClient, http } from "viem";
import { createTevmTransport } from "tevm/memory-client";
import { optimism } from "tevm/common";
const client = createClient({
transport: createTevmTransport({
fork: { transport: http("https://mainnet.optimism.io") },
common: optimism,
}),
});
// Import actions from viem
import { getBlockNumber } from "viem";
await getBlockNumber(client);
The tree-shakable API will not include any viem or tevm actions you aren't using. For apps running in the browser the tree-shakable client is recomended to keep bundle size minimal.
Ethers client
In addition to viem support Tevm plugs into Ethers as well as any other library that supports the EIP-1193 Provider
standard.
import { TevmProvider } from "tevm/ethers";
import { http, toHex, parseEth } from "tevm";
const provider = await TevmProvider.createMemoryProvider({
fork: {
transport: http("https://mainnet.optimism.io"),
},
});
await provider.send("tevm_setAccount", [
{
address: `0x${"69".repeat(20)}`,
nonce: toHex(1),
balance: toHex(parseEth("25")),
},
]);
Use your client to read and write to an in-memory blockchain
Below is a complete typical flow of using Tevm to:
- Add a contract to the Tevm state
- Write to the contract with viem
- Mine a new block with the transaction
- Read from contract with viem
import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { http } from "viem";
import { SimpleContract } from "tevm/contract";
import { optimism } from "tevm/common";
// Create an in-memory Ethereum client forking optimism
const client = createMemoryClient({
common: optimism,
fork: { transport: http("https://mainnet.optimism.io") },
});
const contract = SimpleContract.withAddress(`0x${"40".repeat(20)}`);
// Deploy contract bytecode. There are many ways to do this including tevmDeploy.
// Here we just use an anvil test action
await client.setCode({
address: contract.address,
bytecode: contract.deployedBytecode,
});
// Write to contract
await client.writeContract({
account: PREFUNDED_ACCOUNTS[0],
abi: contract.abi,
functionName: "set",
args: [420n],
address: contract.address,
});
await client.tevmMine();
// Read from contract
const value = await client.readContract({
abi: contract.abi,
functionName: "get",
address: contract.address,
});
console.log(value); // 420n
How it works
- We create a
memoryClient
that runs an Ethereum node entirely in memory - We define a contract and its address
- We deploy the contract using
setCode
(simpler than a full deployment transaction) - We write to the contract using a familiar viem-style interface
- We mine the transaction to include it in a block
- We read the updated value from the contract
Key Points:
- Familiar Interface — Tevm uses viem as its primary API, making it instantly familiar to viem users
- Ethers Support - Tevm also supports
ethers.js
as via theEIP-1193 provider
standard - In-Memory Execution — TevmNode runs the Ethereum environment directly in memory rather than using JSON-RPC over HTTP
- Full Control — Mine blocks on demand, manipulate state, and more
- Cross platform - Run your ethereum node in the browser
- Powerful - Comes with many advantages over an
Anvil
,Ganache
, orHardhat
node
Start Building
You're now ready to build with Tevm!
// Runs in browsers, Node.js, Deno, Bun and beyond
// Zero native dependencies
import { createMemoryClient } from "tevm";
// Optionally import solidity contracts directly into typescript in a typesafe way
// This requires the Tevm Bundler to be set up (see Bundler Quickstart)
import { ComplexSimulation } from "../contracts/ComplexSimulation.s.sol";
const client = createMemoryClient();
// Use the viem api you already know
console.log(await client.getBlockNumber());
// Or use powerful typescript native apis
const {
data,
error,
logs,
createdAddresses,
executionGasUsed,
l1Fee,
trace,
accessList,
txHash,
} = await client.tevmContract({
deployedBytecode: ComplexSimulation.deployedBytecode,
...ComplexSimulation.read.simulate(2n, "arg2"),
createTrace: true,
createAccessList: true,
createTransaction: true,
throwOnFail: false,
onStep: (step, next) => {
console.log(step.opcode);
next?.();
},
});
Essential viem concepts
If you're new to Ethereum development, don't worry! Tevm serves as an excellent learning platform for both Ethereum and TypeScript:
For users new to viem it is recomended you at least become familiar with viem. But as a short introduction of the essential concepts you need to know:
Clients
are made withcreateClient
,createPublicClient
,createWalletClient
,createTestClient
, andcreateMemoryClient
and are the main abstraction you interact with.Actions
such asgetBlockNumber
,call
,readContract
,estimateGas
, andtevmSetAccount
are how you interact withviem
andtevm
Transports
such ashttp
andcreateTevmTransport
are EIP-1193 compatable providers used to resolve JSON-RPC requests. Bothviem
andtevm
use transports such as thehttp()
transport to fetch remotely over httpTevmNode
itself is a transport that plugs an in-memory ethereum node into viem to resolve JSON-RPC requests