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
Property | Type | Default | Description |
---|---|---|---|
fork | { transport: EIP1193RequestFn; blockTag?: BlockTag; } | - | Enables forking from a live network or another Tevm instance |
common | Common | tevmDevnet | Chain 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 |
customPrecompiles | Precompile[] | [] | Additional precompiled contracts |
allowUnlimitedContractSize | boolean | false | Disables 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
- Node Interface - Explore the complete TevmNode interface and its capabilities
- Forking & Reforking - Learn how to fork from live networks and efficiently manage forks
- State Management - Understand how to manipulate blockchain state
- Custom Precompiles - Create your own precompiled contracts to extend EVM functionality