Skip to content

Creating a MemoryClient

Create a Basic Node

Instantiate a client with default configuration:

TypeScript

import { createMemoryClient } from "tevm";
 
const client = createMemoryClient();
 
// Optionally wait for the client to be ready before using it
// This is not required but useful if profiling performance or debugging an issue
await client.ready();
 
// Your client is now ready to use!

Configure to Your Needs

Tailor the node with powerful configuration options:

import { createMemoryClient, http } from "tevm";
 
const client = createMemoryClient({
  // Fork from a live network
  fork: {
    transport: http("https://mainnet.infura.io/v3/YOUR-KEY"),
  },
  // Configure automatic mining
  miningConfig: { type: "auto" },
  // Set logging verbosity
  loggingLevel: "debug",
});
 
await client.ready();

Configuration Options

Tevm Node offers extensive configuration options to adapt to different use cases. Here's a complete breakdown:

Fork Configuration

The fork option creates a node that connects to an existing network:

import { createMemoryClient, http } from "tevm";
 
const node = createMemoryClient({
  fork: {
    // Use any EIP-1193 compatible provider
    transport: http("https://mainnet.infura.io/v3/YOUR-KEY"),
    // Optional: Fork from a specific block
    blockTag: 17_000_000n,
  },
});
 
await node.ready();

Mining Configuration

Control how and when blocks are produced with various mining modes:

// Auto-mining: Mine a block for every transaction
const node = createMemoryClient({
  miningConfig: {
    type: "auto",
  },
});
 
// Interval-based mining: Mine at regular intervals
const intervalNode = createMemoryClient({
  miningConfig: {
    type: "interval",
    interval: 12_000, // Mine every 12 seconds
  },
});
 
await node.ready();
await intervalNode.ready();

Chain Configuration

Customize the chain parameters or use pre-configured chains:

import { createMemoryClient } from "tevm";
import { Common } from "tevm/common";
 
// Custom chain configuration
const customNode = createMemoryClient({
  common: Common.custom({
    chainId: 1337,
    networkId: 1337,
    // Other chain parameters
  }),
});
 
await customNode.ready();

Or use one of the pre-configured chains:

import { createMemoryClient } from "tevm";
import { mainnet, optimism, arbitrum, base } from "tevm/common";
 
// Create a node with Optimism chain configuration
const optimismNode = createMemoryClient({
  common: optimism,
});
 
await optimismNode.ready();

Want to add your own network? If you need support for a network not included in Tevm, first add it to viem/chains and then open an issue on the Tevm repository to request the network to be added.

Logging Configuration

Configure the internal logger to match your needs:

const node = createMemoryClient({
  loggingLevel: "debug", // 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
});
 
// Later use the logger directly
node.logger.debug("Detailed debugging information");
node.logger.info("Informational message");
node.logger.warn("Warning!");
node.logger.error("Error encountered", { details: "Something went wrong" });
 
await node.ready();

Custom Precompiles

Add your own precompiled contracts to unlock powerful capabilities:

import { definePrecompile, createContract, parseAbi } from "tevm";
 
const calculatorPrecompile = definePrecompile({
  // Define contract interface
  contract: createContract({
    abi: parseAbi([
      "function add(uint256 a, uint256 b) returns (uint256)",
      "function subtract(uint256 a, uint256 b) returns (uint256)",
    ]),
    address: "0xf2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2",
  }),
  // Implement the precompile logic
  call: async ({ data, gasLimit }) => {
    // Precompile implementation goes here
    console.log("Precompile called with data:", data, "gas limit:", gasLimit);
 
    return {
      returnValue: new Uint8Array([0x01]), // Example return value
      executionGasUsed: 200n,
    };
  },
});
 
// Register the precompile with the node
const node = createMemoryClient({
  customPrecompiles: [calculatorPrecompile.precompile()],
});
 
await node.ready();

Performance Profiling

Enable the built-in profiler for detailed execution metrics:

const node = createMemoryClient({
  profiler: true,
});
 
await node.ready();
 
// Run a transaction or call
// ...
 
// Access profiling data
const vm = await node.getVm();
const performanceLogs = vm.evm.getPerformanceLogs();
console.log("Performance data:", performanceLogs);

Complete Configuration Reference

PropertyTypeDefaultDescription
fork{ transport: EIP1193RequestFn; blockTag?: BlockTag; }-Enables forking from a live network or another Tevm instance
commonCommontevmDevnetChain configuration object
loggingLevel"fatal" | "error" | "warn" | "info" | "debug" | "trace""info"Logging verbosity level
miningConfig{ type: 'auto' } | { type: 'interval', interval: number }{ type: 'auto' }Block mining behavior
customPrecompilesPrecompile[][]Additional precompiled contracts
allowUnlimitedContractSizebooleanfalseDisables EIP-170 contract size checks

Best Practices

Always pass in a common when forking

Though a common object is not required it is highly recomended for following reasons

  • Tevm will initialize faster if a common is provided via not needing to fetch a chainId up front
  • Chain specific information such as EIP and hardfork info will help Tevm provide a closer experience to the real chain.

When a common is not provided tevmDefault common is used

import { createMemoryClient, http } from "tevm";
import { optimism } from "tevm/common";
 
const client = createMemoryClient({
  // always pass in a common
  common: optimism,
  fork: { transport: http() },
});
 
// Because tevm knows this is an op stack chain it will operate closer to an l1 chain including allowing you to calculate l1 data fee
const { l1DataFee } = await client.call({ data });

Choose the Right Mining Configuration

By Default Tevm uses manual mining. We believe explicit mining is the correct configuration for most to all use cases which is why it's the default. As a convenience we do offer automining which will mine a new block everytime a tx enters mempool. We also offer interval mining and gas mining to more closely replicate a real ethereum node.

// For testing: Mine after each transaction
const testNode = createMemoryClient({
  miningConfig: { type: "auto" },
});
 
// For simulation: Mine at intervals to mimic real networks
const simulationNode = createMemoryClient({
  miningConfig: { type: "interval", interval: 12_000 }, // 12 seconds like Ethereum
});

Utilize debug logging when something goes wrong

When you have issues with Tevm, debug logging is useful. Tevm will produce a lot of logs so you should use an LLM to help comb through them.

const client = createMemoryClient({
  loggingLevel: "debug",
});

Call client.ready() if profiling

Anytime you are profiling tevm you will want to call client.ready() before running the profiler or else you will be measuring the initializion time in addition to the action. When you don't call client.ready() all tevm actions implicitly wait for the client to be ready before executing. Tevm initializes very fast though as it doesn't need to do a sync.

Next Steps