Getting Started
Install Tevm and Zod
npm install tevm zodHello World: Ralph Loop
Before choosing clients and integrations, start with one full flow. Ralph Loop is a tiny MDX entry point that accepts props, validates them, writes to a local in-memory chain, mines, then reads the result back.
Create the MDX shell
type RalphLoopPageProps = {
name?: string;
rounds?: number;
};
export default async function RalphLoopPage(props: RalphLoopPageProps) {
const lines = await runRalphLoop(props);
return <pre>{lines.join("\n")}</pre>;
}Add the imports
import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { SimpleContract } from "tevm/contract";
import { z } from "zod";Define the Zod schemas
const ralphLoopProps = z.object({
name: z.string().min(1).default("Ralph"),
rounds: z.number().int().min(1).max(5).default(3),
});
type RalphLoopPageProps = z.input<typeof ralphLoopProps>;Run the Ralph loop
async function runRalphLoop(input: RalphLoopPageProps) {
const { name, rounds } = ralphLoopProps.parse(input);
const client = createMemoryClient();
const contract = SimpleContract.withAddress(`0x${"40".repeat(20)}`);
await client.setCode({
address: contract.address,
bytecode: contract.deployedBytecode,
});
const lines = [];
for (let round = 1; round <= rounds; round += 1) {
await client.writeContract({
account: PREFUNDED_ACCOUNTS[0],
address: contract.address,
abi: contract.abi,
functionName: "set",
args: [BigInt(round)],
});
await client.tevmMine();
const value = await client.readContract({
address: contract.address,
abi: contract.abi,
functionName: "get",
});
lines.push(`Hello ${name}: loop ${round} committed value ${value}`);
}
return lines;
}Choose Your Client
In-memory client
Spin up an empty Ethereum node:
import { createMemoryClient } from "tevm";
const memoryClient = createMemoryClient();
const blockNumber = await memoryClient.getBlockNumber();How it works
memoryClientruns an Ethereum node entirely in memory (TypeScript + Wasm).- Query it via the
viemapi.
Fork client
Fork an existing chain (like Anvil/Hardhat):
import { createMemoryClient } from "tevm";
import { http } from "viem"; // also re-exported from tevm
import { mainnet } from "tevm/common";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
},
});How it works
- Pass a transport to
createMemoryClient. - Tevm fetches the latest block on creation.
- State is lazily fetched from the fork URL as needed.
- Tevm has optimizations making fetching more efficient than Anvil/Hardhat.
Rebasing client (coming soon)
Forks an existing chain and listens for new blocks, rebasing local state as new blocks arrive.
import { createMemoryClient } from "tevm";
import { http } from "viem";
import { mainnet } from "tevm/common";
const forkClient = createMemoryClient({
fork: {
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"),
common: mainnet,
rebase: true,
},
});How it works
- Same as the fork client but with
rebase: true. - Tevm updates its fork as the "latest" tag changes, efficiently invalidating only affected state.
Tree-shakable client
Tevm supports Viem's 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 { getBlockNumber } from "viem";
await getBlockNumber(client);The tree-shakable API excludes unused viem/tevm actions. Recommended for browser apps to keep bundle size minimal.
Ethers client
Tevm works with Ethers (and any EIP-1193-compatible library):
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
A full flow: add a contract, write to it, mine, then read.
import { createMemoryClient, PREFUNDED_ACCOUNTS } from "tevm";
import { http } from "viem";
import { SimpleContract } from "tevm/contract";
import { optimism } from "tevm/common";
const client = createMemoryClient({
common: optimism,
fork: { transport: http("https://mainnet.optimism.io") },
});
const contract = SimpleContract.withAddress(`0x${"40".repeat(20)}`);
await client.setCode({
address: contract.address,
bytecode: contract.deployedBytecode,
});
await client.writeContract({
account: PREFUNDED_ACCOUNTS[0],
abi: contract.abi,
functionName: "set",
args: [420n],
address: contract.address,
});
await client.tevmMine();
const value = await client.readContract({
abi: contract.abi,
functionName: "get",
address: contract.address,
});
console.log(value); // 420nHow it works
- Create an in-memory
memoryClient. - Define the contract and its address.
- Deploy via
setCode(simpler than a full deploy tx). - Write using viem-style API.
- Mine to include the tx.
- Read the updated value.
Key Points
- Familiar Interface — viem is the primary API.
- Ethers Support — works via the
EIP-1193 providerstandard. - In-Memory Execution — runs in memory, not over JSON-RPC/HTTP.
- Full Control — mine on demand, manipulate state, more.
- Cross-platform — runs in the browser.
- Powerful — advantages over Anvil, Ganache, and Hardhat.
Start Building
// Runs in browsers, Node.js, Deno, Bun. Zero native deps.
import { createMemoryClient } from "tevm";
// Optionally import Solidity directly (requires Tevm Bundler — see Bundler Quickstart)
import { ComplexSimulation } from "../contracts/ComplexSimulation.s.sol";
const client = createMemoryClient();
console.log(await client.getBlockNumber());
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
Tevm is a great learning platform for both Ethereum and TypeScript:
- Isolated Environment — experiment without network costs or confirmations.
- TypeScript Integration — full type safety and autocomplete.
- Simple API Surface — focus on core concepts.
- Step Debugger — trace opcode-level execution.
For users new to viem, familiarize yourself with viem. Essentials:
Clients—createClient,createPublicClient,createWalletClient,createTestClient,createMemoryClient. The main abstraction.Actions—getBlockNumber,call,readContract,estimateGas,tevmSetAccount, etc.Transports— EIP-1193 providers used to resolve JSON-RPC. Both viem and tevm use transports likehttp().TevmNode— itself a transport, plugging an in-memory Ethereum node into viem.

