# Tevm Node > A lightweight, unopinionated, powerful EVM node that runs in the browser ### Advanced Ethers.js Integration #### Contract Deployment ```ts import { ContractFactory } from 'ethers' import { Wallet } from 'ethers' // Prepare compiler output (ABI and bytecode) const abi = [/* ... */] const bytecode = '0x...' // Get a signer const signer = Wallet.createRandom().connect(provider) // Fund the account await client.setBalance({ address: signer.address, value: parseEther('10') }) // Create and deploy the contract const factory = new ContractFactory(abi, bytecode, signer) const contract = await factory.deploy(...constructorArgs) // Mine the deployment transaction await client.mine({ blocks: 1 }) // Get the deployment address const contractAddress = await contract.getAddress() console.log(`Contract deployed to: ${contractAddress}`) ``` #### Working with Events ```ts // Add event listeners contract.on('Transfer', (from, to, amount, event) => { console.log(`Transfer of ${amount} from ${from} to ${to}`) // Access additional data console.log(`Block: ${event.blockNumber}`) console.log(`Transaction hash: ${event.transactionHash}`) }) // Query past events const filter = contract.filters.Transfer() const events = await contract.queryFilter(filter, -1000, 'latest') // Process events for (const event of events) { const { from, to, value } = event.args console.log(`Historical transfer: ${value} from ${from} to ${to}`) } ``` ### Common Patterns with Ethers & Tevm #### State Snapshots & Time Control ```ts // Take a snapshot of current state const snapshot = await client.snapshot() // Run some transactions await contract.transfer(recipient, amount) await client.mine({ blocks: 1 }) // Reset to previous state await client.revert({ id: snapshot }) // Manipulate blockchain time await client.setNextBlockTimestamp(Date.now() + 3600000) // 1 hour in the future ``` #### Debugging Transactions Combine ethers.js with Tevm's EVM stepping capability: ```ts // Get the calldata for a transaction const calldata = contract.interface.encodeFunctionData( 'complexFunction', [param1, param2] ) // Execute with EVM stepping const result = await client.tevmCall({ to: await contract.getAddress(), data: calldata, onStep: (step, next) => { console.log(`Opcode: ${step.opcode.name}`) console.log(`Memory: ${step.memory}`) next?.() } }) ``` ### Next Steps * Learn about [forking from live networks](/core/forking) * Explore [building a debugger UI](/examples/debugger-ui) * See how to [integrate with local testing](/examples/local-testing) import { Callout, Steps, Step } from 'vocs/components' ## Creating a Tevm Node `createTevmNode` is the foundation of Tevm - a powerful function that bootstraps a complete Ethereum execution environment in JavaScript. `createTevmNode` is the main entry point for spinning up a local Tevm Node instance. With a flexible configuration API, you can customize your node's behavior to fit various development, testing, and production scenarios. First, install the required packages: ```bash npm install tevm ```
Using Yarn or pnpm? ```bash # Yarn yarn add tevm # pnpm pnpm add tevm ```
Instantiate a node with default configuration: ```ts import { createTevmNode } from 'tevm' // Create the node instance const node = createTevmNode() // Always wait for the node to be ready before using it await node.ready() // Your node is now ready to use! ``` Tailor the node with powerful configuration options: ```ts import { createTevmNode, http } from 'tevm' const node = createTevmNode({ // 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 node.ready() ```
### Configuration Options Tevm Node offers extensive configuration options to adapt to different use cases. Here's a complete breakdown: #### Fork Configuration Forking allows you to create a local copy of any EVM chain for testing and development. The `fork` option creates a node that connects to an existing network: ```ts import { createTevmNode, http } from 'tevm' const node = createTevmNode({ 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() ``` When forking, state is fetched lazily from the remote provider and cached locally. This means your first access to any account or storage slot will be slower, but subsequent accesses will be fast. Tevm uses a least recently used (LRU) cache for state management that can hold up to 10 million items before it starts pruning state that hasn't been accessed recently. This provides an excellent balance between memory usage and performance. #### Mining Configuration Mining configuration determines how transactions are processed and blocks are produced. Control how and when blocks are produced with various mining modes: ```ts // Auto-mining: Mine a block for every transaction const node = createTevmNode({ miningConfig: { type: 'auto', }, }) // Interval-based mining: Mine at regular intervals const intervalNode = createTevmNode({ miningConfig: { type: 'interval', interval: 12_000, // Mine every 12 seconds }, }) await node.ready() await intervalNode.ready() ``` #### Chain Configuration Chain configuration defines the rules and parameters of the blockchain. Customize the chain parameters or use pre-configured chains: ```ts import { createTevmNode } from 'tevm' import { Common } from 'tevm/common' // Custom chain configuration const customNode = createTevmNode({ common: Common.custom({ chainId: 1337, networkId: 1337, // Other chain parameters }), }) await customNode.ready() ``` Or use one of the pre-configured chains: ```ts import { createTevmNode } from 'tevm' import { mainnet, optimism, arbitrum, base } from 'tevm/common' // Create a node with Optimism chain configuration const optimismNode = createTevmNode({ 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 Logging helps debug issues and understand what's happening inside your node. Configure the internal logger to match your needs: ```ts const node = createTevmNode({ 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 Custom precompiles allow you to extend the EVM with your own functions. Add your own precompiled contracts to unlock powerful capabilities: ```ts 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 = createTevmNode({ customPrecompiles: [calculatorPrecompile.precompile()], }) await node.ready() ``` #### Performance Profiling Performance profiling helps identify bottlenecks in your smart contracts. Enable the built-in profiler for detailed execution metrics: ```ts const node = createTevmNode({ 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 | | `profiler` | `boolean` | `false` | Enables performance logging | | `customPrecompiles` | `Precompile[]` | `[]` | Additional precompiled contracts | | `allowUnlimitedContractSize` | `boolean` | `false` | Disables EIP-170 contract size checks | ### Best Practices Following these best practices will help you avoid common pitfalls when working with Tevm Node. #### Always await node.ready() ```ts const node = createTevmNode() await node.ready() // Essential: Ensures node is fully initialized // Now safe to use the node ``` #### Choose the Right Mining Configuration ```ts // For testing: Mine after each transaction const testNode = createTevmNode({ miningConfig: { type: 'auto' } }) // For simulation: Mine at intervals to mimic real networks const simulationNode = createTevmNode({ miningConfig: { type: 'interval', interval: 12_000 } // 12 seconds like Ethereum }) await testNode.ready() await simulationNode.ready() ``` #### Handle Initialization Errors ```ts try { const node = createTevmNode() await node.ready() console.log('Node successfully initialized') } catch (error) { console.error('Node initialization failed:', error) // Implement proper error handling } ``` ### Usage Scenarios * **Fresh Node**: Fast, auto-mining configuration with unlimited contract sizes for rapid local development * **Forked Node**: Realistic forked environment with precise error handling for production simulation * **Testing Environment**: Performance-focused configuration for CI/CD and test automation #### Fresh Node ```ts import { createTevmNode } from 'tevm' const freshNode = createTevmNode({ miningConfig: { type: 'auto' }, loggingLevel: 'debug', // See detailed logs during development allowUnlimitedContractSize: true, // No contract size limits during development }) await freshNode.ready() // Node is ready for local development console.log('Fresh node ready!') ``` #### Forked Node ```ts import { createTevmNode, http } from 'tevm' const forkedNode = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/YOUR-KEY'), blockTag: 'latest', // Always use the latest block }, miningConfig: { type: 'interval', interval: 12000 // Mine every 12 seconds like Ethereum mainnet }, loggingLevel: 'error', // Only show errors }) await forkedNode.ready() console.log('Forked node ready!') ``` #### Testing Node ```ts import { createTevmNode } from 'tevm' const testNode = createTevmNode({ miningConfig: { type: 'auto' }, // Immediate mining for faster tests profiler: true, // Enable profiling for performance testing }) await testNode.ready() // Run your tests with profiling enabled console.log('Testing node with profiling ready!') ``` ### Next Steps * [Node Interface](./tevm-node-interface) - Explore the complete TevmNode interface and its capabilities * [Forking & Reforking](./forking) - Learn how to fork from live networks and efficiently manage forks * [State Management](./managing-state) - Understand how to manipulate blockchain state * [Custom Precompiles](../advanced/custom-precompiles) - Create your own precompiled contracts to extend EVM functionality import { Callout } from "vocs/components"; ## Forking & Reforking Tevm's forking capability creates a local blockchain that mirrors the state of any EVM network, allowing you to work with real-world data in an isolated environment. Forking allows you to create a local copy of an Ethereum network (or any EVM-compatible chain) at a specific point in time. This local copy can be modified without affecting the original network, making it perfect for: * Testing against production smart contracts and state * Debugging complex transactions * Developing with real-world data * Simulating DeFi protocols and interactions * Performing "what-if" analysis and scenario testing ### Basic Forking #### Fork from Mainnet ```ts import { createTevmNode, http } from "tevm"; // Create a node that forks from Ethereum mainnet const node = createTevmNode({ fork: { transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY")({}), blockTag: "latest", // Fork from the latest block }, }); // Always wait for the node to be ready await node.ready(); // The node now contains a copy of the entire Ethereum mainnet state console.log("Forked node ready!"); ``` #### Fork from Optimism ```ts import { createTevmNode, http } from "tevm"; import { optimism } from "tevm/common"; // Create a node that forks from Optimism const node = createTevmNode({ fork: { transport: http("https://mainnet.optimism.io")({}), blockTag: "latest", }, common: optimism, // Use Optimism chain configuration }); await node.ready(); console.log("Optimism fork ready!"); ``` #### Fork from Specific Block ```ts import { createTevmNode, http } from "tevm"; // Create a node that forks from a specific block const node = createTevmNode({ fork: { transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY")({}), blockTag: 17_500_000n, // Specific block number }, }); await node.ready(); console.log("Forked from block 17,500,000"); ``` Remember to call the transport function with an empty object `({})` as shown in the examples above. This is required for the viem transport to be initialized correctly. ### Fork Transport Options Tevm can fork from any EIP-1193 compatible provider, giving you flexibility in where you source your blockchain data. #### Provider Types Tevm supports multiple transport types for forking: ```ts import { createTevmNode, http } from "tevm"; import { createPublicClient, http as viemHttp } from "viem"; import { BrowserProvider } from "ethers"; // 1. Using Tevm's http transport (recommended) const tevmNode1 = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); // 2. Using viem's PublicClient const publicClient = createPublicClient({ transport: viemHttp("https://mainnet.infura.io/v3/YOUR-KEY"), }); const tevmNode2 = createTevmNode({ fork: { transport: publicClient, }, }); // 3. Using Ethers.js Provider (must be wrapped to match EIP-1193) const ethersProvider = new BrowserProvider(window.ethereum); const tevmNode3 = createTevmNode({ fork: { transport: { request: async ({ method, params }) => { return await ethersProvider.send(method, params || []); }, }, }, }); ``` #### Provider Selection Each provider type has different characteristics: | Provider | Pros | Cons | | --------------------- | ---------------------------------------- | -------------------------------- | | `http` from Tevm | Optimized for Tevm, minimal dependencies | Limited middleware support | | Public RPC nodes | Free, easy to use | Rate limits, may be slow | | Alchemy/Infura/etc | Fast, reliable, archive data | Requires API key, may have costs | | Local Ethereum node | Full control, no rate limits | Resource intensive to run | | Another Tevm instance | Memory efficient | Adds complexity | For best performance with large-scale testing, consider using a dedicated RPC provider like Alchemy or Infura, or running your own Ethereum node. ### How Forking Works Understanding how forking works internally helps you optimize your usage of Tevm. Tevm implements **lazy loading with local caching** for forked state: 1. **Initial Fork**: When you create a forked node, Tevm doesn't immediately copy the entire blockchain state. 2. **Lazy Loading**: When your code accesses specific accounts or storage slots, Tevm fetches only that data from the remote provider. 3. **Local Cache**: Fetched data is stored in a local cache, so subsequent reads are fast and don't require network requests. 4. **Local Modifications**: Any changes you make (e.g., account balance changes, contract deployments) are stored locally and take precedence over the forked state. This approach provides the perfect balance between having a complete blockchain copy and efficient memory usage. ```ts import { createTevmNode, http } from "tevm"; import { createAddress } from "tevm/address"; import { performance } from "node:perf_hooks"; const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await node.ready(); const vm = await node.getVm(); const daiAddress = createAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F"); // First access - fetches from remote provider console.log("Fetching DAI contract from mainnet..."); const t0 = performance.now(); const daiContract = await vm.stateManager.getAccount(daiAddress); console.log(`First access: ${performance.now() - t0}ms`); // Second access - uses local cache const t1 = performance.now(); await vm.stateManager.getAccount(daiAddress); console.log(`Cached access: ${performance.now() - t1}ms`); // The difference in timing demonstrates the caching in action ``` ### Reforking Strategies Reforking allows you to create multiple nodes based on the same initial state, perfect for testing different scenarios. There are two primary approaches to reforking in Tevm: #### 1. Using a Node as Transport This is the recommended approach for most scenarios as it's more memory efficient: ```ts import { createTevmNode, http, hexToBigInt } from "tevm"; import { requestEip1193 } from "tevm/decorators"; // Step 1: Create the source node with EIP-1193 support const sourceNode = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), blockTag: 17_000_000n, }, }).extend(requestEip1193()); await sourceNode.ready(); // Step 2: Get the current block number const currentBlock = await sourceNode.request({ method: "eth_blockNumber", }); // Step 3: Create a new node that forks from the source node const forkNode = createTevmNode({ fork: { transport: sourceNode, // Use the source node as a transport blockTag: hexToBigInt(currentBlock), }, }); await forkNode.ready(); // Now forkNode is a fork of sourceNode // Changes in forkNode won't affect sourceNode ``` This approach has several advantages: * **Memory Efficiency**: Reuses the cached state from the source node * **Performance**: Avoids duplicate network requests for the same data * **Isolation**: Changes in the new fork don't affect the source node * **Flexibility**: Can create multiple forks from the same source #### 2. Using Deep Copy For complete isolation, you can create an independent copy: ```ts import { createTevmNode, http } from "tevm"; // Step 1: Create the original node const originalNode = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await originalNode.ready(); // Let the node fetch some state // ... perform operations ... // Step 2: Create a completely independent copy const independentNode = await originalNode.deepCopy(); // Now independentNode is a complete copy of originalNode with its own state // Changes to either node won't affect the other ``` Deep copying duplicates the entire state tree, which can be memory intensive. Use this approach sparingly and prefer using the node as a transport when possible. ### Working with Forked State Once you have a forked node, you can read from and write to its state just like a regular Ethereum node. #### Reading State Access the state of accounts, contracts, and storage: ```ts import { createTevmNode, http } from "tevm"; import { createAddress } from "tevm/address"; import { formatEther, hexToBytes } from "viem"; // Create a node forked from mainnet const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await node.ready(); const vm = await node.getVm(); // 1. Read an EOA (externally owned account) state const vitalikAddress = createAddress( "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", ); const vitalikAccount = await vm.stateManager.getAccount(vitalikAddress); if (vitalikAccount) { console.log(`Vitalik's balance: ${formatEther(vitalikAccount.balance)} ETH`); console.log(`Nonce: ${vitalikAccount.nonce}`); } // 2. Read a contract's code const uniswapV2Router = createAddress( "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", ); const routerAccount = await vm.stateManager.getAccount(uniswapV2Router); const routerCode = await vm.stateManager.getContractCode(uniswapV2Router); console.log(`Uniswap Router code size: ${routerCode.length} bytes`); // 3. Read contract storage const slot0 = await vm.stateManager.getContractStorage( uniswapV2Router, hexToBytes( "0x0000000000000000000000000000000000000000000000000000000000000000", ), ); console.log(`Storage slot 0: ${slot0}`); // 4. Execute view function via low-level call const result = await vm.evm.runCall({ to: uniswapV2Router, data: hexToBytes("0xc45a0155"), // factory() function selector gasLimit: 100000n, }); console.log("Factory address:", result.execResult.returnValue); ``` #### Modifying State Make changes to the forked state: ```ts import { createTevmNode, http } from "tevm"; import { createAddress } from "tevm/address"; import { parseEther, formatEther } from "viem"; const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await node.ready(); const vm = await node.getVm(); // 1. Modify account balance const testAddress = createAddress("0x1234567890123456789012345678901234567890"); let account = await vm.stateManager.getAccount(testAddress); // If account doesn't exist yet, create it if (!account) { account = { nonce: 0n, balance: 0n, storageRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", codeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", }; } // Add 100 ETH to the account account.balance += parseEther("100"); await vm.stateManager.putAccount(testAddress, account); // Verify the change const updatedAccount = await vm.stateManager.getAccount(testAddress); console.log(`New balance: ${formatEther(updatedAccount.balance)} ETH`); // 2. Impersonate an account node.setImpersonatedAccount("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); // Vitalik // Now you can send transactions as this account const txResult = await vm.runTx({ tx: { from: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", to: testAddress, value: parseEther("1"), gasLimit: 21000n, }, }); console.log(`Transaction success: ${!txResult.execResult.exceptionError}`); console.log(`Gas used: ${txResult.gasUsed}`); // 3. Modify contract storage directly const uniswapV3Factory = createAddress( "0x1F98431c8aD98523631AE4a59f267346ea31F984", ); await vm.stateManager.putContractStorage( uniswapV3Factory, hexToBytes( "0x0000000000000000000000000000000000000000000000000000000000000000", ), hexToBytes( "0x0000000000000000000000001234567890123456789012345678901234567890", ), ); ``` ### Advanced Use Cases * **๐Ÿ”„ DeFi Protocol Testing** - Test complex DeFi interactions like flash loans, liquidations, and arbitrage * **๐Ÿ”’ Vulnerability Analysis** - Analyze smart contract security by manipulating state to trigger edge cases * **๐Ÿ—ณ๏ธ Governance Simulation** - Simulate DAO votes and governance proposals * **๐Ÿ’ฐ MEV Strategy Testing** - Develop and test MEV strategies without risking real capital #### DeFi Protocol Example Simulate a complex DeFi interaction: ```ts import { createTevmNode, http } from "tevm"; import { createAddress } from "tevm/address"; import { hexToBytes, parseEther } from "viem"; async function simulateFlashLoan() { const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await node.ready(); // Setup our test account const trader = createAddress("0x1234567890123456789012345678901234567890"); const vm = await node.getVm(); // Give account some ETH await vm.stateManager.putAccount(trader, { nonce: 0n, balance: parseEther("10"), storageRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", codeHash: "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", }); // AAVE V2 lending pool address const aaveLendingPool = createAddress( "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", ); // Encode a flashLoan call (simplified) // In reality you'd need to encode the parameters correctly const flashLoanCalldata = hexToBytes("0xab9c4b5d..."); // Execute the flash loan const result = await vm.runTx({ tx: { from: trader, to: aaveLendingPool, data: flashLoanCalldata, gasLimit: 5000000n, }, }); console.log("Flash loan simulation result:"); console.log(`Success: ${!result.execResult.exceptionError}`); if (result.execResult.exceptionError) { console.log(`Error: ${result.execResult.exceptionError}`); } else { console.log(`Gas used: ${result.gasUsed}`); } // Check ending balance const finalAccount = await vm.stateManager.getAccount(trader); console.log(`Final balance: ${finalAccount.balance}`); } // Run the simulation simulateFlashLoan(); ``` ### Performance Optimization Optimizing your forking setup can significantly improve performance, especially for complex test suites. #### Efficient State Access ```ts import { createTevmNode, http } from "tevm"; import { createAddress } from "tevm/address"; // Create a forked node const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), // Specify a block to avoid moving target issues blockTag: 17_000_000n, }, }); await node.ready(); // Access pattern optimization async function optimizedStateAccess() { const vm = await node.getVm(); const stateManager = vm.stateManager; // โœ… Batch related state requests const addresses = [ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI ].map(createAddress); // Fetch all in parallel const accounts = await Promise.all( addresses.map((addr) => stateManager.getAccount(addr)), ); // โœ… Reuse state manager for multiple operations const usdcAddress = addresses[0]; // Access code and storage from the same contract const [code, slot0, slot1] = await Promise.all([ stateManager.getContractCode(usdcAddress), stateManager.getContractStorage( usdcAddress, Buffer.from("0".padStart(64, "0"), "hex"), ), stateManager.getContractStorage( usdcAddress, Buffer.from("1".padStart(64, "0"), "hex"), ), ]); return { accounts, code, storage: [slot0, slot1] }; } ``` #### Selective Forking For some tests, you might not need the entire state of mainnet: ```ts // Specify chainId for minimal setup when you don't need a full fork const lightNode = createTevmNode({ // No fork specified - creates a minimal local node common: { chainId: 1 }, }); // Only fork when you need production data const conditionalFork = process.env.USE_FORK === "true" ? { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), blockTag: "latest", } : undefined; const node = createTevmNode({ fork: conditionalFork, }); ``` #### Cache Warmer For critical paths, pre-warm the cache: ```ts // Pre-warm the cache for frequently accessed contracts async function warmCache(node) { const vm = await node.getVm(); const contracts = [ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", // UniswapV2Router ].map(createAddress); console.log("Warming cache..."); await Promise.all( contracts.map((addr) => vm.stateManager .getAccount(addr) .then((account) => vm.stateManager.getContractCode(addr)), ), ); console.log("Cache warmed!"); } ``` ### Best Practices Following these best practices will help you avoid common pitfalls when working with forked nodes. #### 1. RPC Provider Setup ```ts // โœ… Always call http transport with empty object const node = createTevmNode({ fork: { transport: http("https://ethereum.quicknode.com/YOUR-API-KEY")({}), }, }); // โœ… Specify a block number for deterministic tests const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), blockTag: 17_000_000n, // Fixed block for reproducibility }, }); ``` #### 2. Error Handling ```ts // โœ… Add proper error handling for RPC failures try { const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), }, }); await node.ready(); } catch (error) { if (error.message.includes("rate limit")) { console.error( "RPC rate limit exceeded. Consider using a different provider.", ); } else if (error.message.includes("network")) { console.error("Network error. Check your internet connection."); } else { console.error("Fork initialization failed:", error); } } ``` #### 3. State Handling ```ts // โœ… Use proper null checks const account = await vm.stateManager.getAccount(address); if (account) { // Account exists, safe to use account.balance += parseEther("1"); await vm.stateManager.putAccount(address, account); } // โœ… Handle potential RPC failures in state access try { const code = await vm.stateManager.getContractCode(contractAddress); // Use code } catch (error) { console.error("Failed to fetch contract code:", error); // Implement fallback or retry logic } ``` #### 4. Performance Considerations ```ts // โœ… Choose the right block for your needs const node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), // For recent transactions, use 'latest' // For reproducible tests, use a specific number // For historical analysis, use a past block blockTag: process.env.NODE_ENV === "test" ? 17_000_000n : "latest", }, }); // โœ… Remember that first access is slower (RPC call) // but subsequent accesses are fast (from cache) ``` #### 5. Testing Setup ```ts // โœ… For test isolation, use a fresh fork for each test beforeEach(async () => { node = createTevmNode({ fork: { transport: http("https://mainnet.infura.io/v3/YOUR-KEY")({}), blockTag: 17_000_000n, }, }); await node.ready(); }); // โœ… For test efficiency, reuse the same fork but reset state let baseNode; before(async () => { baseNode = createTevmNode({ fork: { /* ... */ }, }); await baseNode.ready(); }); beforeEach(async () => { // Clone the base node for each test node = await baseNode.deepCopy(); }); ``` ### Next Steps * [State Management](/core/managing-state) - Learn more about manipulating blockchain state * [Mining Modes](/core/mining-modes) - Configure how transactions are mined in your forked node * [Transaction Pool](/advanced/txpool) - Understand how to work with pending transactions * [Forking Example](/examples/forking-mainnet) - See a complete example of forking mainnet import { Callout, Steps, Button } from 'vocs/components' ## Managing State Tevm provides powerful state management capabilities through multiple APIs to read, write, and manipulate blockchain state. Tevm offers various approaches to manage Ethereum state, from low-level direct state access to high-level client APIs. Each approach has its benefits and use cases. ### State Management Approaches :::code-group ```typescript [Raw API] showLineNumbers {1-2,4-6,9,10-15,18-26,29} filename="raw-state-manager.ts" import { createTevmNode } from 'tevm' import { EthjsAccount } from 'tevm/utils' const node = createTevmNode() const vm = await node.getVm() const stateManager = vm.stateManager // [!code focus] // Read account state const address = '0x1234567890123456789012345678901234567890' const account = await stateManager.getAccount(address) // [!code focus] console.log({ balance: account.balance, // [!code focus] nonce: account.nonce, // [!code focus] codeHash: account.codeHash, // [!code focus] storageRoot: account.storageRoot // [!code focus] }) // Create or update an account await stateManager.putAccount( // [!code focus] address, new EthjsAccount({ nonce: 0n, balance: 10_000_000n, storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' }) ) // Delete an account await stateManager.deleteAccount(address) // [!code focus] ```

The most direct way to manage state is through the StateManager interface.

The raw StateManager API gives you maximum control but requires more detailed knowledge of Ethereum's state structure.
```typescript [Client API] showLineNumbers {1,3,6-9,12-13} filename="client-api.ts" import { createMemoryClient } from 'tevm' const client = createMemoryClient() // Set account balance await client.setBalance({ // [!code focus] address: '0x1234567890123456789012345678901234567890', // [!code focus] value: 1000000000000000000n // [!code focus] }) // [!code focus] // Get account balance const balance = await client.getBalance({ // [!code focus] address: '0x1234567890123456789012345678901234567890' // [!code focus] }) // [!code focus] console.log(`Balance: ${balance}`) ```

For most applications, the high-level client API provides a more familiar and convenient interface.

This approach is recommended for most users as it provides a clean, consistent interface similar to other Ethereum libraries.
::: ### Contract State Management

๐Ÿ“„ Deploy Code

Deploy smart contract bytecode to the blockchain

๐Ÿ‘๏ธ Read Code

Read the deployed bytecode of a contract

๐Ÿ’พ Storage Access

Read and write to contract storage slots

๐Ÿ”„ State Reset

Clear contract storage or delete contracts

:::code-group ```typescript [Raw API] showLineNumbers {3-6,9,12-13,16-19,22} filename="contract-management.ts" // Using the raw StateManager API // Deploy contract code await stateManager.putContractCode( // [!code focus] address, new Uint8Array([/* bytecode */]) // [!code focus] ) // Read contract code const code = await stateManager.getContractCode(address) // [!code focus] // Read storage slot const slot = '0x0000000000000000000000000000000000000000000000000000000000000000' // [!code focus] const value = await stateManager.getContractStorage(address, slot) // [!code focus] // Write storage const key = '0x0000000000000000000000000000000000000000000000000000000000000000' // [!code focus] const newValue = '0x0000000000000000000000000000000000000000000000000000000000000001' // [!code focus] await stateManager.putContractStorage(address, key, newValue) // [!code focus] // Clear all storage await stateManager.clearContractStorage(address) // [!code focus] ``` ```typescript [Client API] showLineNumbers {1,3-4,7-11,14-17,20-23} filename="client-contract-management.ts" import { createMemoryClient, hexToBytes } from 'tevm' const client = createMemoryClient() const contractAddress = '0x1234567890123456789012345678901234567890' // Deploy contract code await client.setCode({ // [!code focus] address: contractAddress, // [!code focus] // Convert hex string to bytes bytecode: hexToBytes('0x608060405234801561001057600080fd5b50...') // [!code focus] }) // [!code focus] // Read contract code const code = await client.getCode({ // [!code focus] address: contractAddress, // [!code focus] blockTag: 'latest' // [!code focus] }) // [!code focus] // Use contract methods via ethers or viem const result = await client.call({ // [!code focus] to: contractAddress, // [!code focus] data: '0x70a08231000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266' // [!code focus] }) // [!code focus] ``` ::: ### Framework Integration Tevm allows you to combine high-level libraries like Viem and Ethers with low-level state access. :::code-group ```typescript [Viem] showLineNumbers {1,3,6-9,12-13,16-23} filename="viem-integration.ts" import { createMemoryClient } from 'tevm' const client = createMemoryClient() // Use Viem-style API for basic operations await client.setBalance({ // [!code focus] address: '0x1234567890123456789012345678901234567890', // [!code focus] value: 1000000000000000000n // [!code focus] }) // [!code focus] // Access raw state manager for advanced operations const vm = await client.transport.tevm.getVm() // [!code focus] const stateManager = vm.stateManager // [!code focus] // Use raw API for complex operations await stateManager.checkpoint() // [!code focus] try { // [!code focus] await stateManager.putContractStorage(address, key, value) // [!code focus] await stateManager.commit() // [!code focus] } catch (error) { // [!code focus] await stateManager.revert() // [!code focus] } // [!code focus] ```
The `createMemoryClient` function creates a client that implements viem's public actions, making it compatible with viem's ecosystem.
```typescript [Ethers] showLineNumbers {1-3,5-6,9,12-13} filename="ethers-integration.ts" import { createTevmNode } from 'tevm' import { requestEip1193 } from 'tevm/decorators' import { BrowserProvider } from 'ethers' const node = createTevmNode().extend(requestEip1193()) // [!code focus] const provider = new BrowserProvider(node) // [!code focus] // Use Ethers API for standard operations const balance = await provider.getBalance('0x1234...') // [!code focus] // Access raw state manager for advanced operations const vm = await node.getVm() // [!code focus] const stateManager = vm.stateManager // [!code focus] // Now you can use both Ethers and low-level StateManager APIs ```
Using the `requestEip1193` decorator makes your Tevm node compatible with Ethers.js Provider interface.
::: ### Advanced Features #### State Checkpoints Create atomic state changes that can be committed or reverted: ```typescript showLineNumbers {1,3,4-10,12-13} filename="checkpoints.ts" const stateManager = (await node.getVm()).stateManager await stateManager.checkpoint() // [!code focus] try { // [!code focus] // Batch multiple state changes await Promise.all([ stateManager.putAccount(address, account), stateManager.putContractStorage(address, key, value), ]) await stateManager.commit() // [!code focus] } catch (error) { // [!code focus] await stateManager.revert() // [!code focus] console.error('State changes reverted:', error) // [!code focus] } ``` Checkpoints provide transaction-like semantics, allowing you to revert changes if something goes wrong. #### State Persistence Save and load blockchain state: ```typescript showLineNumbers {2,5,8-9} filename="persistence.ts" // Dump complete state const state = await stateManager.dumpCanonicalGenesis() // [!code focus] // Save state (example with localStorage) localStorage.setItem('tevmState', JSON.stringify(state)) // [!code focus] // Load saved state const savedState = JSON.parse(localStorage.getItem('tevmState')) // [!code focus] await stateManager.generateCanonicalGenesis(savedState) // [!code focus] ``` You can save state to any storage system - from localStorage in browsers to database systems in server environments. #### Fork Mode Tevm supports lazy loading with caching when forking from another network: ```typescript showLineNumbers {1-5,7,10,13} filename="fork-mode.ts" const node = createTevmNode({ fork: { transport: http('https://mainnet.optimism.io') } }) const stateManager = (await node.getVm()).stateManager // [!code focus] // First access fetches from remote const account = await stateManager.getAccount('0x1234...') // [!code focus] // Subsequent access uses cache const cachedAccount = await stateManager.getAccount('0x1234...') // [!code focus] ``` When forking, Tevm only loads the state it needs from the remote provider and caches it locally. This provides an efficient balance between performance and state availability. ### Best Practices

โš ๏ธ Error Handling

Properly handle errors from state operations

๐Ÿ›ก๏ธ State Isolation

Create isolated copies of state for testing

โš›๏ธ Atomic Operations

Group related state changes with checkpoints

:::code-group ```typescript [Error Handling] showLineNumbers {1-14} filename="error-handling.ts" try { const account = await stateManager.getAccount('0x1234...') if (!account) { throw new Error('Account not found') } // Work with account } catch (error) { if (error instanceof MissingAccountError) { // Handle specific error types console.log('Account does not exist yet') } else { // Handle generic errors console.error('State operation failed:', error) } } ```
Tevm provides specific error types for different state operations. Catch and handle these appropriately.
```typescript [State Isolation] showLineNumbers {2,5} filename="state-isolation.ts" // Create isolated copy for testing const isolatedState = await stateManager.deepCopy() // [!code focus] // Make changes without affecting original await isolatedState.putAccount(address, account) // [!code focus] // Changes to isolatedState don't affect the original stateManager ```
State isolation is particularly useful for testing scenarios where you want to try operations without affecting your main state.
```typescript [Atomic Operations] showLineNumbers {1-12} filename="atomic-operations.ts" await stateManager.checkpoint() try { // Group related changes await Promise.all([ stateManager.putAccount(address, account), stateManager.putContractStorage(address, key, value), ]) await stateManager.commit() } catch (error) { // Revert all changes if any operation fails await stateManager.revert() console.error('Transaction reverted:', error) } ``` ::: ### Related Resources
import { Callout, Steps, Button } from 'vocs/components' import { Card, CardGrid, FileTree } from '../../../components' ## Mining Modes Tevm Node provides several mining modes to control how and when blocks are produced, allowing you to simulate different network conditions. Mining modes determine when transactions are included in blocks and confirmed. By choosing the right mining mode, you can accurately simulate real blockchain networks or optimize for specific testing scenarios. ### Available Mining Modes Mines a block after each transaction Mines blocks at fixed time intervals Only mines when explicitly requested Mines when gas threshold is reached :::code-group ```typescript [Auto Mining] showLineNumbers {2-4} filename="auto-mining.ts" const node = createTevmNode({ miningConfig: { // [!code focus] type: 'auto' // [!code focus] } // [!code focus] }) // Send a transaction - it will be mined immediately const txHash = await node.sendTransaction({ // transaction details }) // Transaction is already confirmed in a block const receipt = await node.getTransactionReceipt({ hash: txHash }) console.log('Block number:', receipt.blockNumber) ```

Automatically mines a new block after each transaction.

```typescript [Interval Mining] showLineNumbers {2-5} filename="interval-mining.ts" const node = createTevmNode({ miningConfig: { // [!code focus] type: 'interval', // [!code focus] interval: 12000 // 12 seconds, similar to Ethereum // [!code focus] } // [!code focus] }) // Transaction will remain pending until the next mining interval const txHash = await node.sendTransaction({ // transaction details }) // Wait for the next interval (or use a Promise to be notified) setTimeout(async () => { const receipt = await node.getTransactionReceipt({ hash: txHash }) console.log('Block number:', receipt.blockNumber) }, 12000) ```

Mines blocks at fixed time intervals, simulating real network block times.

```typescript [Manual Mining] showLineNumbers {2-4,10} filename="manual-mining.ts" const node = createTevmNode({ miningConfig: { // [!code focus] type: 'manual' // [!code focus] } // [!code focus] }) // Transaction will remain pending until you manually mine const txHash = await node.sendTransaction({/* transaction details */}) // Later, mine blocks manually await node.mine() // [!code focus] // Now the transaction is confirmed const receipt = await node.getTransactionReceipt({ hash: txHash }) console.log('Block number:', receipt.blockNumber) ```

Only mines blocks when explicitly requested, giving you complete control over block production.

```typescript [Gas-Based Mining] showLineNumbers {2-5} filename="gas-based-mining.ts" const node = createTevmNode({ miningConfig: { // [!code focus] type: 'gas', // [!code focus] gasLimit: 15000000 // Similar to Ethereum block gas limit // [!code focus] } // [!code focus] }) // Transactions accumulate in the mempool until gas threshold is reached // A block is mined when total gas exceeds gasLimit // Small transaction (won't trigger mining by itself) await node.sendTransaction({/* details for small tx */}) // Large transaction that pushes total over threshold will trigger mining await node.sendTransaction({/* details for large tx */}) ```

Mines a new block when the accumulated gas usage reaches a specified threshold.

::: ### Changing Mining Modes You can change the mining mode at any time during the node's lifecycle to adapt to different testing phases. ```typescript showLineNumbers {2-5,8-10} filename="changing-mining-modes.ts" // Switch to interval mining await node.setMiningConfig({ // [!code focus] type: 'interval', // [!code focus] interval: 5000 // 5 seconds // [!code focus] }) // [!code focus] // Switch to manual mining await node.setMiningConfig({ // [!code focus] type: 'manual' // [!code focus] }) // [!code focus] ``` ### Best Practices #### Choose the Right Mode for Your Use Case Use auto mining for the fastest feedback loop during development Use manual mining for deterministic tests with precise control Use interval mining to simulate real-world network conditions Use gas mining to test application behavior under congestion #### Consider Performance Implications Different mining modes have varying performance characteristics: * Auto mining can be resource-intensive with many transactions * Interval mining might delay transaction processing * Gas mining helps simulate network congestion but requires careful configuration #### Testing Best Practices ```typescript filename="testing-strategies.ts" // For time-sensitive logic testing const timeNode = createTevmNode({ miningConfig: { type: 'interval', interval: 10000 } }) // For deterministic test cases const deterministicNode = createTevmNode({ miningConfig: { type: 'manual' } }) // For gas-sensitive contract testing const gasNode = createTevmNode({ miningConfig: { type: 'gas', gasLimit: 8000000 } }) ``` ### Example: Testing Different Mining Modes :::code-group ```typescript [Comparative Example] showLineNumbers {4-6,9-13,16-18,21-24} filename="mining-mode-comparison.ts" import { createTevmNode } from 'tevm' // Auto mining for quick tests const autoNode = createTevmNode({ // [!code focus] miningConfig: { type: 'auto' } // [!code focus] }) // [!code focus] // Interval mining for realistic scenarios const intervalNode = createTevmNode({ // [!code focus] miningConfig: { // [!code focus] type: 'interval', // [!code focus] interval: 12000 // [!code focus] } // [!code focus] }) // Manual mining for controlled tests const manualNode = createTevmNode({ // [!code focus] miningConfig: { type: 'manual' } // [!code focus] }) // [!code focus] // Test transaction processing await autoNode.sendTransaction({...}) // Mines immediately // [!code focus] await intervalNode.sendTransaction({...}) // Mines after interval // [!code focus] await manualNode.sendTransaction({...}) // Stays pending until manual mine // [!code focus] await manualNode.mine() // Now the transaction is mined // [!code focus] ``` ```typescript [Real-world Simulation] filename="real-world-simulation.ts" import { createTevmNode, http } from 'tevm' import { mainnet } from 'tevm/chains' // Create a node that simulates mainnet conditions const node = createTevmNode({ // Fork from mainnet fork: { transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'), common: mainnet }, // Use Ethereum's ~12 second block time miningConfig: { type: 'interval', interval: 12000 } }) // Transaction will stay in mempool until next block const txHash = await node.sendTransaction({ from: '0x123...', to: '0x456...', value: 1000000000000000000n }) console.log('Transaction is pending in the mempool...') // You can monitor pending transactions const pending = await node.getTxPoolContent() console.log('Pending transactions:', pending) // Wait for the block to be mined setTimeout(async () => { const receipt = await node.getTransactionReceipt({ hash: txHash }) console.log('Transaction mined in block:', receipt.blockNumber) }, 12000) ``` ::: ### Related Resources
import { Callout } from 'vocs/components' import { Card, CardGrid } from '../../../components' ## TevmNode Interface The TevmNode interface is the core API of Tevm, providing direct access to all Ethereum execution capabilities without abstraction layers. The `TevmNode` interface represents the foundational layer of Tevm's architecture. It exposes powerful low-level access to all the essential components that make up an Ethereum node - from the EVM execution engine to transaction processing and state management. ### Interface Overview ```ts export type TevmNode = { // Logging & status readonly logger: Logger status: 'INITIALIZING' | 'READY' | 'SYNCING' | 'MINING' | 'STOPPED' readonly ready: () => Promise // Core components readonly getVm: () => Promise readonly getTxPool: () => Promise readonly getReceiptsManager: () => Promise readonly miningConfig: MiningConfig // Forking support readonly mode: TMode readonly forkTransport?: { request: EIP1193RequestFn } // Account management readonly getImpersonatedAccount: () => Address | undefined readonly setImpersonatedAccount: (address: Address | undefined) => void // Event filtering readonly setFilter: (filter: Filter) => void readonly getFilters: () => Map readonly removeFilter: (id: Hex) => void // Extensibility readonly extend: ( decorator: (client: TevmNode) => TExtension ) => TevmNode // State management readonly deepCopy: () => Promise> } & EIP1193EventEmitter & TExtended ``` When using TypeScript, the interface is fully typed, providing excellent autocomplete and type checking as you develop. ### Key Capabilities Direct access to the Ethereum Virtual Machine for executing code Working with pending transactions Advanced account management and impersonation Creating and managing event filters and subscriptions Adding custom functionality through decorators ### Initialization & Status Always wait for node initialization using the `ready()` method before interacting with other node components. ```ts import { createTevmNode } from 'tevm' // Create a node instance const node = createTevmNode() // Wait for initialization to complete await node.ready() // Check the current status console.log(`Node status: ${node.status}`) // 'READY' // Access the logger for debugging node.logger.debug('Node successfully initialized') ``` The node status can be one of the following values: * `INITIALIZING`: Node is starting up and components are being created * `READY`: Node is fully initialized and ready for use * `SYNCING`: Node is synchronizing state (usually in fork mode) * `MINING`: Node is currently mining a block * `STOPPED`: Node has been stopped or encountered a fatal error ### Virtual Machine Access The VM (Virtual Machine) is the heart of Tevm, providing direct access to EVM execution capabilities. ```ts import { createTevmNode } from 'tevm' import { createAddress } from 'tevm/address' import { hexToBytes } from 'viem' const node = createTevmNode() await node.ready() // Get access to the VM const vm = await node.getVm() // Execute a transaction directly const txResult = await vm.runTx({ tx: { to: createAddress('0x1234567890123456789012345678901234567890'), value: 1000000000000000000n, // 1 ETH nonce: 0n, gasLimit: 21000n, gasPrice: 10000000000n } }) console.log('Transaction executed:', txResult.execResult.exceptionError ? `Failed: ${txResult.execResult.exceptionError}` : 'Success!') // Execute EVM bytecode directly const evmResult = await vm.evm.runCall({ to: createAddress('0x1234567890123456789012345678901234567890'), caller: createAddress('0x5678901234567890123456789012345678901234'), data: hexToBytes('0xa9059cbb000000000000000000000000abcdef0123456789abcdef0123456789abcdef0000000000000000000000000000000000000000000000008ac7230489e80000'), // transfer(address,uint256) gasLimit: 100000n, }) console.log('EVM call result:', evmResult.execResult) // Hook into EVM execution for debugging vm.evm.events.on('step', (data, next) => { console.log(`${data.pc}: ${data.opcode.name}`) next?.() // Continue to next step }) ``` ### Transaction Pool Management The Transaction Pool (TxPool) keeps track of pending transactions before they are included in blocks. ```ts import { createTevmNode } from 'tevm' import { parseEther } from 'viem' const node = createTevmNode() await node.ready() // Get the transaction pool const txPool = await node.getTxPool() // Add transactions to the pool await txPool.add({ from: '0x1234567890123456789012345678901234567890', to: '0x5678901234567890123456789012345678901234', value: parseEther('1.5'), gasLimit: 21000n, maxFeePerGas: 30000000000n, }) // Check pool content const pendingTxs = await txPool.content() console.log('Pending transactions:', pendingTxs.pending) // Get ordered transactions (by price and nonce) const orderedTxs = await txPool.txsByPriceAndNonce() console.log('Ordered transactions:', orderedTxs) // Get pending transactions for a specific address const txsForAddress = await txPool.contentFrom('0x1234567890123456789012345678901234567890') console.log('Transactions for address:', txsForAddress) ``` ### Receipt & Log Management The ReceiptsManager tracks transaction receipts and logs, allowing you to query and filter events. ```ts import { createTevmNode } from 'tevm' const node = createTevmNode() await node.ready() // Get the receipts manager const receiptsManager = await node.getReceiptsManager() // After a transaction is executed, get its receipt const receipt = await receiptsManager.getReceiptByTxHash('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') // Query logs with filters const transferLogs = await receiptsManager.getLogs({ fromBlock: 0n, toBlock: 'latest', address: '0x1234567890123456789012345678901234567890', // Optional: contract address topics: [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer event signature null, // Any from address '0x0000000000000000000000005678901234567890123456789012345678901234' // Filter by to address ], }) console.log('Transfer logs:', transferLogs) // Create a subscription for new logs const subId = await receiptsManager.newLogSubscription({ address: '0x1234567890123456789012345678901234567890', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }) // Handle new matching logs receiptsManager.on('log', (log) => { if (log.subscriptionId === subId) { console.log('New transfer detected:', log) } }) ``` ### Account Impersonation Account impersonation lets you send transactions as any address in fork mode, without needing the private key. ```ts import { createTevmNode, http } from 'tevm' import { parseEther } from 'viem' // Create a forked node const node = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/YOUR-KEY'), }, }) await node.ready() // Impersonate a known address (like a whale or contract owner) node.setImpersonatedAccount('0x28C6c06298d514Db089934071355E5743bf21d60') // Example whale address // Get the VM const vm = await node.getVm() // Send a transaction as the impersonated account const txResult = await vm.runTx({ tx: { from: '0x28C6c06298d514Db089934071355E5743bf21d60', // Impersonated address to: '0x1234567890123456789012345678901234567890', value: parseEther('10'), gasLimit: 21000n, }, }) console.log('Transaction result:', txResult.execResult) // Check if an address is being impersonated const currentImpersonated = node.getImpersonatedAccount() console.log('Currently impersonating:', currentImpersonated) // Stop impersonating node.setImpersonatedAccount(undefined) ``` ### Event Filtering Event filters allow you to track and query blockchain events based on various criteria. ```ts import { createTevmNode } from 'tevm' import { formatEther, parseEther } from 'viem' const node = createTevmNode() await node.ready() // Create a filter for all "Transfer" events node.setFilter({ id: '0x1', // Custom ID fromBlock: 0n, toBlock: 'latest', address: '0x1234567890123456789012345678901234567890', // Optional: Filter by contract topics: [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer event signature ], }) // Create a filter for a specific address receiving tokens node.setFilter({ id: '0x2', fromBlock: 0n, toBlock: 'latest', topics: [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer null, // Any sender '0x0000000000000000000000001234567890123456789012345678901234567890', // Specific recipient (padded) ], }) // Get all active filters const filters = node.getFilters() console.log(`Active filters: ${filters.size}`) // After executing transactions, get the logs from a filter const receiptManager = await node.getReceiptsManager() const logs = await receiptManager.getFilterLogs('0x1') // Format and display the transfer logs logs.forEach(log => { // Decode Transfer(address,address,uint256) const from = '0x' + log.topics[1].slice(26) const to = '0x' + log.topics[2].slice(26) const value = BigInt(log.data) console.log(`Transfer: ${from} -> ${to}: ${formatEther(value)} ETH`) }) // Remove a filter when done node.removeFilter('0x1') ``` ### Extensibility The extend method allows you to add custom functionality to any TevmNode instance, creating powerful abstractions. ```ts import { createTevmNode } from 'tevm' import { parseEther, formatEther } from 'viem' const node = createTevmNode() await node.ready() // Extend the node with custom methods const enhancedNode = node.extend((baseNode) => ({ // Add a method to get an account's balance async getBalance(address) { const vm = await baseNode.getVm() const account = await vm.stateManager.getAccount(address) return account.balance }, // Add a method to transfer ETH between accounts async transferETH(from, to, amount) { const vm = await baseNode.getVm() // Execute the transfer const result = await vm.runTx({ tx: { from, to, value: amount, gasLimit: 21000n, } }) return { success: !result.execResult.exceptionError, gasUsed: result.gasUsed, } }, // Add a method to get all account balances async getAllBalances(addresses) { const results = {} for (const addr of addresses) { results[addr] = formatEther(await this.getBalance(addr)) } return results } })) // Use the extended methods const balance = await enhancedNode.getBalance('0x1234567890123456789012345678901234567890') console.log(`Balance: ${formatEther(balance)} ETH`) // Transfer ETH const transfer = await enhancedNode.transferETH( '0x1234567890123456789012345678901234567890', '0x5678901234567890123456789012345678901234', parseEther('1.5') ) console.log(`Transfer ${transfer.success ? 'succeeded' : 'failed'}, gas used: ${transfer.gasUsed}`) // Get multiple balances at once const balances = await enhancedNode.getAllBalances([ '0x1234567890123456789012345678901234567890', '0x5678901234567890123456789012345678901234' ]) console.log('Account balances:', balances) ``` ### State Management The deepCopy method creates fully independent node instances with identical state, perfect for testing different scenarios. ```ts import { createTevmNode } from 'tevm' import { parseEther } from 'viem' // Create a base node and perform initial setup const baseNode = createTevmNode() await baseNode.ready() // Set up initial state const vm = await baseNode.getVm() await vm.stateManager.putAccount( '0x1234567890123456789012345678901234567890', { nonce: 0n, balance: parseEther('100'), codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' } ) // Create an independent copy for a specific test scenario const scenarioNode = await baseNode.deepCopy() // Modify state in the copy (without affecting the original) const scenarioVm = await scenarioNode.getVm() await scenarioVm.stateManager.putAccount( '0x1234567890123456789012345678901234567890', { nonce: 0n, balance: parseEther('200'), // Different balance in this scenario codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' } ) // Both nodes now have independent state const originalAccount = await (await baseNode.getVm()).stateManager.getAccount( '0x1234567890123456789012345678901234567890' ) const modifiedAccount = await (await scenarioNode.getVm()).stateManager.getAccount( '0x1234567890123456789012345678901234567890' ) console.log('Original balance:', originalAccount.balance) // 100 ETH console.log('Scenario balance:', modifiedAccount.balance) // 200 ETH ``` ### Practical Examples :::code-group ```ts [Contract Deployment] import { createTevmNode } from 'tevm' import { hexToBytes } from 'viem' const node = createTevmNode() await node.ready() // ERC20 contract bytecode (simplified for example) const bytecode = '0x60806040...' // Contract bytecode (truncated) // Deploy the contract const vm = await node.getVm() const deployResult = await vm.runTx({ tx: { nonce: 0n, gasLimit: 2000000n, gasPrice: 10000000000n, data: hexToBytes(bytecode), } }) if (deployResult.execResult.exceptionError) { throw new Error(`Deployment failed: ${deployResult.execResult.exceptionError}`) } // Get the created contract address const contractAddress = deployResult.createdAddress console.log(`Contract deployed at: ${contractAddress}`) // Now you can interact with the contract const callResult = await vm.runTx({ tx: { to: contractAddress, data: hexToBytes('0x70a08231000000000000000000000000' + '1234567890123456789012345678901234567890'.slice(2)), // balanceOf(address) gasLimit: 100000n, } }) console.log('Call result:', callResult.execResult.returnValue) ``` ```ts [Contract Debugging] import { createTevmNode } from 'tevm' import { hexToBytes } from 'viem' const node = createTevmNode() await node.ready() const vm = await node.getVm() // Set up debugging hooks const opcodeCounts = {} const memoryAccesses = [] const stackChanges = [] vm.evm.events.on('step', (data, next) => { // Count opcodes opcodeCounts[data.opcode.name] = (opcodeCounts[data.opcode.name] || 0) + 1 // Track memory operations if (['MLOAD', 'MSTORE', 'MSTORE8'].includes(data.opcode.name)) { memoryAccesses.push({ op: data.opcode.name, offset: data.stack[data.stack.length - 1]?.toString(16), }) } // Track stack changes if (data.opcode.name === 'PUSH1') { stackChanges.push({ pc: data.pc, op: data.opcode.name, value: data.opcode.pushValue?.toString(16), }) } // Continue execution next?.() }) // Execute a transaction with the debugging hooks active const result = await vm.runTx({ tx: { to: '0x1234567890123456789012345678901234567890', // Contract address data: hexToBytes('0xa9059cbb000000000000000000000000abcdef0123456789abcdef0123456789abcdef0000000000000000000000000000000000000000000000008ac7230489e80000'), // transfer(address,uint256) gasLimit: 100000n, } }) // Output debugging information console.log('Execution complete') console.log(`Gas used: ${result.gasUsed}`) console.log('Most common opcodes:', Object.entries(opcodeCounts).sort((a, b) => b[1] - a[1]).slice(0, 5)) console.log('Memory accesses:', memoryAccesses.length) console.log('PUSH operations:', stackChanges.length) // Clean up vm.evm.events.removeAllListeners('step') ``` ```ts [Chain Simulation] import { createTevmNode } from 'tevm' import { parseEther } from 'viem' async function simulateICO() { // Create a fresh node for the simulation const node = createTevmNode({ miningConfig: { type: 'auto' } // Auto-mine transactions }) await node.ready() const vm = await node.getVm() // Setup accounts const deployer = '0x1111111111111111111111111111111111111111' const investors = [ '0x2222222222222222222222222222222222222222', '0x3333333333333333333333333333333333333333', '0x4444444444444444444444444444444444444444', ] // Fund accounts await vm.stateManager.putAccount(deployer, { nonce: 0n, balance: parseEther('100'), codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' }) for (const investor of investors) { await vm.stateManager.putAccount(investor, { nonce: 0n, balance: parseEther('10'), codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', storageRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' }) } // Deploy ICO contract (simplified) // In reality, you would deploy a real contract here console.log('Deploying ICO contract...') // Simulate investors participating for (const investor of investors) { console.log(`Investor ${investor} participating...`) // In a real simulation, you would call the contract's participate method // This is a simplified example await vm.runTx({ tx: { from: investor, to: deployer, // In reality, this would be the contract address value: parseEther('5'), gasLimit: 21000n, } }) } // Check final state const deployerAccount = await vm.stateManager.getAccount(deployer) console.log(`Deployer final balance: ${deployerAccount.balance}`) for (const investor of investors) { const investorAccount = await vm.stateManager.getAccount(investor) console.log(`Investor ${investor} final balance: ${investorAccount.balance}`) } // In a real simulation, you would also check token balances } // Run the simulation simulateICO().catch(console.error) ``` ::: ### Best Practices Following these best practices will help you avoid common pitfalls when working with the TevmNode interface. #### 1. Initialization Flow Always wait for the node to be fully initialized before accessing components: ```ts const node = createTevmNode() await node.ready() // Essential: Ensures all components are initialized // Now safe to use the node const vm = await node.getVm() ``` #### 2. Error Handling Implement robust error handling for EVM operations: ```ts try { const vm = await node.getVm() const result = await vm.runTx({ tx: { /* ... */ } }) if (result.execResult.exceptionError) { console.error(`Execution failed: ${result.execResult.exceptionError}`) console.error(`At PC: ${result.execResult.exceptionError.pc}`) // Handle the specific error } } catch (error) { // Handle unexpected errors console.error('Unexpected error:', error.message) } ``` #### 3. Resource Management Clean up resources when they're no longer needed: ```ts // Remove filters when done node.getFilters().forEach((_, id) => node.removeFilter(id)) // Remove event listeners const vm = await node.getVm() vm.evm.events.removeAllListeners('step') // For subscriptions const receiptsManager = await node.getReceiptsManager() receiptsManager.removeAllListeners('log') ``` #### 4. State Isolation Use `deepCopy` for testing different scenarios: ```ts const baseNode = createTevmNode() await baseNode.ready() // Set up initial state // ... // For each test case, create an independent copy async function runTestCase(scenario) { const testNode = await baseNode.deepCopy() // Modify state for this specific test // ... // Run the test // ... // Each test has isolated state that doesn't affect other tests } ``` #### 5. Optimizing Performance For heavy workloads, consider these optimizations: ```ts // Disable profiling when not needed const node = createTevmNode({ profiler: false, }) // Use direct VM access for bulk operations const vm = await node.getVm() const stateManager = vm.stateManager // Batch state changes const addresses = ['0x1111...', '0x2222...', '0x3333...'] for (const address of addresses) { await stateManager.putAccount(address, { // Account data }) } // Only mine when necessary (if using manual mining) await node.mine({ blocks: 1 }) ``` ### Type Safety The `TevmNode` interface is fully typed with TypeScript, providing excellent development-time safety: ```ts import type { TevmNode } from 'tevm/node' // Function that works with any TevmNode function setupNode( node: TevmNode ) { return async () => { await node.ready() // Fork-specific operations with type checking if (node.mode === 'fork') { node.setImpersonatedAccount('0x...') return { mode: 'fork', impersonatedAccount: node.getImpersonatedAccount() } } return { mode: 'normal' } } } // With extension types function createEnhancedNode() { const baseNode = createTevmNode() const enhancedNode = baseNode.extend((base) => ({ async getBalance(address: string): Promise { const vm = await base.getVm() const account = await vm.stateManager.getAccount(address) return account.balance } })) // TypeScript knows enhancedNode has getBalance method return enhancedNode } ``` ### Next Steps Learn how to create and manage forks from live Ethereum networks Understand the state management capabilities of Tevm Explore the different block mining strategies Use Tevm's Ethereum-compatible JSON-RPC interface Work with low-level EVM execution events Add custom precompiled contracts to extend EVM functionality import { Callout, Steps, Step, Button } from 'vocs/components' ## Custom Precompiles Tevm Node allows you to extend the Ethereum Virtual Machine with custom JavaScript functions that behave like native precompiled contracts. Precompiled contracts are special EVM contracts with native implementations at fixed addresses. Tevm lets you create your own precompiles in JavaScript to add custom functionality, improve performance, or implement new features directly in the EVM. ### What Are Precompiles? ### Precompile Types * **Ethereum Native** ๐Ÿงฉ - Standard precompiles include hash functions, curve operations, and more at reserved addresses * **Custom JavaScript** โšก - Tevm lets you add your own JavaScript functions as precompiles * **Performance** ๐Ÿš€ - Native code is much faster than EVM bytecode for complex operations * **Cross-Chain** ๐ŸŒ‰ - Many L2s use precompiles to implement chain-specific functionality ### Quick Start #### Basic Example ```typescript showLineNumbers {1-6,9-18,21-22,25-32} filename="simple-precompile.ts" import { createTevmNode, definePrecompile } from 'tevm' import { createAddress } from 'tevm/address' import { createContract } from 'tevm/contract' import { parseAbi } from 'tevm/utils' import { createImpersonatedTx } from 'tevm/tx' import { EvmError, EvmErrorMessage } from 'tevm/evm' // Create a basic precompile that doubles each byte const customPrecompile = definePrecompile({ // [!code focus] contract: createContract({ // [!code focus] abi: parseAbi(['function double(bytes) returns (bytes)']), // [!code focus] address: '0x0000000000000000000000000000000000000123' // [!code focus] }), // [!code focus] call: async ({ data }) => { // [!code focus] const input = Array.from(data) // [!code focus] return { // [!code focus] returnValue: new Uint8Array(input.map(byte => Number(byte) * 2)), // [!code focus] executionGasUsed: 200n, // [!code focus] } // [!code focus] }, // [!code focus] }) // Create node with the precompile const node = createTevmNode({ // [!code focus] customPrecompiles: [customPrecompile.precompile()], // [!code focus] }) // Create a transaction to call the precompile const tx = createImpersonatedTx({ // [!code focus] impersonatedAddress: createAddress('0x1234567890123456789012345678901234567890'), // [!code focus] to: customPrecompile.contract.address, // [!code focus] data: '0x00', // [!code focus] gasLimit: 21000n, // [!code focus] }) // [!code focus] // Execute the transaction const vm = await node.getVm() // [!code focus] const result = await vm.runTx({ tx }) // [!code focus] ``` The definePrecompile function provides full type safety based on the provided ABI, making your precompiles safer and easier to use. #### Complex Example ```typescript showLineNumbers {9-22,25-26} filename="advanced-precompile.ts" import { createTevmNode, definePrecompile } from 'tevm' import { createAddress } from 'tevm/address' import { createContract } from 'tevm/contract' import { parseAbi, hexToBytes } from 'tevm/utils' import { createImpersonatedTx } from 'tevm/tx' import { EvmError, EvmErrorMessage } from 'tevm/evm' import { keccak256 } from 'tevm/crypto' // Create a cryptographic hash precompile const hashPrecompile = definePrecompile({ // [!code focus] contract: createContract({ // [!code focus] abi: parseAbi(['function hash(bytes) returns (bytes32)']), // [!code focus] address: '0x0000000000000000000000000000000000000321' // [!code focus] }), // [!code focus] call: async ({ data, gasLimit }) => { // [!code focus] // Calculate gas cost based on input size const gasPerByte = 10n const gasUsed = BigInt(data.length) * gasPerByte + 100n // Base cost + per-byte cost // Perform the hash operation const hash = keccak256(data) return { returnValue: hash, executionGasUsed: gasUsed } // [!code focus] }, // [!code focus] }) // Create a node with multiple precompiles const node = createTevmNode({ // [!code focus] customPrecompiles: [customPrecompile.precompile(), hashPrecompile.precompile()], // [!code focus] }) ``` ### Precompile Interface When creating a precompile, you need to define both the contract interface (ABI) and the implementation function. #### Define the Contract Interface Every precompile needs an ABI and address: ```typescript showLineNumbers {1-4} filename="interface.ts" const contract = createContract({ abi: parseAbi(['function myFunction(uint256) returns (uint256)']), address: '0x0000000000000000000000000000000000000123' }) ``` #### Implement the Call Handler The call handler receives the input data and gas limit: ```typescript showLineNumbers {1-10} filename="handler.ts" const call = async ({ data, gasLimit }: PrecompileInput): Promise => { // Process input data // ... return { returnValue: new Uint8Array([/* result data */]), executionGasUsed: 1000n, // Optional: exceptionError for when the operation fails } } ``` #### Create and Register the Precompile Combine both parts and register with a Tevm Node: ```typescript showLineNumbers {1-8} filename="register.ts" const myPrecompile = definePrecompile({ contract, call }) // Create a node with the precompile const node = createTevmNode({ customPrecompiles: [ myPrecompile.precompile() ] }) ``` ### Example Implementations #### State Access Example ```typescript showLineNumbers {1-20} filename="state-precompile.ts" const statePrecompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function store(bytes32,bytes32)']), address: '0x0000000000000000000000000000000000000124' }), call: async ({ data, gasLimit }) => { // Extract key and value from input data const key = data.slice(0, 32) const value = data.slice(32) // Get VM and state manager const vm = await node.getVm() // Store the value at the specified key await vm.stateManager.putContractStorage( createAddress(statePrecompile.contract.address), key, value ) return { returnValue: new Uint8Array(), executionGasUsed: 200n } }, }) ``` This precompile demonstrates how to access and modify blockchain state from within a precompile. #### Gas Calculation Example ```typescript showLineNumbers {1-16} filename="gas-precompile.ts" const gasPrecompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function processWithGas(bytes)']), address: '0x0000000000000000000000000000000000000125' }), call: async ({ data, gasLimit }) => { // Charge 100 gas per byte const gasUsed = BigInt(data.length * 100) // Check if we have enough gas if (gasUsed > gasLimit) { return { returnValue: new Uint8Array(), exceptionError: new EvmError(EvmErrorMessage.OUT_OF_GAS), executionGasUsed: gasLimit, } } return { returnValue: new Uint8Array(), executionGasUsed: gasUsed } }, }) ``` Always check if there's enough gas before executing operations, and return the exact amount of gas used. #### Error Handling Example ```typescript showLineNumbers {1-21} filename="error-precompile.ts" const errorPrecompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function process(bytes)']), address: '0x0000000000000000000000000000000000000126' }), call: async ({ data, gasLimit }) => { try { // Validate input if (data.length === 0) { return { returnValue: new Uint8Array(), exceptionError: new EvmError('Custom error: Empty input not allowed'), executionGasUsed: 200n, } } // Process data return { returnValue: processData(data), executionGasUsed: 200n } } catch (error) { // Handle unexpected errors return { returnValue: new Uint8Array(), exceptionError: new EvmError(`Precompile error: ${error.message}`), executionGasUsed: gasLimit, } } }, }) ``` #### Multiple Precompiles Example ```typescript showLineNumbers {1-22} filename="multiple-precompiles.ts" // First precompile const precompileA = definePrecompile({ contract: createContract({ abi: parseAbi(['function processA() returns (bytes)']), address: '0x0000000000000000000000000000000000000127' }), call: async () => ({ returnValue: new Uint8Array([1]), executionGasUsed: 200n, }), }) // Second precompile const precompileB = definePrecompile({ contract: createContract({ abi: parseAbi(['function processB() returns (bytes)']), address: '0x0000000000000000000000000000000000000128' }), call: async () => ({ returnValue: new Uint8Array([2]), executionGasUsed: 200n, }), }) // Register both precompiles const node = createTevmNode({ customPrecompiles: [precompileA.precompile(), precompileB.precompile()], }) ``` ### Use Cases #### Common Use Cases * **๐Ÿ” Cryptographic Operations** - Implement efficient cryptographic operations like encryption, hashing, or signature verification * **๐Ÿ”ฎ Oracle Functionality** - Simulate oracles or external data sources during local testing * **๐Ÿงฎ Complex Math** - Perform complex mathematical calculations that would be gas-intensive in Solidity * **๐ŸŒ‰ Cross-Chain Bridges** - Simulate cross-chain verification logic for testing bridge implementations * **๐Ÿ“Š Custom Data Structures** - Implement efficient data structure operations (trees, graphs, etc.) * **๐Ÿงช Testing Helpers** - Create special testing functions like time manipulation or state snapshots ### Best Practices #### Gas Calculation Always calculate gas based on the actual work performed, similar to how the EVM charges gas for operations. ```typescript showLineNumbers {1-18} filename="gas-best-practices.ts" const precompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function process(bytes)']), address: createAddress('0x0000000000000000000000000000000000000123') }), call: async ({ data, gasLimit }) => { // Calculate gas based on input size and operations const baseGas = 100n; // Base cost const dataGas = BigInt(data.length * 10); // Per-byte cost const totalGas = baseGas + dataGas; // Total cost // Check gas limit if (totalGas > gasLimit) { return { returnValue: new Uint8Array(), exceptionError: new EvmError(EvmErrorMessage.OUT_OF_GAS), executionGasUsed: gasLimit, } } // Process data return { returnValue: processData(data), executionGasUsed: totalGas } }, }) ``` #### Error Handling Use appropriate error types and include detailed error information. ```typescript showLineNumbers {1-24} filename="error-best-practices.ts" const precompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function process(bytes32,uint256)']), address: createAddress('0x0000000000000000000000000000000000000123') }), call: async ({ data, gasLimit }) => { try { // Validate input format if (data.length < 36) { return { returnValue: new Uint8Array(), exceptionError: new EvmError('Invalid input: insufficient data'), executionGasUsed: 100n, } } // Additional validation and processing // ... return { returnValue: result, executionGasUsed: gasUsed } } catch (error) { // Log error for debugging (will not be visible to the transaction caller) console.error('Precompile execution error:', error); // Return appropriate error to the EVM return { returnValue: new Uint8Array(), exceptionError: new EvmError(error.message || 'Unknown precompile error'), executionGasUsed: Math.min(100n, gasLimit), // Charge some minimum gas } } }, }) ``` #### State Management When your precompile needs to maintain state across calls. ```typescript showLineNumbers {1-20} filename="state-best-practices.ts" const statePrecompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function getData(bytes32) returns (bytes32)', 'function setData(bytes32,bytes32)']), address: createAddress('0x0000000000000000000000000000000000000124') }), call: async ({ data, gasLimit }) => { const vm = await node.getVm(); const stateManager = vm.stateManager; const address = createAddress(statePrecompile.contract.address); // Parse function selector const selector = data.slice(0, 4); const isGetData = selector[0] === 0x9b & selector[1] === 0x18 & selector[2] === 0x30 & selector[3] === 0x4c; if (isGetData) { // Read from state const key = data.slice(4, 36); const value = await stateManager.getContractStorage(address, key); return { returnValue: value, executionGasUsed: 200n }; } else { // Write to state const key = data.slice(4, 36); const value = data.slice(36, 68); await stateManager.putContractStorage(address, key, value); return { returnValue: new Uint8Array(), executionGasUsed: 500n }; } }, }) ``` #### Performance Optimize your precompile's performance to minimize resource usage. ```typescript showLineNumbers {1-15} filename="performance-best-practices.ts" const precompile = definePrecompile({ // Contract definition... call: async ({ data, gasLimit }) => { // For expensive operations, consider caching results const cacheKey = data.toString(); if (resultsCache.has(cacheKey)) { return resultsCache.get(cacheKey); } // Perform computation const result = performExpensiveOperation(data); // Cache the result resultsCache.set(cacheKey, { returnValue: result, executionGasUsed: gasUsed }); return { returnValue: result, executionGasUsed: gasUsed }; }, }) ``` ### Related Resources * [Contract Reference](/reference/contract) - Working with smart contracts in Tevm * [State Management](/core/managing-state) - Access and manipulate blockchain state * [JSON-RPC Support](/api/json-rpc) - Expose precompiles via JSON-RPC * [EVM Precompiles Reference](https://www.evm.codes/precompiled) - Standard Ethereum precompiled contracts import { Callout, Steps, Step, Button } from 'vocs/components' ## Performance & Profiler Tevm Node includes a powerful built-in profiler that helps you analyze EVM execution, identify bottlenecks, and optimize your smart contracts. The performance profiler tracks execution time, gas usage, and other metrics for every operation in the EVM, giving you deep insights into how your contracts perform. ### Quick Start #### Basic Setup ```typescript showLineNumbers {1,3-8,11,14,17,20} filename="basic-profiling.ts" import { createTevmNode } from 'tevm' const node = createTevmNode({ // [!code focus] profiler: { // [!code focus] enabled: true, // [!code focus] includeOpcodes: true, // Track individual opcodes // [!code focus] includePrecompiles: true, // Track precompiled contracts // [!code focus] } // [!code focus] }) // Execute some transactions or function calls const vm = await node.getVm() // [!code focus] // Run your contract operations await vm.runTx({ /* transaction details */ }) // [!code focus] // Get performance logs const logs = vm.evm.getPerformanceLogs() // [!code focus] // Clear logs when done vm.evm.clearPerformanceLogs() // [!code focus] ``` #### Complete Example ```typescript showLineNumbers {3-12,15-19,22-35} filename="contract-profiling.ts" import { createTevmNode } from 'tevm' // Create a node with profiling enabled const node = createTevmNode({ // [!code focus] profiler: { // [!code focus] enabled: true, // [!code focus] includeOpcodes: true, // [!code focus] includePrecompiles: true, // [!code focus] includeMemory: true, // Include memory operations // [!code focus] includeStorage: true, // Include storage operations // [!code focus] callStackDepth: 10, // Maximum call stack depth to track // [!code focus] } // [!code focus] }) // Set up the contract const vm = await node.getVm() // [!code focus] const deployTx = createTx({ /* deployment transaction */ }) // [!code focus] await vm.runTx({ tx: deployTx }) // [!code focus] const contractAddress = deployTx.createdAddress // [!code focus] // Profile a specific function call // Clear previous logs first vm.evm.clearPerformanceLogs() // [!code focus] // Call the contract function const callTx = createTx({ // [!code focus] to: contractAddress, // [!code focus] data: '0xa9059cbb000000000000000000000000123...', // function selector + params // [!code focus] }) // [!code focus] await vm.runTx({ tx: callTx }) // [!code focus] // Analyze the performance const logs = vm.evm.getPerformanceLogs() // [!code focus] const analysis = analyzePerformance(logs) // [!code focus] console.log(`Total execution time: ${analysis.totalTime}ms`) // [!code focus] console.log(`Total gas used: ${analysis.totalGas}`) // [!code focus] console.log(`Hotspots: ${JSON.stringify(analysis.hotspots)}`) // [!code focus] ``` ### Profiler Configuration * **โš™๏ธ Opcode Tracking** - Track execution time and gas usage for individual EVM opcodes * **๐Ÿ“ž Call Stack Analysis** - Profile the entire call tree to identify expensive contract interactions * **โ›ฝ Gas Analysis** - Identify operations that consume the most gas * **๐Ÿ’พ Memory Usage** - Track memory allocation and access patterns * **๐Ÿ’ฝ Storage I/O** - Measure the impact of storage reads and writes * **๐Ÿงฉ Precompile Usage** - Profile built-in and custom precompiled contracts You can configure which operations to profile based on your specific needs. For general purposes, enabling opcode and call tracking provides a good balance between detail and performance. ### Log Types #### Performance Log Base Type All log entries share these properties: ```typescript showLineNumbers {1-6} filename="log-types.ts" interface PerformanceLog { type: 'opcode' | 'precompile' | 'call' | 'create' startTime: number // Performance.now() start timestamp endTime: number // Performance.now() end timestamp executionTime: number // Duration in milliseconds gasUsed?: bigint // Gas consumed by this operation } ``` #### Opcode-Specific Logs Detailed information about each opcode execution: ```typescript showLineNumbers {1-5} filename="opcode-logs.ts" interface OpcodeLog extends PerformanceLog { type: 'opcode' opcode: string // Opcode name (e.g., "ADD", "SSTORE") pc: number // Program counter position stack?: bigint[] // Stack contents (if enabled) } ``` #### Call and Contract Logs Information about contract interactions: ```typescript showLineNumbers {1-7} filename="call-logs.ts" interface CallLog extends PerformanceLog { type: 'call' from: string // Caller address to: string // Target address value: bigint // ETH value transferred input: Uint8Array // Call data depth: number // Call stack depth } ``` #### Precompile Logs Information about precompiled contract executions: ```typescript showLineNumbers {1-5} filename="precompile-logs.ts" interface PrecompileLog extends PerformanceLog { type: 'precompile' address: string // Precompile address name: string // Precompile name if known input: Uint8Array // Input data } ``` ### Performance Analysis #### Opcode Analysis

Identify which opcodes consume the most time and gas:

```typescript showLineNumbers {2-12,15-21,23-24} filename="opcode-analysis.ts" // Group logs by opcode const opcodeStats = logs // [!code focus] .filter(log => log.type === 'opcode') // [!code focus] .reduce((acc, log) => { // [!code focus] const key = log.opcode // [!code focus] acc[key] = acc[key] || { count: 0, totalTime: 0, totalGas: 0n } // [!code focus] acc[key].count++ // [!code focus] acc[key].totalTime += log.executionTime // [!code focus] acc[key].totalGas += log.gasUsed ?? 0n // [!code focus] return acc // [!code focus] }, {}) // [!code focus] // Find most expensive operations by time const expensiveOpsByTime = Object.entries(opcodeStats) // [!code focus] .sort(([, a], [, b]) => b.totalTime - a.totalTime) // [!code focus] .slice(0, 5) // [!code focus] .map(([opcode, stats]) => ({ // [!code focus] opcode, // [!code focus] count: stats.count, // [!code focus] totalTimeMs: stats.totalTime.toFixed(2), // [!code focus] avgTimeMs: (stats.totalTime / stats.count).toFixed(2) // [!code focus] })) // [!code focus] console.log('Most time-consuming opcodes:') // [!code focus] console.table(expensiveOpsByTime) // [!code focus] ``` Storage operations (SLOAD, SSTORE), complex math (MULMOD, ADDMOD), and external calls (CALL, DELEGATECALL) are typically the most expensive operations. #### Call Tree Analysis

Analyze contract interactions and call patterns:

```typescript showLineNumbers {2-9,12-15,18-23} filename="call-analysis.ts" // Map call depth and patterns const callTree = logs // [!code focus] .filter(log => log.type === 'call') // [!code focus] .map(log => ({ // [!code focus] from: log.from, // [!code focus] to: log.to, // [!code focus] executionTime: log.executionTime, // [!code focus] gasUsed: log.gasUsed, // [!code focus] depth: log.depth // [!code focus] })) // Group by contract address const contractTimes = callTree.reduce((acc, call) => { // [!code focus] acc[call.to] = (acc[call.to] || 0) + call.executionTime // [!code focus] return acc // [!code focus] }, {}) // [!code focus] // Find most time-consuming contracts const slowestContracts = Object.entries(contractTimes) // [!code focus] .sort(([, a], [, b]) => b - a) // [!code focus] .slice(0, 3) // [!code focus] .map(([address, time]) => // [!code focus] `${address}: ${time.toFixed(2)}ms` // [!code focus] ) // [!code focus] ``` #### Gas Analysis

Track gas consumption patterns:

```typescript showLineNumbers {2-8,11-18} filename="gas-analysis.ts" // Track gas usage over time const gasTimeline = logs // [!code focus] .filter(log => log.gasUsed !== undefined) // [!code focus] .map(log => ({ // [!code focus] timestamp: log.startTime, // [!code focus] gasUsed: log.gasUsed, // [!code focus] type: log.type // [!code focus] })) // [!code focus] // Calculate gas efficiency by operation type const gasEfficiencyByType = gasTimeline.reduce((acc, log) => { // [!code focus] acc[log.type] = acc[log.type] || { totalGas: 0n, totalTime: 0, count: 0 } // [!code focus] acc[log.type].totalGas += log.gasUsed // [!code focus] acc[log.type].totalTime += log.executionTime // [!code focus] acc[log.type].count++ // [!code focus] return acc // [!code focus] }, {}) // [!code focus] // Calculate gas per millisecond for each operation type Object.entries(gasEfficiencyByType).forEach(([type, stats]) => { const gasPerMs = Number(stats.totalGas) / stats.totalTime console.log(`${type}: ${gasPerMs.toFixed(2)} gas/ms (${stats.count} operations)`) }) ``` ### Use Cases #### Smart Contract Optimization Profile your contract to identify performance bottlenecks and optimize the most expensive operations. ```typescript showLineNumbers {1-18} filename="contract-optimization.ts" // Deploy the contract first const vm = await node.getVm() const deployTx = createTx({ data: contractBytecode }) await vm.runTx({ tx: deployTx }) const contractAddress = deployTx.createdAddress // Clear logs before profiling the specific function vm.evm.clearPerformanceLogs() // Call the function you want to optimize const functionCallTx = createTx({ to: contractAddress, data: encodeFunctionData({ abi, functionName: 'expensiveFunction', args: [param1, param2] }) }) await vm.runTx({ tx: functionCallTx }) const logs = vm.evm.getPerformanceLogs() // Analyze to find optimization opportunities const hotspots = identifyHotspots(logs) console.log('Optimization targets:', hotspots) ``` #### Compare Different Implementations Compare different approaches to solve the same problem and choose the most efficient one. ```typescript showLineNumbers {1-21} filename="implementation-comparison.ts" async function compareImplementations(implementations) { const results = [] const vm = await node.getVm() for (const impl of implementations) { // Clear previous logs vm.evm.clearPerformanceLogs() // Deploy this implementation const deployTx = createTx({ data: impl.bytecode }) await vm.runTx({ tx: deployTx }) // Call with standard test case const callTx = createTx({ to: deployTx.createdAddress, data: impl.encodedFunctionCall }) await vm.runTx({ tx: callTx }) const logs = vm.evm.getPerformanceLogs() // Collect metrics results.push(analyzePerformance(logs, impl.name)) } return compareResults(results) } // Example output: // Implementation A: 1.2ms, 45,000 gas // Implementation B: 0.8ms, 32,000 gas (29% improvement) ``` #### Gas Optimization Identify and optimize operations that consume the most gas to make your contracts cheaper to use. ```typescript showLineNumbers {1-17} filename="gas-optimization.ts" // Find gas-intensive parts of your contract const contractGasUsage = logs .filter(log => log.type === 'opcode' && log.gasUsed) .reduce((acc, log) => { // Group by program counter to identify code locations const pcKey = log.pc.toString().padStart(4, '0') acc[pcKey] = acc[pcKey] || { opcode: log.opcode, count: 0, totalGas: 0n } acc[pcKey].count++ acc[pcKey].totalGas += log.gasUsed return acc }, {}) // Identify gas hotspots const gasHotspots = Object.entries(contractGasUsage) .sort(([, a], [, b]) => Number(b.totalGas - a.totalGas)) .slice(0, 10) ``` ### Best Practices #### Targeted Profiling For most accurate results, profile specific operations in isolation rather than entire sessions. ```typescript showLineNumbers {1-10} filename="targeted-profiling.ts" // Clear logs before the specific operation you want to profile vm.evm.clearPerformanceLogs() // Run only the operation you want to profile await vm.runTx({ tx: specificOperationTx }) // Analyze just that operation const logs = vm.evm.getPerformanceLogs() const analysis = analyzePerformance(logs) // Clear logs when done to prevent memory build-up vm.evm.clearPerformanceLogs() ``` #### Memory Management Profiling generates a lot of data. Implement strategies to manage memory usage. ```typescript showLineNumbers {1-21} filename="memory-management.ts" // For long-running sessions, clear logs periodically async function profileWithMemoryManagement(operations) { const results = [] for (const op of operations) { // Clear before each operation vm.evm.clearPerformanceLogs() // Run the operation await vm.runTx({ tx: op.tx }) // Process logs immediately const logs = vm.evm.getPerformanceLogs() const summary = summarizeLogs(logs) // Extract just what you need // Save only the summary results.push({ operation: op.name, summary }) // Clear immediately to free memory vm.evm.clearPerformanceLogs() } return results } ``` #### Comparative Analysis Always measure performance before and after optimization to verify improvements. ```typescript showLineNumbers {1-14} filename="comparative-analysis.ts" // Compare before and after optimization async function measureOptimizationImpact(originalCode, optimizedCode) { // Profile original implementation vm.evm.clearPerformanceLogs() await runImplementation(originalCode) const beforeLogs = vm.evm.getPerformanceLogs() const beforeMetrics = analyzePerformance(beforeLogs) // Profile optimized implementation vm.evm.clearPerformanceLogs() await runImplementation(optimizedCode) const afterLogs = vm.evm.getPerformanceLogs() const afterMetrics = analyzePerformance(afterLogs) // Calculate improvements return { timeImprovement: (1 - afterMetrics.totalTime / beforeMetrics.totalTime) * 100, gasImprovement: (1 - Number(afterMetrics.totalGas) / Number(beforeMetrics.totalGas)) * 100, details: compareMetrics(beforeMetrics, afterMetrics) } } ``` #### Production Use The profiler itself adds some overhead. Consider these guidelines for production use. ```typescript showLineNumbers {1-15} filename="production-profiling.ts" // For production environments const node = createTevmNode({ profiler: { // Only enable in specific environments enabled: process.env.ENABLE_PROFILING === 'true', // Use selective profiling to reduce overhead includeOpcodes: false, // Disable full opcode tracking samplingRate: 0.01, // Only profile 1% of operations includeMemory: false, // Skip memory tracking includeStorage: false, // Skip storage tracking // Only track high-level calls for a system overview includeHighLevelCalls: true } }) ``` ### Related Resources
import { Callout, Steps, Step, Button } from 'vocs/components' import { Card, CardGrid, Tab, TabGroup } from '../../../components' ## Receipts & Logs Tevm Node provides robust support for managing transaction receipts and event logs through the ReceiptsManager module and filter system. Transaction receipts and event logs are essential for tracking state changes and application events in Ethereum. Tevm's ReceiptsManager gives you powerful tools to work with them, enabling features like event listening, transaction status tracking, and log filtering. ### Quick Start ```typescript showLineNumbers {1-4,6-7,10-16,19-20,23-31} filename="basic-receipt-usage.ts" import { createTevmNode } from 'tevm' import { createImpersonatedTx } from 'tevm/tx' import { runTx } from 'tevm/vm' import { createAddress } from 'tevm/utils' const node = createTevmNode() // [!code focus] const receiptsManager = await node.getReceiptsManager() // [!code focus] // Execute a transaction const vm = await node.getVm() // [!code focus] const tx = createImpersonatedTx({ // [!code focus] impersonatedAddress: createAddress('0x1234567890123456789012345678901234567890'), // [!code focus] to: createAddress('0x2345678901234567890123456789012345678901'), // [!code focus] value: 1000000000000000000n, // 1 ETH // [!code focus] gasLimit: 21000n, // [!code focus] }) // [!code focus] // Run the transaction const result = await runTx(vm)({ tx }) // [!code focus] const txHash = tx.hash() // [!code focus] // Get the receipt const receiptResult = await receiptsManager.getReceiptByTxHash(txHash) // [!code focus] if (receiptResult) { // [!code focus] const [receipt, blockHash, txIndex, logIndex] = receiptResult // [!code focus] // Access receipt data console.log({ // [!code focus] status: 'status' in receipt ? receipt.status : undefined, // [!code focus] gasUsed: receipt.cumulativeBlockGasUsed, // [!code focus] logs: receipt.logs // [!code focus] }) // [!code focus] } // [!code focus] ``` ```typescript showLineNumbers {1-4,6-7,10-14,17-23,26-32} filename="log-filtering.ts" import { createTevmNode } from 'tevm' import { createImpersonatedTx } from 'tevm/tx' import { runTx } from 'tevm/vm' import { createAddress, hexToBytes } from 'tevm/utils' const node = createTevmNode() // [!code focus] const receiptsManager = await node.getReceiptsManager() // [!code focus] // Get blocks for filtering const vm = await node.getVm() // [!code focus] const fromBlock = await vm.blockchain.getBlockByTag('earliest') // [!code focus] const toBlock = await vm.blockchain.getBlockByTag('latest') // [!code focus] const contractAddress = createAddress('0x1234567890123456789012345678901234567890') // [!code focus] // Filter by contract address const addressLogs = await receiptsManager.getLogs( // [!code focus] fromBlock, // [!code focus] toBlock, // [!code focus] [contractAddress.toBytes()], // Filter by this address // [!code focus] undefined // No topic filter // [!code focus] ) // [!code focus] // Filter by event topic (event signature hash) const eventTopic = hexToBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef') // Transfer event // [!code focus] const topicLogs = await receiptsManager.getLogs( // [!code focus] fromBlock, // [!code focus] toBlock, // [!code focus] undefined, // Any address // [!code focus] [eventTopic] // Filter by this topic // [!code focus] ) // [!code focus] // Print results console.log(`Found ${addressLogs.length} logs from the contract`) console.log(`Found ${topicLogs.length} Transfer events`) ``` ### Receipt Types Tevm supports different receipt types across all Ethereum hard forks. Uses state root for transaction results Uses status codes (success/failure) Includes blob gas information ```typescript showLineNumbers {1-6,8-12,14-17} filename="receipt-types.ts" interface PreByzantiumReceipt { stateRoot: Uint8Array // Merkle root after transaction cumulativeBlockGasUsed: bigint logs: Log[] // Event logs // No status field } interface PostByzantiumReceipt { status: number // 1 for success, 0 for failure cumulativeBlockGasUsed: bigint logs: Log[] // Event logs } interface EIP4844Receipt extends PostByzantiumReceipt { blobGasUsed: bigint // Gas used for blob data blobGasPrice: bigint // Price paid for blob data } ``` ```typescript showLineNumbers {1-12} filename="receipt-handling.ts" // Function to handle different receipt types function processReceipt(receiptResult) { if (!receiptResult) return 'Receipt not found' const [receipt] = receiptResult if ('status' in receipt) { // Post-Byzantium receipt return `Transaction ${receipt.status === 1 ? 'succeeded' : 'failed'}` } else { // Pre-Byzantium receipt return `Transaction included with state root: 0x${Buffer.from(receipt.stateRoot).toString('hex')}` } } ``` Always check the receipt structure before accessing properties, as the type depends on the Ethereum hard fork configuration. ### Working with Event Logs #### Contract Deployment First, deploy a contract that emits events: ```typescript showLineNumbers {1-14} filename="deploy-contract.ts" // Deploy a contract that emits events const deployTx = createImpersonatedTx({ impersonatedAddress: createAddress('0x1234567890123456789012345678901234567890'), data: CONTRACT_BYTECODE, // Your contract bytecode gasLimit: 1000000n, }) const vm = await node.getVm() const deployResult = await runTx(vm)({ tx: deployTx }) // Check deployment success const contractAddress = deployResult.createdAddress if (!contractAddress) { throw new Error('Contract deployment failed') } ``` #### Emit Events Next, interact with the contract to emit events: ```typescript showLineNumbers {1-10} filename="emit-events.ts" // Call a function that emits events const interactTx = createImpersonatedTx({ impersonatedAddress: createAddress('0x1234567890123456789012345678901234567890'), to: contractAddress, // Function selector + parameters (e.g., transfer function on an ERC-20) data: '0xa9059cbb000000000000000000000000123456789012345678901234567890123456789000000000000000000000000000000000000000000000008ac7230489e80000', gasLimit: 100000n, }) await runTx(vm)({ tx: interactTx }) ``` #### Query Logs Then, query the logs using various filters: ```typescript showLineNumbers {1-25} filename="query-logs.ts" // Get the block range for filtering const fromBlock = await vm.blockchain.getBlockByTag('earliest') const toBlock = await vm.blockchain.getBlockByTag('latest') // 1. Get all logs from the contract const contractLogs = await receiptsManager.getLogs( fromBlock, toBlock, [contractAddress.toBytes()], undefined ) // 2. Get logs for a specific event (e.g., Transfer event) const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' const transferLogs = await receiptsManager.getLogs( fromBlock, toBlock, [contractAddress.toBytes()], [hexToBytes(transferEventSignature)] ) // 3. Get logs with specific parameters (e.g., transfers to a specific address) const receiverAddress = '0x1234567890123456789012345678901234567890' const paddedAddress = '0x000000000000000000000000' + receiverAddress.slice(2) const topicForReceiver = hexToBytes(paddedAddress) const transfersToReceiver = await receiptsManager.getLogs( fromBlock, toBlock, [contractAddress.toBytes()], [hexToBytes(transferEventSignature), undefined, topicForReceiver] ) ``` #### Process Log Data Finally, decode and process the log data: ```typescript showLineNumbers {1-19} filename="decode-logs.ts" // Process Transfer event logs for (const log of transferLogs) { // A Transfer event typically has this structure: // topic[0]: event signature // topic[1]: from address (padded to 32 bytes) // topic[2]: to address (padded to 32 bytes) // data: amount (as a 32-byte value) const from = '0x' + Buffer.from(log.topics[1]).toString('hex').slice(24) const to = '0x' + Buffer.from(log.topics[2]).toString('hex').slice(24) // Convert the data to a BigInt (amount) const amount = BigInt('0x' + Buffer.from(log.data).toString('hex')) console.log(`Transfer: ${from} โ†’ ${to}: ${amount} tokens`) // You can also access other metadata console.log(`Block: ${log.blockNumber}, TxIndex: ${log.txIndex}, LogIndex: ${log.logIndex}`) } ``` ### Advanced Features Ethereum logs support filtering on up to 4 topics, allowing for complex queries. ```typescript showLineNumbers {1-22} filename="complex-filtering.ts" // Get logs with complex filters: // 1. From a specific contract // 2. With Transfer event signature // 3. From a specific sender // 4. To a specific receiver const fromAddress = '0x1234567890123456789012345678901234567890' const toAddress = '0x2345678901234567890123456789012345678901' // Event topics with wildcards (undefined means "any value") const topics = [ hexToBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), // Transfer event hexToBytes('0x000000000000000000000000' + fromAddress.slice(2)), // From address (padded) hexToBytes('0x000000000000000000000000' + toAddress.slice(2)) // To address (padded) ] const filteredLogs = await receiptsManager.getLogs( fromBlock, toBlock, [contractAddress.toBytes()], // Contract address topics // Topic filters ) ``` Filter logs from multiple contracts at once. ```typescript showLineNumbers {1-21} filename="multi-contract-filtering.ts" // Track events across multiple contracts (e.g., a token and a marketplace) const tokenAddress = createAddress('0x1234567890123456789012345678901234567890') const marketplaceAddress = createAddress('0x2345678901234567890123456789012345678901') // Get Transfer events from either contract const transferEvent = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' const transfers = await receiptsManager.getLogs( fromBlock, toBlock, [tokenAddress.toBytes(), marketplaceAddress.toBytes()], // Multiple addresses [hexToBytes(transferEvent)] ) console.log('Events by contract:') const tokenEvents = transfers.filter(log => Buffer.from(log.address).toString('hex') === Buffer.from(tokenAddress.toBytes()).toString('hex') ) const marketplaceEvents = transfers.filter(log => Buffer.from(log.address).toString('hex') === Buffer.from(marketplaceAddress.toBytes()).toString('hex') ) ``` ReceiptsManager maintains indexes for efficient queries. ```typescript showLineNumbers {1-24} filename="receipt-indexing.ts" // The ReceiptsManager maintains indexes for: // 1. Transaction hash โ†’ receipt // 2. Block hash โ†’ receipts // 3. Address โ†’ logs // 4. Topics โ†’ logs // You can directly get a receipt by transaction hash const receipt = await receiptsManager.getReceiptByTxHash(txHash) // Or get all receipts in a block const block = await vm.blockchain.getLatestBlock() const blockHash = block.hash() const receipts = await receiptsManager.getBlockReceipts(blockHash) // Performance considerations: // - Receipt storage grows with blockchain size // - ReceiptsManager implements limits to prevent excessive resource usage // Built-in limits const GET_LOGS_LIMIT = 10000 // Maximum number of logs returned const GET_LOGS_LIMIT_MEGABYTES = 150 // Maximum response size const GET_LOGS_BLOCK_RANGE_LIMIT = 2500 // Maximum block range for queries // For large-scale applications, implement pagination or additional filtering ``` ### Best Practices Use specific filters and limit block ranges for better performance Always check for null/undefined results when working with receipts Check receipt types before accessing properties Implement pagination for large log queries #### Efficient Log Queries ```typescript showLineNumbers {1-12} filename="efficient-queries.ts" // Instead of querying the entire chain // Focus on specific block ranges when possible const latestBlock = await vm.blockchain.getLatestBlock() const blockNumber = latestBlock.header.number // Get logs from the last 100 blocks const fromBlock = await vm.blockchain.getBlock(blockNumber - 100n > 0n ? blockNumber - 100n : 0n) // Use specific filters (address + topics) const logs = await receiptsManager.getLogs( fromBlock, latestBlock, [contractAddress.toBytes()], [eventTopic] ) ``` #### Proper Error Handling ```typescript showLineNumbers {1-18} filename="error-handling.ts" // Handle missing receipts gracefully async function safeGetReceipt(txHash) { try { const receiptResult = await receiptsManager.getReceiptByTxHash(txHash) if (receiptResult === null) { console.log('Receipt not found - transaction may be pending or not exist') return null } const [receipt] = receiptResult return receipt } catch (error) { console.error('Error retrieving receipt:', error.message) // Handle specific error types if needed return null } } ``` #### Working with Receipt Types ```typescript showLineNumbers {1-18} filename="type-safety.ts" // Safely work with different receipt types function getTransactionStatus(receipt) { if (!receipt) return 'Unknown' if ('status' in receipt) { // Post-Byzantium receipt return receipt.status === 1 ? 'Success' : 'Failed' } else if ('stateRoot' in receipt) { // Pre-Byzantium receipt - check if it's the empty root const emptyRoot = '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' const actualRoot = '0x' + Buffer.from(receipt.stateRoot).toString('hex') return actualRoot === emptyRoot ? 'Likely Failed' : 'Likely Success' } return 'Unknown Format' } ``` ### Related Resources
## Transaction Pool The Transaction Pool (TxPool) is a crucial component that manages pending transactions before they're included in blocks. It handles transaction ordering, replacement, validation, and lifecycle management. ### Quick Start ```ts import { createTevmNode } from 'tevm' import { createAddress } from 'tevm/address' import { createImpersonatedTx } from 'tevm/tx' // Initialize node and get txpool const node = createTevmNode() const txPool = await node.getTxPool() // Create and add a transaction const tx = createImpersonatedTx({ impersonatedAddress: createAddress('0x1234...'), to: createAddress('0x2345...'), value: 1000000000000000000n, // 1 ETH gasLimit: 21000n, maxFeePerGas: 20000000000n, maxPriorityFeePerGas: 20000000000n, nonce: 0n, }) await txPool.addUnverified(tx) ``` ### Key Features * ๐Ÿ” **Transaction Validation** - Comprehensive validation including nonce, balance, and gas checks * ๐Ÿ”„ **Transaction Replacement** - Replace pending transactions with higher gas price versions * ๐Ÿ“Š **Nonce Ordering** - Maintains correct transaction sequence per account * ๐Ÿงน **Automatic Pruning** - Removes old transactions to prevent memory bloat * โšก **Performance Optimized** - Efficient handling of large transaction volumes ### Core Concepts #### Pool Limits The TxPool enforces several limits to ensure stable operation: ```ts const LIMITS = { MAX_POOL_SIZE: 5000, // Maximum total transactions MAX_TXS_PER_ACCOUNT: 100, // Maximum per account MIN_GAS_PRICE: 100000000n, // 0.1 GWei minimum TX_MAX_DATA_SIZE: 128 * 1024, // 128KB max transaction size } ``` #### Transaction Lifecycle 1. **Addition** - Transactions enter the pool via `add()` or `addUnverified()` 2. **Validation** - Optional checks for nonce, balance, and gas parameters 3. **Storage** - Valid transactions are stored and ordered by nonce 4. **Pruning** - Old transactions are removed after `POOLED_STORAGE_TIME_LIMIT` (20 minutes) ### Detailed Usage #### Adding Transactions Two methods for adding transactions, each with different validation levels: ```ts // Method 1: With full validation try { await txPool.add(tx) } catch (error) { if (error.message.includes('insufficient balance')) { console.error('Account has insufficient funds') } } // Method 2: Without validation (faster) await txPool.addUnverified(tx) ``` #### Transaction Replacement Replace a pending transaction by submitting a new one with the same nonce and higher gas price: ```ts const originalTx = createImpersonatedTx({ // ... base transaction params ... maxFeePerGas: 20000000000n, nonce: 0n, }) const replacementTx = createImpersonatedTx({ // ... same params as original ... maxFeePerGas: 30000000000n, // At least 10% higher nonce: 0n, // Same nonce as original }) await txPool.addUnverified(originalTx) await txPool.addUnverified(replacementTx) // Replaces originalTx ``` > **Note**: Replacement transactions must increase gas price by at least `MIN_GAS_PRICE_BUMP_PERCENT` (10%) #### Querying Transactions ```ts // Get transactions by sender const senderTxs = await txPool.getBySenderAddress(senderAddress) // Get transactions by hash const txHashes = [hash1, hash2] const specificTxs = txPool.getByHash(txHashes) // Get ordered transactions for mining const orderedTxs = await txPool.txsByPriceAndNonce({ baseFee: currentBaseFee, allowedBlobs: 6, // For EIP-4844 }) ``` #### Block Processing When new blocks are added, update the pool: ```ts import { mineHandler } from 'tevm/actions' // Mine new blocks await mineHandler(node)() // Remove included transactions txPool.removeNewBlockTxs(newBlocks) ``` ### Advanced Features #### Pool Management ```ts // Start transaction processing txPool.start() // Stop processing (but keep transactions) txPool.stop() // Clear all transactions txPool.close() // Manual cleanup of old transactions txPool.cleanup() ``` #### Transaction Types The pool supports all Ethereum transaction types: * Legacy Transactions * EIP-2930 (Access Lists) * EIP-1559 (Fee Market) * EIP-4844 (Blob Transactions) * Tevm Impersonated Transactions ### Best Practices #### 1. Transaction Creation Always use `createImpersonatedTx` with proper types: ```ts const tx = createImpersonatedTx({ impersonatedAddress: createAddress(address), to: createAddress(recipient), value: parseEther('1'), // Use helper functions for values gasLimit: 21000n, maxFeePerGas: gweiToWei('20'), maxPriorityFeePerGas: gweiToWei('2'), nonce: 0n, }) ``` #### 2. Error Handling Implement comprehensive error handling: ```ts try { await txPool.add(tx) } catch (error) { switch (true) { case error.message.includes('insufficient balance'): // Handle balance error break case error.message.includes('nonce too low'): // Handle nonce error break case error.message.includes('gas price too low'): // Handle gas price error break default: // Handle unknown errors } } ``` #### 3. Performance Optimization * Use `addUnverified` for bulk operations * Implement proper cleanup cycles * Monitor pool size and transaction age #### 4. Memory Management ```ts // Regular cleanup cycle setInterval(() => { txPool.cleanup() }, 5 * 60 * 1000) // Every 5 minutes // Monitor pool size const poolSize = txPool.txsInPool if (poolSize > MAX_POOL_SIZE * 0.8) { console.warn('Pool approaching capacity') } ``` ### API Reference #### TxPool Class ```ts class TxPool { constructor(options: { vm: Vm }) async add(tx: Transaction): Promise async addUnverified(tx: Transaction): Promise async getBySenderAddress(address: Address): Promise getByHash(hashes: Uint8Array[]): Transaction[] removeByHash(hash: string): void removeNewBlockTxs(blocks: Block[]): void start(): boolean stop(): boolean cleanup(): void close(): void } ``` ### Related Topics * [JSON-RPC Support](../api/json-rpc) * [VM & Submodules](../api/vm-and-submodules) * [Receipts & Logs](./receipts-and-logs) ## Account Management Tevm provides two key actions for managing account state: `getAccountHandler` and `setAccountHandler`. ### getAccountHandler The `getAccountHandler` action allows you to retrieve the current state of an account. #### Parameters ```ts type GetAccountParams = { // Required address of the account address: Address // Optional block tag to query state from blockTag?: 'latest' | 'pending' | 'earliest' | number // Whether to return storage (can be expensive) returnStorage?: boolean } ``` #### Return Type ```ts type GetAccountResult = { // Address of the account address: Address // Current nonce nonce: bigint // Balance in wei balance: bigint // Deployed bytecode (if contract) deployedBytecode: Hex // Storage root storageRoot: Hex // Code hash codeHash: Hex // Whether this is a contract isContract: boolean // Whether account is empty isEmpty: boolean // Storage (if returnStorage=true) storage?: { [key: Hex]: Hex } // Any errors that occurred errors?: TevmGetAccountError[] } ``` #### Example ```ts import { createTevmNode } from 'tevm' import { getAccountHandler } from 'tevm/actions' const node = createTevmNode() const account = await getAccountHandler(node)({ address: '0x...', blockTag: 'latest', returnStorage: true }) console.log('Balance:', account.balance) console.log('Nonce:', account.nonce) if (account.isContract) { console.log('Code:', account.deployedBytecode) console.log('Storage:', account.storage) } ``` ### setAccountHandler The `setAccountHandler` action allows you to modify account state directly. #### Parameters ```ts type SetAccountParams = { // Required address to modify address: Address // New nonce value nonce?: bigint // New balance in wei balance?: bigint // New deployed bytecode deployedBytecode?: Hex // New storage values state?: { [key: Hex]: Hex } } ``` #### Return Type ```ts type SetAccountResult = { // Any errors that occurred errors?: TevmSetAccountError[] } ``` #### Examples ##### 1. Setting Account Balance ```ts import { setAccountHandler } from 'tevm/actions' await setAccountHandler(node)({ address: '0x...', balance: parseEther('100') }) ``` ##### 2. Deploying Contract Code ```ts await setAccountHandler(node)({ address: contractAddress, deployedBytecode: '0x...', state: { // Initial storage values '0x0000...': '0x0000...' } }) ``` ##### 3. Modifying Multiple Properties ```ts await setAccountHandler(node)({ address: '0x...', nonce: 5n, balance: parseEther('10'), state: { [slot1]: value1, [slot2]: value2 } }) ``` ### Best Practices 1. **Storage Management**: ```ts // Avoid fetching storage unless needed const account = await getAccountHandler(node)({ address: '0x...', returnStorage: false // default }) ``` 2. **State Consistency**: ```ts // Check account exists before modifying const account = await getAccountHandler(node)({ address }) if (!account.isEmpty) { await setAccountHandler(node)({ address, balance: account.balance + amount }) } ``` 3. **Error Handling**: ```ts const result = await setAccountHandler(node)({ address: '0x...', balance: newBalance, throwOnFail: false }) if (result.errors) { console.error('Failed to set account:', result.errors) } ``` ### Related Topics * [State Management](../core/managing-state) * [Call API](./tevm-call) * [JSON-RPC Support](./json-rpc) ### See Also * [JSON-RPC API](/api/json-rpc) * [Client Types](#TODO) * [Actions Reference](/reference/actions) * [EIP-1193 Specification](https://eips.ethereum.org/EIPS/eip-1193) * [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) ## EVM Events Tevm Node provides access to low-level EVM events, allowing you to monitor and debug contract execution at a granular level. ### Available Events ```ts type EVMEvent = { // Emitted when a new contract is created newContract: (data: { address: Address, // Contract address code: Uint8Array // Contract bytecode }, next?: () => void) => void // Emitted before a message (call) is processed beforeMessage: (data: Message, next?: () => void) => void // Emitted after a message (call) is processed afterMessage: (data: EVMResult, next?: () => void) => void // Emitted on each EVM step (instruction execution) step: (data: InterpreterStep, next?: () => void) => void } ``` #### The InterpreterStep Object The `step` event handler receives a detailed `InterpreterStep` object with the following properties: ```ts interface InterpreterStep { // Program counter - current position in the bytecode pc: number // Current opcode information opcode: { name: string // Opcode name (e.g., 'SSTORE', 'CALL') fee: number // Base gas fee of the opcode dynamicFee?: bigint // Additional dynamic gas fee (if applicable) isAsync: boolean // Whether opcode is asynchronous } // Gas information gasLeft: bigint // Remaining gas gasRefund: bigint // Gas refund accumulator // EVM state stateManager: StateManager // Reference to the StateManager instance stack: Uint8Array[] // Current EVM stack contents returnStack: bigint[] // Return stack for RETURNSUB operations // Account information account: Account // Account which owns the code running address: Address // Address of the current account // Execution context depth: number // Current call depth (starts at 0 for top-level call) memory: Uint8Array // Current EVM memory contents memoryWordCount: bigint // Current size of memory in words (32 bytes) codeAddress: Address // Address of the code being executed // (differs from address in DELEGATECALL/CALLCODE) } ``` Where: * [`Address`](/reference/address) is Tevm's address type (from `tevm/address`) * [`StateManager`](/reference/state) is the state manager interface (from `tevm/state`) * [`Account`](/reference/node) is Tevm's account representation #### The NewContractEvent Object The `onNewContract` event handler receives a `NewContractEvent` object with the following properties: ```ts interface NewContractEvent { // Contract information address: Address // Address of the newly created contract code: Uint8Array // Deployed contract bytecode } ``` #### The Message Object The `beforeMessage` event handler receives a `Message` object with the following properties: ```ts interface Message { // Call information to?: Address // Target address (undefined for contract creation) value: bigint // Value sent with the call (in wei) caller: Address // Address of the account that initiated this call // Gas information gasLimit: bigint // Gas limit for this call // Data and code data: Uint8Array // Input data to the call code?: Uint8Array // Contract code for the call codeAddress?: Address // Address of contract code // Call type depth: number // Call depth isStatic: boolean // Whether the call is static (view) isCompiled: boolean // Whether this is precompiled contract code delegatecall: boolean // Whether this is a DELEGATECALL callcode: boolean // Whether this is a CALLCODE // Other salt?: Uint8Array // Salt for CREATE2 calls authcallOrigin?: Address // Origin address for AUTH calls } ``` #### The EVMResult Object The `afterMessage` event handler receives an `EVMResult` object with the following properties: ```ts interface EVMResult { execResult: { // Return information returnValue: Uint8Array // Return data from the call executionGasUsed: bigint // Gas used in execution gasRefund?: bigint // Gas refunded // Error information exceptionError?: { // Error encountered during execution error: string // Error type (e.g., 'revert', 'out of gas') errorType?: string // Additional error type information } // State logs?: Log[] // Logs emitted during execution selfdestruct?: Record // Self-destructed addresses gas?: bigint // Remaining gas } // Other information gasUsed: bigint // Total gas used (including intrinsic costs) createdAddress?: Address // Address of created contract (if any) gasRefund?: bigint // Total gas refunded } ``` ### Using with tevmCall Family The recommended way to access EVM events is through the tevmCall family of methods. Tevm offers two API styles for this: #### Client-based API (batteries included) ```ts import { createMemoryClient } from 'tevm' import { encodeFunctionData } from 'viem' const client = createMemoryClient() // Listen for EVM steps and other events during execution const result = await client.tevmCall({ to: contractAddress, data: encodeFunctionData({ abi, functionName: 'myFunction', args: [arg1, arg2] }), // Listen for EVM steps onStep: (step, next) => { console.log('EVM Step:', { pc: step.pc, // Program counter opcode: step.opcode, // Current opcode gasLeft: step.gasLeft, // Remaining gas stack: step.stack, // Stack contents depth: step.depth, // Call depth }) next?.() }, // Listen for contract creation onNewContract: (data, next) => { console.log('New contract deployed:', { address: data.address.toString(), codeSize: data.code.length, }) next?.() }, // Listen for message execution onBeforeMessage: (message, next) => { console.log('Executing message:', { to: message.to?.toString(), value: message.value.toString(), delegatecall: message.delegatecall, }) next?.() }, onAfterMessage: (result, next) => { console.log('Message result:', { gasUsed: result.execResult.executionGasUsed.toString(), returnValue: result.execResult.returnValue.toString('hex'), error: result.execResult.exceptionError?.error, }) next?.() } }) ``` #### Tree-shakable API ```ts import { tevmCall } from 'tevm/actions' import { createClient, custom } from 'viem' import { createTevmNode } from 'tevm/node' import { requestEip1193 } from 'tevm/decorators' import { encodeFunctionData } from 'viem' // Create Tevm Node with EIP-1193 support const node = createTevmNode().extend(requestEip1193()) // Create Viem client const client = createClient({ transport: custom(node), }) // Listen for EVM steps and other events during execution const result = await tevmCall(client, { to: contractAddress, data: encodeFunctionData({ abi, functionName: 'myFunction', args: [arg1, arg2] }), // Event handlers work the same way with both API styles onStep: (step, next) => { console.log('EVM Step:', { pc: step.pc, opcode: step.opcode, gasLeft: step.gasLeft, stack: step.stack, depth: step.depth, }) next?.() }, onNewContract: (data, next) => { console.log('New contract deployed:', { address: data.address.toString(), codeSize: data.code.length, }) next?.() }, onBeforeMessage: (message, next) => { console.log('Executing message:', { to: message.to?.toString(), value: message.value.toString(), delegatecall: message.delegatecall, }) next?.() }, onAfterMessage: (result, next) => { console.log('Message result:', { gasUsed: result.execResult.executionGasUsed.toString(), returnValue: result.execResult.returnValue.toString('hex'), error: result.execResult.exceptionError?.error, }) next?.() } }) ``` ### Advanced Examples #### Debugging You can create a debug tracer to collect comprehensive execution data: ```ts import { createMemoryClient } from 'tevm' import { tevmCall } from 'tevm/actions' const client = createMemoryClient() // Create a debug tracer const trace = { steps: [], contracts: new Set(), errors: [], } // Execute with tracing const result = await tevmCall(client, { to: contractAddress, data: '0x...', // Track each EVM step onStep: (step, next) => { trace.steps.push({ pc: step.pc, opcode: step.opcode.name, gasCost: step.opcode.fee, stack: step.stack.map(item => item.toString(16)), }) next?.() }, // Track contract creation onNewContract: (data, next) => { trace.contracts.add(data.address.toString()) next?.() }, // Track errors onAfterMessage: (result, next) => { if (result.execResult.exceptionError) { trace.errors.push({ error: result.execResult.exceptionError.error, returnData: result.execResult.returnValue.toString('hex'), }) } next?.() } }) console.log('Execution trace:', { stepCount: trace.steps.length, contracts: Array.from(trace.contracts), errors: trace.errors, }) ``` #### Gas Profiling Create a gas profiler to analyze opcode costs: ```ts import { createMemoryClient } from 'tevm' import { tevmCall } from 'tevm/actions' const client = createMemoryClient() // Create a gas profiler const profile = { opcodes: new Map(), totalGas: 0n, } // Execute with profiling const result = await tevmCall(client, { to: contractAddress, data: '0x...', onStep: (step, next) => { const opName = step.opcode.name const gasCost = BigInt(step.opcode.fee) const stats = profile.opcodes.get(opName) || { count: 0, totalGas: 0n } stats.count++ stats.totalGas += gasCost profile.totalGas += gasCost profile.opcodes.set(opName, stats) next?.() } }) // Get gas usage by opcode for (const [opcode, stats] of profile.opcodes) { console.log(`${opcode}:`, { count: stats.count, totalGas: stats.totalGas.toString(), percentageOfTotal: Number(stats.totalGas * 100n / profile.totalGas), }) } ``` #### Error Handling Handle errors in execution: ```ts import { createMemoryClient } from 'tevm' import { tevmCall } from 'tevm/actions' const client = createMemoryClient() const result = await tevmCall(client, { to: contractAddress, data: '0x...', onAfterMessage: (result, next) => { if (result.execResult.exceptionError) { const error = result.execResult.exceptionError switch (error.error) { case 'out of gas': console.error('Transaction ran out of gas') break case 'revert': console.error('Transaction reverted:', result.execResult.returnValue.toString('hex')) break case 'invalid opcode': console.error('Invalid opcode encountered') break default: console.error('Unknown error:', error) } } next?.() } }) ``` ### Best Practices 1. **Always Call Next** ```ts // Important: Call next to continue execution onStep: (step, next) => { // Process step... next?.() } ``` 2. **Handle Errors Gracefully** ```ts onStep: (step, next) => { try { // Process step... } catch (error) { console.error('Error processing step:', error) } next?.() } ``` 3. **Be Efficient** ```ts // Focus on information you need onStep: (step, next) => { // Only log SSTORE operations if (step.opcode.name === 'SSTORE') { console.log('Storage write:', { key: step.stack[step.stack.length - 1].toString(16), value: step.stack[step.stack.length - 2].toString(16) }) } next?.() } ``` ### Related Topics * [Tevm Call API](../api/tevm-call) * [Methods](../api/methods) * [Performance Profiler](../advanced/performance-profiler) * [Gas Estimation](../api/methods) ## JSON-RPC Support Tevm Node provides comprehensive [JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) support through an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible interface. This allows seamless integration with popular Ethereum libraries and tools. ### EIP-1193 Provider The node can be extended to expose an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compatible request interface: ```ts import { createTevmNode } from 'tevm' import { requestEip1193 } from 'tevm/decorators' const node = createTevmNode().extend(requestEip1193()) // Use standard JSON-RPC methods const blockNum = await node.request({ method: 'eth_blockNumber', params: [], }) ``` ### Supported Methods #### Core Ethereum Methods * **Block & Chain** * [`eth_blockNumber`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/blockNumberHandler.md) - Get current block number * [`eth_getBlockByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetBlockByHashProcedure.md) - Get block by hash * [`eth_getBlockByNumber`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetBlockByNumberProcedure.md) - Get block by number * [`eth_chainId`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/chainIdHandler.md) - Get current chain ID * **State & Account** * [`eth_getBalance`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getBalanceHandler.md) - Get account balance * [`eth_getCode`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getCodeHandler.md) - Get contract code * [`eth_getStorageAt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getStorageAtHandler.md) - Get storage value * [`eth_getTransactionCount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionCountProcedure.md) - Get account nonce * **Transaction** * [`eth_call`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethCallHandler.md) - Execute contract call * [`eth_estimateGas`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethEstimateGasProcedure.md) - Estimate gas usage * [`eth_sendTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethSendTransactionHandler.md) - Send transaction * [`eth_sendRawTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethSendRawTransaction.md) - Send signed transaction * [`eth_getTransactionByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionByHashProcedure.md) - Get transaction details * [`eth_getTransactionReceipt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionReceipt.md) - Get transaction receipt * **Logs & Events** * [`eth_getLogs`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetLogsHandler.md) - Get event logs * [`eth_newFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethNewFilterProcedure.md) - Create new filter * [`eth_newBlockFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethNewBlockFilterProcedure.md) - Create block filter * [`eth_getFilterChanges`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetFilterChangesProcedure.md) - Get filter updates * [`eth_getFilterLogs`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetFilterLogsProcedure.md) - Get all filter logs * [`eth_uninstallFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethUninstallFilterProcedure.md) - Remove filter #### Extended Methods Tevm also supports additional methods commonly found in development environments: * **Debug Methods** * [`debug_traceTransaction`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/debugTraceTransactionProcedure.md) - Trace transaction execution * [`debug_dumpState`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/debugDumpStateProcedure.md) - Dump current state * **Anvil Methods** (For [Foundry](https://book.getfoundry.sh/reference/anvil/) compatibility) * [`anvil_setCode`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetCodeProcedure.md) - Set contract code * [`anvil_setBalance`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetBalance.md) - Set account balance * [`anvil_setNonce`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetNonceProcedure.md) - Set account nonce * [`anvil_setStorageAt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilSetStorageAtProcedure.md) - Set storage value * [`anvil_impersonateAccount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilImpersonateAccount.md) - Impersonate account * [`anvil_stopImpersonatingAccount`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/anvilStopImpersonatingAccountProcedure.md) - Stop impersonating ### Client Integration #### Using with Viem For more information, see the [Viem Documentation](https://viem.sh/docs/clients/custom.html). ```ts import { createTevmNode } from 'tevm' import { createPublicClient, custom } from 'viem' import { requestEip1193 } from 'tevm/decorators' const node = createTevmNode().extend(requestEip1193()) const client = createPublicClient({ chain: mainnet, transport: custom(node.request), }) ``` #### Using with Ethers For more information, see the [Ethers Documentation](https://docs.ethers.org/v6/api/providers/#Provider). ```ts import { createTevmNode } from 'tevm' import { BrowserProvider } from 'ethers' import { requestEip1193 } from 'tevm/decorators' const node = createTevmNode().extend(requestEip1193()) const provider = new BrowserProvider(node) ``` ### Error Handling JSON-RPC errors follow the [standard format](https://www.jsonrpc.org/specification#error_object) and are fully typed. See the [error types documentation](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/JsonRpcError.md) for more details: ```ts interface JsonRpcError { code: number message: string data?: unknown } ``` Common error codes (see [Error Types](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorCodes.md)): * `-32700`: Parse error ([`ParseError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/ParseError.md)) * `-32600`: Invalid request ([`InvalidRequest`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InvalidRequest.md)) * `-32601`: Method not found ([`MethodNotFound`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/MethodNotFound.md)) * `-32602`: Invalid params ([`InvalidParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InvalidParams.md)) * `-32603`: Internal error ([`InternalError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/InternalError.md)) * `-32000` to `-32099`: Server error ([`ServerError`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/ServerError.md)) For detailed error handling examples and best practices, see the [Error Handling Guide](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorHandling.md). ### Best Practices 1. **Error Handling**: Always wrap RPC calls in try-catch blocks to handle potential errors gracefully. See [Error Types](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables/ErrorCodes.md) for all possible errors. 2. **Gas Estimation**: For transactions, use [`eth_estimateGas`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethEstimateGasProcedure.md) before sending to ensure sufficient gas: ```ts const gasEstimate = await node.request({ method: 'eth_estimateGas', params: [tx], }) ``` 3. **Receipt Confirmation**: Wait for transaction receipts to confirm state changes using [`eth_getTransactionReceipt`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethGetTransactionReceipt.md): ```ts const txHash = await node.request({ method: 'eth_sendTransaction', params: [tx], }) const receipt = await node.request({ method: 'eth_getTransactionReceipt', params: [txHash], }) ``` 4. **Event Filtering**: Use filters efficiently by: * Setting appropriate block ranges * Using specific [topics](https://docs.soliditylang.org/en/latest/abi-spec.html#events) * Cleaning up unused filters with [`eth_uninstallFilter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/ethUninstallFilterProcedure.md) For more examples and detailed API documentation, see: * [Complete Actions Documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) * [Type Definitions](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases) * [Function Reference](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions) * [Variables and Constants](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/variables) ### Related Topics * [Using with Viem](../examples/viem) * [Using with Ethers](../examples/ethers) * [Managing State](../core/managing-state) * [Receipts & Logs](../advanced/receipts-and-logs) * [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) * [EIP-1193: Ethereum Provider JavaScript API](https://eips.ethereum.org/EIPS/eip-1193) * [Tevm API Documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) ### Using Tevm Actions Tevm provides a set of high-level actions that can be imported from `tevm/actions`. See the [complete actions documentation](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) for all available actions. ```ts import { tevmCall, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/callHandler.md tevmMine, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/mineHandler.md tevmGetAccount, // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/getAccountHandler.md tevmSetAccount // See: https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/setAccountHandler.md } from 'tevm/actions' import { createTevmNode } from 'tevm' const node = createTevmNode() // Call a contract const result = await tevmCall(node, { to: '0x...', data: '0x...', value: 0n, createTransaction: true }) // Mine pending transactions await tevmMine(node) // Get account state const account = await tevmGetAccount(node, { address: '0x...', blockTag: 'latest' }) // Set account state await tevmSetAccount(node, { address: '0x...', balance: 100n, nonce: 0n, deployedBytecode: '0x...' }) ``` For detailed type information, see: * [`CallParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/CallParams.md) * [`MineParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/MineParams.md) * [`GetAccountParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/GetAccountParams.md) * [`SetAccountParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/type-aliases/SetAccountParams.md) Note: By default, tevm actions require manual mining via [`tevmMine()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/actions/docs/functions/mineHandler.md). If you want transactions to be automatically applied, you can either: 1. Use the lower level API `vm.runCall` 2. Configure the client with `miningConfig: { type: 'auto' }` ### Optimistic Updates with Receipt Manager For more information on transaction receipts and logs, see the [Ethereum Receipts Documentation](https://ethereum.org/en/developers/docs/transactions/transaction-receipts/). ```ts import { createTevmNode } from 'tevm' import { tevmCall, tevmMine } from 'tevm/actions' const node = createTevmNode() const receiptsManager = await node.getReceiptsManager() // Submit transaction const { txHash } = await tevmCall(node, { method: 'eth_sendTransaction', params: [tx], createTransaction: true }) // Get optimistic receipt const pendingReceipt = await receiptsManager.getReceiptByTxHash(txHash) // Update UI optimistically updateUI(pendingReceipt) // Wait for real receipt const realReceipt = await node.request({ method: 'eth_getTransactionReceipt', params: [txHash] }) // Eject optimistic tx if real receipt differs if (receiptsAreDifferent(pendingReceipt, realReceipt)) { await receiptsManager.removeReceipt(txHash) updateUI(realReceipt) } // Advanced: Rebase on new blocks node.on('block', async (blockNumber) => { // Get new block const block = await node.request({ method: 'eth_getBlockByNumber', params: [blockNumber, true] }) // Get our pending transactions const pendingTxs = await receiptsManager.getPendingTransactions() // Rebase our transactions on top of new block for (const tx of pendingTxs) { const result = await tevmCall(node, { ...tx, blockTag: 'pending' }) // Update receipt await receiptsManager.putReceipt(tx.hash, result) } // Mine rebased transactions await tevmMine(node) }) ``` import { Callout, Steps, Button } from 'vocs/components' ## Tevm Node Methods Tevm Node provides a comprehensive API for interacting with the Ethereum Virtual Machine. This reference covers all core methods and capabilities. This reference documents the main API methods available on a Tevm Node instance. These methods allow you to interact with the Ethereum Virtual Machine, manage state, and control execution at various levels of abstraction. ### Core Methods

๐Ÿš€ Initialization

Create and set up a Tevm Node instance

โš™๏ธ Virtual Machine

Access the low-level EVM interface

๐Ÿ“ฅ Transaction Pool

Manage pending transactions

๐Ÿ“ƒ Receipts & Logs

Access transaction receipts and event logs

:::code-group ```ts [Initialization] showLineNumbers {1,3-7,9} filename="node-initialization.ts" import { createTevmNode, http } from 'tevm' const node = createTevmNode({ // [!code focus] fork: { // [!code focus] transport: http('https://mainnet.infura.io/v3/YOUR-KEY') // [!code focus] } // [!code focus] }) // [!code focus] await node.ready() // Wait for initialization // [!code focus] ```
Always wait for the node to be ready before using it. The ready() method returns a promise that resolves when the node is fully initialized.
```ts [Virtual Machine] showLineNumbers {1-3,5,8,11-19,22-27,30-33} filename="vm-access.ts" import { createImpersonatedTx } from 'tevm/tx' import { Block } from 'tevm/block' import { createAddress } from 'tevm/address' const vm = await node.getVm() // [!code focus] // Create a block for the transaction const block = new Block() // Create an impersonated transaction const tx = createImpersonatedTx({ // [!code focus] impersonatedAddress: createAddress('0x1234...'), // [!code focus] nonce: 0n, // [!code focus] gasLimit: 21064n, // [!code focus] maxFeePerGas: 8n, // [!code focus] maxPriorityFeePerGas: 1n, // [!code focus] to: createAddress('0x5678...'), // [!code focus] value: 1000000000000000000n, // 1 ETH // [!code focus] }) // [!code focus] // Execute the transaction const result = await vm.runTx({ // [!code focus] tx, // [!code focus] block, // [!code focus] skipNonce: true, // Skip nonce check // [!code focus] skipBalance: true, // Skip balance check // [!code focus] }) // [!code focus] // Check execution result if (!result.execResult.exceptionError) { // [!code focus] console.log('Transaction successful') // [!code focus] console.log('Gas used:', result.totalGasSpent) // [!code focus] } // [!code focus] ```
The VM interface provides the lowest level of control, allowing you to execute transactions with precise parameters and inspect every aspect of execution.
```ts [Transaction Pool] showLineNumbers {1,4-9,12} filename="txpool-management.ts" const txPool = await node.getTxPool() // [!code focus] // Add a transaction await txPool.add({ // [!code focus] from: '0x1234...', // [!code focus] to: '0x5678...', // [!code focus] value: 1000000000000000000n, // [!code focus] }) // [!code focus] // Get pending transactions // Sorted by gas price and nonce const pending = await txPool.txsByPriceAndNonce() // [!code focus] // Get all pending transactions (raw access) const allPending = txPool.getPendingTransactions() // Get pending nonce for an address const nextNonce = txPool.getPendingNonce('0x1234...') ```
Transactions added to the pool remain pending until they are mined into a block. The mining behavior depends on your node's mining configuration.
```ts [Receipts & Logs] showLineNumbers {1,4,7-12} filename="receipts-and-logs.ts" const receipts = await node.getReceiptsManager() // [!code focus] // Get receipt for a transaction const receipt = await receipts.getReceiptByTxHash('0x1234...') // [!code focus] // Query logs with filtering const logs = await receipts.getLogs({ // [!code focus] fromBlock: 0n, // [!code focus] toBlock: 'latest', // [!code focus] address: '0x1234...', // Contract address (optional) // [!code focus] topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] // Optional topic filters // [!code focus] }) // [!code focus] // Create a subscription for future logs const subscription = receipts.createLogSubscription({ address: '0x1234...', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'] }) // Handle new logs subscription.on('log', (log) => { console.log('New log:', log) }) ```
The logs API follows the same pattern as Ethereum's JSON-RPC, allowing you to filter by address, topics, and block range.
::: ### State Management

๐Ÿ‘ค Account Impersonation

Act as another account in fork mode

๐Ÿ” Event Filtering

Create and manage event filters

:::code-group ```ts [Account Impersonation] showLineNumbers {2,5,8} filename="account-impersonation.ts" // Impersonate an account (fork mode only) node.setImpersonatedAccount('0x1234...') // [!code focus] // Get current impersonated account const impersonated = node.getImpersonatedAccount() // [!code focus] // Stop impersonating node.setImpersonatedAccount(undefined) // [!code focus] // Now you can send transactions as this account // without having its private key ```
Account impersonation primarily affects the JSON-RPC layer, enabling methods like eth\_sendRawTransaction to execute as the impersonated account. It works best in fork mode.
```ts [Event Filtering] showLineNumbers {2-10,13,16} filename="event-filtering.ts" // Create a filter for Transfer events node.setFilter({ // [!code focus] id: '0x1', // [!code focus] fromBlock: 0n, // [!code focus] toBlock: 'latest', // [!code focus] address: '0x1234...', // [!code focus] topics: [ // [!code focus] '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer event signature // [!code focus] ], // [!code focus] }) // [!code focus] // Get all filters const filters = node.getFilters() // [!code focus] // Remove a filter node.removeFilter('0x1') // [!code focus] // Use filters with eth_getLogs and eth_getFilterChanges const logs = await node.request({ method: 'eth_getFilterChanges', params: ['0x1'] }) ```

The topic 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef is the keccak256 hash of the Transfer event signature:

Transfer(address,address,uint256)

::: ### Node Properties

๐Ÿšฆ Status

Current state of the node

๐Ÿ”„ Mode

Fork or normal operation mode

๐Ÿ“ Logger

Built-in logging capabilities

Status

The status property indicates the current state of the node:

```ts showLineNumbers filename="node-status.ts" console.log(node.status) // One of: 'INITIALIZING' | 'READY' | 'SYNCING' | 'MINING' | 'STOPPED' // Example: Wait until node is ready const waitForReady = async () => { while (node.status !== 'READY') { await new Promise(resolve => setTimeout(resolve, 100)) } console.log('Node is ready!') } ```

Mode

The mode property indicates whether the node is running in fork or normal mode:

```ts showLineNumbers filename="node-mode.ts" console.log(node.mode) // 'fork' or 'normal' // Use mode to adapt behavior if (node.mode === 'fork') { console.log('Node is forking from a remote provider') // Use fork-specific features like impersonation } else { console.log('Node is running in standalone mode') // Use local-only features } ```

Logger

Built-in logging capabilities with multiple levels:

```ts showLineNumbers filename="node-logger.ts" // Different log levels node.logger.trace('Extremely detailed information') node.logger.debug('Detailed debugging information') node.logger.info('General information') node.logger.warn('Warning messages') node.logger.error('Error information') node.logger.fatal('Critical errors that stop execution') // Log with context node.logger.info('Transaction processed', { hash: '0x1234...', from: '0x5678...', to: '0x9abc...', value: '1 ETH' }) ```
### Advanced Actions Tevm provides a family of "tevm" prefixed actions for direct EVM execution with extensive debugging capabilities. #### tevmCall Execute low-level EVM calls with detailed execution tracing: ```ts showLineNumbers {1-3,5,7-29} filename="tevm-call.ts" import { tevmCall } from 'tevm/actions' import { encodeFunctionData } from 'viem' import { createMemoryClient } from 'tevm' const client = createMemoryClient() const result = await tevmCall(client, { to: '0x1234...', data: encodeFunctionData({ abi, functionName: 'myFunction', args: [arg1, arg2] }), // Monitor EVM execution steps onStep: (step, next) => { console.log(`Opcode: ${step.opcode.name}, PC: ${step.pc}`) next?.() }, // Monitor contract creation onNewContract: (data, next) => { console.log(`New contract at: ${data.address.toString()}`) next?.() }, // Monitor call execution onBeforeMessage: (message, next) => { console.log(`Call to: ${message.to?.toString()}`) next?.() }, onAfterMessage: (result, next) => { console.log(`Return: ${result.execResult.returnValue.toString('hex')}`) next?.() } }) ``` The onStep event fires for every EVM instruction executed, allowing you to trace execution at the opcode level. #### tevmContract High-level contract interaction with EVM event monitoring: ```ts showLineNumbers {1-2,4,6-16} filename="tevm-contract.ts" import { tevmContract } from 'tevm/actions' import { createMemoryClient } from 'tevm' const client = createMemoryClient() const result = await tevmContract(client, { abi, address: '0x1234...', functionName: 'myFunction', args: [arg1, arg2], // Monitor EVM execution onStep: (step, next) => { console.log(`Opcode: ${step.opcode.name}, Stack: ${step.stack.length}`) next?.() }, onNewContract: (data, next) => { console.log(`New contract created: ${data.address.toString()}`) next?.() } }) console.log('Function result:', result) ``` #### tevmDeploy Contract deployment with execution monitoring: ```ts showLineNumbers {1-2,4,6-16} filename="tevm-deploy.ts" import { tevmDeploy } from 'tevm/actions' import { createMemoryClient } from 'tevm' const client = createMemoryClient() const deployResult = await tevmDeploy(client, { abi, bytecode, args: [constructorArg1, constructorArg2], // Monitor deployment execution onStep: (step, next) => { console.log(`Executing: ${step.opcode.name}`) next?.() }, onNewContract: (data, next) => { console.log(`Deployed at: ${data.address.toString()}`) next?.() } }) console.log('Contract deployed at:', deployResult.address) console.log('Deployment gas used:', deployResult.gasUsed) ```

Debug Use Cases

โ›ฝ Gas Profiling

Track gas consumption of specific operations

๐Ÿงช Smart Contract Testing

Trace execution to debug test failures

๐Ÿ”’ Security Auditing

Analyze EVM execution paths for vulnerabilities

๐ŸŽ“ Educational Tools

Create EVM instruction visualizers

### Extensibility :::code-group ```ts [Custom Methods] showLineNumbers {1-7,10} filename="custom-methods.ts" const enhancedNode = node.extend((baseNode) => ({ async getBalance(address: string) { const vm = await baseNode.getVm() const account = await vm.stateManager.getAccount(address) return account.balance }, })) // Use the new method const balance = await enhancedNode.getBalance('0x1234...') ```
Tevm's extension system allows you to add custom methods to the node instance. These methods have full access to the base node's functionality.
```ts [State Cloning] showLineNumbers {2,5-7} filename="state-copying.ts" // Create a deep copy with independent state const nodeCopy = await node.deepCopy() // [!code focus] // Fork from another node const forkedNode = createTevmNode({ // [!code focus] fork: { transport: node } // [!code focus] }) // [!code focus] // Now you can experiment with different states await nodeCopy.sendTransaction({ ... }) // Original node state remains unchanged console.log('Original state intact') ```
State copying is useful for creating test scenarios, simulating alternative execution paths, or creating snapshots of state for later comparison.
::: ### JSON-RPC Support Tevm Node implements standard Ethereum JSON-RPC methods, making it compatible with existing Ethereum tools and libraries. :::code-group ```ts [EIP-1193 Interface] showLineNumbers {1,3,6-9} filename="eip1193.ts" import { requestEip1193 } from 'tevm/decorators' const node = createTevmNode().extend(requestEip1193()) // Standard JSON-RPC calls const blockNumber = await node.request({ // [!code focus] method: 'eth_blockNumber', // [!code focus] params: [] // [!code focus] }) // [!code focus] // Get balance const balance = await node.request({ method: 'eth_getBalance', params: ['0x1234...', 'latest'] }) ```
The EIP-1193 interface makes Tevm Node compatible with libraries like Ethers.js that expect a standard Ethereum provider.
```ts [Action Methods] showLineNumbers {1,3,6-7} filename="eth-actions.ts" import { ethActions } from 'tevm/decorators' const node = createTevmNode().extend(ethActions()) // Using action methods const blockNumber = await node.eth.getBlockNumber() // [!code focus] const balance = await node.eth.getBalance('0x1234...') // [!code focus] // Send transaction const hash = await node.eth.sendTransaction({ from: '0x1234...', to: '0x5678...', value: 1000000000000000000n }) ```
Action methods provide a more ergonomic API than raw JSON-RPC calls, with proper TypeScript typing and simpler parameter handling.
::: #### Supported Methods

JSON-RPC Methods

State Access
  • ๐Ÿ“„ eth\_getBalance
  • ๐Ÿ“„ eth\_getCode
  • ๐Ÿ“„ eth\_getStorageAt
  • ๐Ÿ“„ eth\_getTransactionCount
Block Methods
  • ๐Ÿ“„ eth\_blockNumber
  • ๐Ÿ“„ eth\_getBlockByHash
  • ๐Ÿ“„ eth\_getBlockByNumber
Transaction Methods
  • ๐Ÿ“„ eth\_sendTransaction
  • ๐Ÿ“„ eth\_sendRawTransaction
  • ๐Ÿ“„ eth\_getTransactionByHash
  • ๐Ÿ“„ eth\_getTransactionReceipt
Anvil Methods
  • ๐Ÿ“„ anvil\_impersonateAccount
  • ๐Ÿ“„ anvil\_stopImpersonatingAccount
  • ๐Ÿ“„ anvil\_mine
  • ๐Ÿ“„ anvil\_setBalance
Tevm Methods
  • ๐Ÿ“„ tevm\_snapshot
  • ๐Ÿ“„ tevm\_revert
  • ๐Ÿ“„ tevm\_mine
  • ๐Ÿ“„ tevm\_setAccount
## Package Reference Tevm Node is built as a collection of modular packages. You can install the complete suite with `npm install tevm` or individual packages based on your needs. ### Installation Options #### Complete Package ```bash npm install tevm ``` ```typescript // Import from the main package import { createTevmNode } from 'tevm' import { callHandler } from 'tevm/actions' import { http } from 'tevm/transport' ``` #### Individual Packages ```bash npm install @tevm/node @tevm/actions ``` ```typescript // Import directly from packages import { createTevmNode } from '@tevm/node' import { callHandler } from '@tevm/actions' ``` ### Core Packages #### Node & Client * **@tevm/node** - Core node implementation and management * Main entry point for creating and managing Tevm nodes * Blockchain and state management * Mining and block production * **@tevm/memory-client** - In-memory Ethereum client * Viem-compatible client interface * Local state management * Transaction handling #### EVM & Execution * **@tevm/vm** - Virtual Machine implementation * EVM execution environment * Opcode handling * State transitions * **@tevm/evm** - Low-level EVM operations * Bytecode execution * Gas calculation * Stack management #### State & Storage * **@tevm/state** - State management * Account state handling * Storage manipulation * State transitions * **@tevm/blockchain** - Blockchain implementation * Block management * Chain reorganization * Header validation ### Transaction Handling #### Transaction Processing * **@tevm/tx** - Transaction utilities * Transaction signing * Gas estimation * Receipt generation * **@tevm/txpool** - Transaction pool management * Pending transaction queue * Transaction ordering * Replacement handling #### Block & Receipt Management * **@tevm/block** - Block utilities * Block creation * Block validation * Chain management * **@tevm/receipt-manager** - Transaction receipts * Receipt storage * Event logging * Gas usage tracking ### Client Integration #### Communication * **@tevm/jsonrpc** - JSON-RPC implementation * Standard Ethereum methods * Custom Tevm methods * Error handling * **@tevm/http-client** - HTTP client * Remote node communication * Request batching * Error handling #### Actions & Procedures * **@tevm/actions** - High-level actions * Contract interaction * Account management * State queries * **@tevm/procedures** - Common operations * State manipulation * Chain operations * Utility functions ### Smart Contract Tools #### Contract Interaction * **@tevm/contract** - Contract utilities * ABI handling * Function encoding * Event parsing * **@tevm/precompiles** - Precompiled contracts * Standard precompiles * Custom implementations * Gas calculation #### Contract Management * **@tevm/predeploys** - Pre-deployed contracts * Standard contracts * Network-specific contracts * Contract deployment ### Utilities & Helpers #### Core Utilities * **@tevm/utils** - Common utilities * Address handling * Data encoding * Type conversion * **@tevm/common** - Shared constants * Chain configurations * Network parameters * Common types #### Development Tools * **@tevm/decorators** - Function decorators * Method extension * Behavior modification * Utility wrappers * **@tevm/errors** - Error handling * Error types * Error messages * Stack traces #### Data Structures * **@tevm/rlp** - RLP encoding/decoding * Data serialization * Network protocol * Storage format * **@tevm/trie** - Merkle Patricia Tree * State storage * Proof verification * Tree manipulation ### Development & Debugging #### Logging & Debugging * **@tevm/logger** - Logging system * Configurable levels * Output formatting * Debug information * **@tevm/effect** - Effect system * Side effect handling * Async operations * Error recovery #### Storage & Types * **@tevm/sync-storage-persister** - Storage persistence * State synchronization * Data persistence * Cache management * **@tevm/client-types** - Type definitions * Interface definitions * Type exports * API documentation ### Best Practices 1. **Start Simple** * Begin with the complete `tevm` package * Only split into individual packages if you need to optimize bundle size 2. **Bundle Optimization** * Import from specific subpaths: `tevm/actions` instead of `@tevm/actions` * Use tree-shaking friendly imports 3. **Version Management** * Keep all @tevm/\* packages on the same version * Update packages together to avoid compatibility issues 4. **Development Workflow** * Use TypeScript for better type safety * Leverage provided type definitions * Follow the documentation for each package ### Related Topics * [Architecture Overview](../introduction/architecture-overview) * [API Reference](./methods) * [GitHub Repository](https://github.com/evmts/tevm-monorepo) import { Callout } from 'vocs/components' ## Call API The Call API is one of the most important APIs in Tevm, covering 90% of use cases along with mining. It provides a powerful interface for executing EVM calls with extensive configuration options. ### Basic Usage Tevm offers two API styles for using the Call API: #### Client-based API (batteries included) ```ts import { createMemoryClient } from 'tevm' const client = createMemoryClient() // Use tevmCall directly on the client object const result = await client.tevmCall({ from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet data: '0x' // Empty call }) ``` #### Tree-shakable API ```ts import { tevmCall } from 'tevm/actions' import { createClient, custom } from 'viem' import { createTevmNode } from 'tevm/node' import { requestEip1193 } from 'tevm/decorators' // Create Tevm Node with EIP-1193 support const node = createTevmNode().extend(requestEip1193()) // Create Viem client const client = createClient({ transport: custom(node), }) // Use the tree-shakable action const result = await tevmCall(client, { from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', // Default account to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet data: '0x' // Empty call }) ``` ### Parameters The `CallParams` type includes: ```ts type CallParams = { // Required for most calls (except contract deployment) to?: Address // Input data for the call data?: Hex // Value in wei to send value?: bigint // Gas limit for the call gas?: bigint // Block tag to execute against blockTag?: 'latest' | 'pending' | 'earliest' | number // Whether to create a transaction createTransaction?: 'on-success' | 'always' | 'never' | boolean // Whether to skip balance checks skipBalance?: boolean // Whether to create an access list createAccessList?: boolean // Whether to create a debug trace createTrace?: boolean // From address (defaults to first account) from?: Address // Gas price settings maxFeePerGas?: bigint maxPriorityFeePerGas?: bigint // State overrides stateOverrideSet?: StateOverrideSet // Block overrides blockOverrideSet?: BlockOverrideSet // Event handlers for EVM execution onStep?: (data: InterpreterStep, next?: () => void) => void onNewContract?: (data: NewContractEvent, next?: () => void) => void onBeforeMessage?: (data: Message, next?: () => void) => void onAfterMessage?: (data: EVMResult, next?: () => void) => void } ``` ### Return Type The `CallResult` includes: ```ts type CallResult = { // Return data from the call rawData: Hex // Gas used by the EVM executionGasUsed: bigint // Total gas including intrinsic costs totalGasSpent?: bigint // Transaction hash if created txHash?: Hex // Logs emitted logs?: Log[] // Created contract address createdAddress?: Address // Access list if requested accessList?: Record> // Debug trace if requested trace?: DebugTraceCallResult // Any errors that occurred errors?: TevmCallError[] } ``` ### Examples #### 1. Simple Contract Call Using client-based API: ```ts import { createMemoryClient } from 'tevm' import { encodeFunctionData, decodeFunctionResult, parseAbi } from 'viem' // Example ERC20 ABI for 'balanceOf' const abi = parseAbi([ 'function balanceOf(address account) view returns (uint256 balance)' ]) const client = createMemoryClient() const result = await client.tevmCall({ to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', data: encodeFunctionData({ abi, functionName: 'balanceOf', args: ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'] }) }) const balance = decodeFunctionResult({ abi, functionName: 'balanceOf', data: result.rawData }) ``` #### 2. Contract Deployment Using client-based API: ```ts import { createMemoryClient } from 'tevm' // Simple contract bytecode (returns 42) const bytecode = '0x6080604052348015600f57600080fd5b50602a60808190526040516100929190810190830190829052565b604051601f19601f830116810160405280815292829060208401853c80601f830112156100c057600080fd5b505b50505050610047806100d36000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632096525514602d575b600080fd5b60336047565b604051603e91906059565b60405180910390f35b602a81565b6000819050919050565b6053816040565b82525050565b6000602082019050606c6000830184604c565b9291505056fea2646970667358221220f1c69e125f1a9f0c5e22a6fb4f9cb134c5b43496922c563e13731844a6e4d12d64736f6c63430008130033' const client = createMemoryClient() const result = await client.tevmCall({ from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', data: bytecode, createTransaction: true }) console.log('Contract deployed at:', result.createdAddress) ``` #### 3. State Override Using client-based API: ```ts import { createMemoryClient } from 'tevm' const client = createMemoryClient() const result = await client.tevmCall({ from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH data: '0x', stateOverrideSet: { ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266']: { balance: 4096n, // 0x1000 as bigint nonce: 2n, code: '0x', state: {} } } }) ``` #### 4. Debug Trace Using client-based API: ```ts import { createMemoryClient } from 'tevm' const client = createMemoryClient() const result = await client.tevmCall({ from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', to: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH data: '0x', createTrace: true }) // Analyze the execution trace if (result.trace) { result.trace.structLogs.forEach(log => { console.log(log.op, log.stack, log.memory) }) } ``` #### 5. EVM Event Handlers You can attach event handlers to monitor EVM execution in real-time. Using client-based API: ```ts import { createMemoryClient } from 'tevm' import { encodeFunctionData } from 'viem' const client = createMemoryClient() // Use EVM event handlers to monitor execution const result = await client.tevmCall({ from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', to: contractAddress, data: encodeFunctionData({ abi, functionName: 'myFunction', args: [arg1, arg2] }), // Monitor each EVM step onStep: (step, next) => { console.log( `Executing ${step.opcode.name} at PC=${step.pc}`, `Gas left: ${step.gasLeft.toString()}`, `Stack depth: ${step.stack.length}` ) // Important: Call next to continue execution next?.() }, // Monitor contract creation events onNewContract: (data, next) => { console.log(`New contract created at: ${data.address.toString()}`) next?.() }, // Monitor messages (calls) before execution onBeforeMessage: (message, next) => { console.log(`Call to: ${message.to?.toString()}`, `Value: ${message.value.toString()}`, `Gas limit: ${message.gasLimit.toString()}`) next?.() }, // Monitor messages (calls) after execution onAfterMessage: (result, next) => { console.log(`Call result: ${result.execResult.returnValue.toString('hex')}`, `Gas used: ${result.execResult.executionGasUsed.toString()}`) if (result.execResult.exceptionError) { console.error(`Error: ${result.execResult.exceptionError.error}`) } next?.() } }) ``` ### Higher Level APIs While `tevmCall` *** title: VM & Submodules description: Overview of the internal EVM, blockchain, state, receipts, and more ## VM & Submodules [Tevm Node](https://github.com/evmts/tevm-monorepo) is built on a modular architecture that separates concerns into distinct submodules. This guide covers the internal components and their APIs. ### Overview The main submodules are: 1\. **EVM (Ethereum Virtual Machine)** - Core [execution engine](https://ethereum.org/en/developers/docs/evm/) 2\. **Blockchain** - [Block and chain state](https://ethereum.org/en/developers/docs/blocks/) management 3\. **StateManager** - [Account and storage state](https://ethereum.org/en/developers/docs/accounts/) management 4\. **TxPool** - [Transaction mempool](https://ethereum.org/en/developers/docs/transactions/) 5\. **ReceiptsManager** - [Transaction receipts and logs](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt) ### EVM Module The EVM module handles bytecode execution and state transitions. It is based on [`@ethereumjs/evm`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm). ```ts const vm = await node.getVm() const evm = await vm.evm // Direct EVM execution const result = await evm.runCall({ to: '0x...', data: '0x...', value: 0n, caller: '0x...', }) // Full transaction execution (recommended) const txResult = await vm.runTx({ tx: transaction, block: block, }) ``` #### Key Features \- **State Management**: Handles [account state](https://ethereum.org/en/developers/docs/accounts/), storage, and code execution \- **Gas Metering**: Tracks [gas consumption](https://ethereum.org/en/developers/docs/gas/) during execution \- **Precompiles**: [Built-in contract implementations](https://www.evm.codes/precompiled) \- **EIP Support**: Implements various [Ethereum Improvement Proposals](https://eips.ethereum.org/) #### Common Operations ```ts // Check execution result if (result.execResult.exceptionError) { console.error('Execution failed:', result.execResult.exceptionError) } else { console.log('Return value:', result.execResult.returnValue) console.log('Gas used:', result.execResult.executionGasUsed) } // Access logs from execution for (const log of result.execResult.logs) { console.log('Event:', { address: log.address, topics: log.topics, data: log.data }) } ``` ### Blockchain Module The blockchain module manages blocks and chain state. It is based on [`@ethereumjs/blockchain`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/blockchain). ```ts const chain = (await node.getVm()).blockchain // Get block by number or hash const block = await chain.getBlock('latest') const blockByHash = await chain.getBlock(blockHash) // Add new block await chain.putBlock(block) // Delete block await chain.delBlock(blockHash) // Get block details console.log({ number: block.header.number, hash: block.hash(), parentHash: block.header.parentHash, stateRoot: block.header.stateRoot }) ``` #### Fork Support ```ts // When forking, blocks are fetched from the parent chain const forkedBlock = await chain.getBlock(blockNumber) // Local blocks override forked blocks await chain.putBlock(localBlock) ``` ### StateManager The StateManager handles account state and storage. It is based on [`@ethereumjs/statemanager`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/statemanager). ```ts const state = (await node.getVm()).stateManager // Account operations const account = await state.getAccount(address) await state.putAccount(address, account) // Contract operations await state.putContractCode(address, bytecode) const code = await state.getContractCode(address) // Storage operations await state.putContractStorage(address, key, value) const value = await state.getContractStorage(address, key) // State snapshots const snapshot = await state.dumpCanonicalGenesis() await state.commit() await state.revert() ``` #### Working with Accounts ```ts // Create or update account const account = { nonce: 1n, balance: 100n, storageRoot: EMPTY_ROOT, codeHash: EMPTY_HASH } await state.putAccount(address, account) // Delete account await state.deleteAccount(address) ``` ### Transaction Pool The TxPool manages pending transactions. It is based on [`@ethereumjs/tx-pool`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/tx-pool). ```ts const pool = await node.getTxPool() // Add transaction await pool.add(transaction) // Get pending transactions const pending = await pool.getPendingTransactions() // Remove transaction await pool.removeByHash(txHash) // Get transactions ordered by price const ordered = await pool.txsByPriceAndNonce({ baseFee: 10n }) ``` #### Transaction Validation ```ts // Validate transaction before adding try { await pool.validateTx(transaction) await pool.add(transaction) } catch (error) { console.error('Invalid transaction:', error) } ``` ### ReceiptsManager The ReceiptsManager handles [transaction receipts](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt) and event logs. ```ts const receipts = await node.getReceiptsManager() // Get receipts for block const blockReceipts = await receipts.getReceipts(blockHash) // Get receipt by transaction hash const txReceipt = await receipts.getReceiptByTxHash(txHash) // Query logs const logs = await receipts.getLogs({ fromBlock: 0n, toBlock: 'latest', address: contractAddress, topics: [eventSignature] }) ``` #### Working with Logs ```ts // Process event logs for (const log of logs) { console.log({ address: log.address, topics: log.topics, data: log.data, blockNumber: log.blockNumber, transactionHash: log.transactionHash, logIndex: log.logIndex }) } ``` ### Best Practices 1\. **Use High-Level APIs**: Prefer `vm.runTx()` over direct EVM execution for full transaction support. 2\. **State Management**: \- Use checkpoints for atomic operations \- Clean up state after tests \- Handle reverts properly 3\. **Gas Optimization**: \- [Estimate gas](https://ethereum.org/en/developers/docs/gas/#why-are-gas-fees-necessary) before execution \- Monitor gas usage in hot paths \- Use appropriate gas limits 4\. **Error Handling**: \- Check execution results \- Handle exceptions gracefully \- Validate inputs ### Related Topics \- [JSON-RPC Support](./json-rpc) \- [Managing State](../core/managing-state) \- [Transaction Pool](../advanced/txpool) \- [Receipts & Logs](../advanced/receipts-and-logs) \- [EVM Opcodes Reference](https://www.evm.codes/) \- [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf) \- [ethereumjs/ethereumjs-monorepo](https://github.com/ethereumjs/ethereumjs-monorepo) **Up Next**\ \- [JSON-RPC Guide](./json-rpc) \- [Advanced Usage](../advanced/txpool) ## Bundler Quickstart This quickstart guide will help you set up Tevm's bundler functionality, allowing you to import Solidity contracts directly into your TypeScript/JavaScript code. ### Overview Tevm bundler enables a seamless integration between Solidity and TypeScript, letting you: * Import `.sol` files directly in your code * Receive full TypeScript type information for contract methods * Use go-to-definition and hover documentation for Solidity code * Interact with contracts in a type-safe way ### Prerequisites * Node.js 18+ (recommended for best ESM support) and npm/yarn/pnpm * A supported bundler (Vite, Webpack, Rollup, ESBuild, or Bun) ### Step 1: Install Tevm and TypeScript Plugin First, install Tevm and the TypeScript plugin in your project: ```bash npm install tevm npm install -D @tevm/ts-plugin ``` ### Step 2: Configure Your Bundler The bundler plugin handles the actual compilation of Solidity files during your build process. This ensures your final JavaScript output will contain the compiled Solidity contract ABIs, bytecode, and TypeScript interfaces. :::tip If you have a complex project or Foundry-based imports, you might need advanced configuration. See the [Advanced Configuration](#advanced-configuration) section for setting up `tevm.config.json`. The bundler creates a `.tevm` cache directory. Consider adding it to your `.gitignore`: ```bash # .gitignore .tevm ``` ::: #### For Vite (recommended) ```ts // vite.config.ts import { defineConfig } from 'vite' import { vitePluginTevm } from 'tevm/bundler/vite-plugin' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ react(), vitePluginTevm(), ], }) ``` #### For Other Bundlers
Webpack ```js // webpack.config.mjs import { webpackPluginTevm } from 'tevm/bundler/webpack-plugin' export default { // Other webpack config plugins: [ webpackPluginTevm(), ], } ```
Next.js ```js // next.config.js or next.config.mjs const nextConfig = { // Your Next.js config... webpack: (config) => { // Optional: Any custom webpack configuration // Note: Next.js may have typechecking issues with .sol imports. // If you encounter type errors, consider the following option: return config }, typescript: { // Typechecking will only be available after the LSP is migrated to volar // Until then typechecking will work in editor but not during a next.js build // If you absolutely need typechecking before then there is a way to generate .ts files via a ts-plugin cli command // To do that run `npx evmts-gen` in the root of your project ignoreBuildErrors: true, } } export default nextConfig ``` :::warning Next.js has its own type-checking pipeline that may conflict with Solidity imports. If you see type errors during `next build`, either: 1. Use `typescript.ignoreBuildErrors: true` (as shown above) 2. Use the codegen approach with `npx tevm gen` to generate explicit TypeScript files :::
Rollup ```js // rollup.config.js import { rollupPluginTevm } from 'tevm/bundler/rollup-plugin' export default { // Other rollup options plugins: [ rollupPluginTevm(), ], } ```
Rspack ```js // rspack.config.mjs import { rspackPluginTevm } from 'tevm/bundler/rspack-plugin' export default { // Other rspack config plugins: [ rspackPluginTevm(), ], } ```
Bun ```ts // plugins.ts import { plugin } from 'bun' import { tevmBunPlugin } from 'tevm/bundler/bun-plugin' plugin(tevmBunPlugin({})) ``` Then in your `bunfig.toml`: ```toml preload = ["./plugins.ts"] [test] preload = ["./plugins.ts"] ```
ESBuild ```js // build.mjs import { build } from 'esbuild' import { esbuildPluginTevm } from 'tevm/bundler/esbuild-plugin' build({ entryPoints: ['src/index.js'], outdir: 'dist', bundle: true, plugins: [ esbuildPluginTevm(), ], }) ```
### Step 3: Configure TypeScript While the bundler plugin handles compilation during build time, the TypeScript plugin configures your TypeScript Language Service (LSP) to be aware of Solidity imports during development. This enables editor features like code completion, type checking, and go-to-definition for Solidity contracts. Add the Tevm TypeScript plugin to your `tsconfig.json`: ```json { "compilerOptions": { "plugins": [ { "name": "@tevm/ts-plugin" } ], // Other TypeScript options... } } ``` ### Step 4: Configure Your Editor #### VS Code and Cursor For VS Code and Cursor users, ensure you're using the workspace version of TypeScript: 1. Open the Command Palette (Ctrl+Shift+P or Cmd+Shift+P in VS Code; โŒ˜+K or Ctrl+K in Cursor) 2. Type "TypeScript: Select TypeScript Version" 3. Select "Use Workspace Version" This step is crucial because the workspace TypeScript installation is what loads the @tevm/ts-plugin to provide Solidity import support. #### Vim, Neovim, and Other Editors For Vim, Neovim, and most other editors with TypeScript support, everything should work automatically without any additional configuration as long as they use the project's workspace version of TypeScript. This is crucial because the workspace TypeScript installation is what loads the @tevm/ts-plugin to provide Solidity import support. ### Step 5: Create a Solidity Contract Create a simple contract in your project. Name it `Counter.s.sol` to include bytecode (the `.s.sol` extension tells Tevm to generate deployable bytecode): :::info **Understanding .sol vs .s.sol extensions:** * **`.sol`** - Only generates the ABI (Application Binary Interface) * Use for interacting with existing deployed contracts * Cannot be deployed (no bytecode) * Example: `import { Token } from './Token.sol'` * **`.s.sol`** - Generates both ABI and bytecode * Can be deployed or used as an interface * Increases bundle size but enables deployment * Example: `import { Token } from './Token.s.sol'` ::: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Counter { uint256 private count = 0; function increment() public { count += 1; } function getCount() public view returns (uint256) { return count; } } ``` :::info Files with the `.s.sol` extension generate both ABI and bytecode, allowing contract deployment. Regular `.sol` files only generate ABIs (useful for interacting with existing contracts). ::: ### Step 6: Import and Use the Contract Now you can import the contract directly in your TypeScript code: ```tsx // src/App.tsx or any other file import { Counter } from './Counter.s.sol' import { createMemoryClient } from 'tevm' import { useState, useEffect } from 'react' function App() { const [count, setCount] = useState(0n) const [client] = useState(() => createMemoryClient()) const [deployedAddress, setDeployedAddress] = useState('') useEffect(() => { const deploy = async () => { // Deploy the contract - need to call .deploy() and pass any constructor args // (Counter doesn't have constructor args in this example) const deployed = await client.deployContract(Counter.deploy()) setDeployedAddress(deployed.address) // Get initial count const initialCount = await deployed.read.getCount() setCount(initialCount) } deploy() }, [client]) const handleIncrement = async () => { if (!deployedAddress) return // Get contract with address const contract = Counter.withAddress(deployedAddress) // Call increment method await client.writeContract(contract.write.increment()) // Mine a block to include the transaction await client.mine({ blocks: 1 }) // Get updated count const newCount = await client.readContract(contract.read.getCount()) setCount(newCount) } return (

Tevm Counter Example

Count: {count.toString()}

) } export default App ``` #### Example of Type-Safe Method Calls The bundler generates strongly-typed methods for your contract: ```ts // Read methods (view/pure functions) const balance = await client.readContract( ERC20.read.balanceOf('0x' + '20'.repeat(20)) ) // Write methods (state-changing functions) await client.writeContract( ERC20.write.transfer('0x' + '30'.repeat(20), 1000000n) ) // Listen to events client.watchEvent( ERC20.events.Transfer({ from: '0x' + '20'.repeat(20), to: null // `null` acts as a wildcard }), (log) => console.log('Transfer event:', log) ) ``` ### What's Happening? When you import `Counter.s.sol`, Tevm: 1. Compiles the Solidity code using solc 2. Generates TypeScript with the ABI, bytecode, and a Contract instance 3. Provides you with a fully typed interface to the contract The `Counter` object is a [Tevm Contract](/reference/contract) instance with: * `abi`: The contract's ABI * `address`: The contract's address (if set with `withAddress()`) * `bytecode`: The contract's creation bytecode (undefined for regular .sol files) * `deployedBytecode`: The contract's runtime bytecode (undefined for regular .sol files) * `deploy()`: Method to create deployment data with constructor arguments * `read`: Type-safe read methods (view/pure functions) * `write`: Type-safe write methods (state-changing functions) * `events`: Type-safe event filters for subscription * `withAddress()`: Method to create a new instance with an address :::info Note that `bytecode` and `deployedBytecode` are only available when using the `.s.sol` extension. If you import a regular `.sol` file, these properties will be `undefined`, and you won't be able to deploy the contract. ::: ### Advanced Configuration #### Foundry Integration By default, Tevm uses Node.js resolution to resolve contract imports (similar to how JavaScript imports work). However, if you're working with a Foundry project or need custom import remappings, create a `tevm.config.json` file in your project root: ```json { "foundryProject": true, "libs": ["lib", "node_modules"], "remappings": { "@openzeppelin/": "node_modules/@openzeppelin/" }, "cacheDir": ".tevm" } ``` :::tip It's highly recommended to add the `.tevm` cache directory to your `.gitignore`: ```bash # .gitignore .tevm ``` This prevents compiled artifacts from being committed to your repository. The `.tevm` directory is ephemeral - you can safely delete it if you encounter stale builds or caching issues, but it will require recompiling all contracts on the next build. ::: Setting `"foundryProject": true` will: * Automatically read your Foundry remappings from `foundry.toml` or `remappings.txt` * Include your Foundry library paths (`lib/` directory by default) * Allow you to import contracts using the same paths as in your Foundry project * Merge your manually specified remappings and libs with those from Foundry You can also manually set remappings and lib paths without using Foundry: ```json { "foundryProject": false, // or omit this line "libs": [ "./contracts", "node_modules" ], "remappings": { "@openzeppelin/": "node_modules/@openzeppelin/", "ds-test/": "lib/forge-std/lib/ds-test/src/", "solmate/": "node_modules/solmate/src/" } } ``` This configuration is especially useful if you: * Have a mixed Foundry/JavaScript project * Use Forge libraries like OpenZeppelin or Solmate * Have complex import paths in your Solidity code For a complete example of Bundler + Foundry integration, see the [Foundry example](https://github.com/evmts/tevm-monorepo/tree/main/examples/vite) in the Tevm examples repository. ### Using Third-Party Contracts #### NPM Packages You can import contracts from npm packages: ```ts // Import OpenZeppelin ERC20 - use .s.sol extension if you need bytecode import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.s.sol' // Deploy with constructor arguments - need to call .deploy() with constructor args const myToken = await client.deployContract( ERC20.deploy("MyToken", "MTK") ) ``` #### Coming Soon Features Tevm is actively developing exciting new features to make working with contracts even easier: ##### Network Imports with CAIP-10 :::info The CAIP-10 network import feature is coming soon and is not yet available in the current release. ::: In an upcoming version, Tevm will support importing contracts directly from any EVM network using CAIP-10 identifiers. This will let you interact with deployed contracts by simply importing them: ```ts // Import WETH from Ethereum mainnet using CAIP-10 identifier import { WETH } from 'caip10:eip155:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' // Import USDC from Optimism import { USDC } from 'caip10:eip155:10:0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85' // Use these contracts directly const balance = await client.readContract( WETH.read.balanceOf('0x123...') ) ``` This will automatically fetch the ABI from the network and create a type-safe contract interface. ##### Inline Solidity with Template Literals :::info The inline Solidity feature is coming soon and is not yet available in the current release. ::: For quick prototyping or one-off contracts, you'll soon be able to define Solidity contracts directly in your JavaScript/TypeScript using template literals: ```ts import { sol } from 'tevm' // Define a contract inline const { Counter } = sol` // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Counter { uint256 private count = 0; function increment() public { count += 1; } function getCount() public view returns (uint256) { return count; } } ` // Use it just like an imported contract const deployed = await client.deployContract(Counter.deploy()) await deployed.write.increment() const count = await deployed.read.getCount() ``` This will provide the same type-safety and features as file-based imports, but with the convenience of inline code. ### Documentation Resources * Bundler Reference Documentation: * [Overview](/reference/bundler/overview) - Key benefits and features * [Internals](/reference/bundler/internals) - How the bundler works * [Methods & Exports](/reference/bundler/methods) - Advanced APIs * [Troubleshooting](/reference/bundler/troubleshooting) - Common issues and solutions * For AI and LLM users, the full Tevm documentation is available as plain text at [https://node.tevm.sh/llms-full.txt](https://node.tevm.sh/llms-full.txt) ### Troubleshooting * **Red underlines in imported Solidity**: Make sure your editor is using the workspace version of TypeScript * **No bytecode available**: Check that you're using the `.s.sol` extension for contracts you want to deploy * **Deployment errors**: Make sure you're calling `.deploy()` with any required constructor arguments * **Compilation errors**: Check your Solidity code and ensure you're using a compatible Solidity version * **TypeScript errors**: Ensure you have the TypeScript plugin correctly configured * **Red underlines in editor**: Verify your editor is using the workspace version of TypeScript with the plugin loaded * **Import resolution failures**: If you're using Foundry-style imports (like `@openzeppelin/contracts/...`) and seeing errors: * Create a `tevm.config.json` with `"foundryProject": true` * Check that your remappings in `foundry.toml` are correct * Try adding explicit remappings in `tevm.config.json` * **Test runner issues**: Most test runners (Vitest, Jest) work out-of-the-box once the bundler plugin is configured. For Jest, you might need extra configuration or use the codegen approach with `npx tevm gen` ### Codegen Alternative to Bundler :::tip If your framework conflicts with the bundler approach or you prefer explicit type generation, try the codegen approach: ```bash npx tevm gen ``` This generates `.ts` files alongside your `.sol` files that can be imported directly, without requiring bundler integration. This is especially useful for Next.js or other frameworks with strict type-checking pipelines. ::: ### Next Steps Now that you have the bundler set up, you can: * [Learn more about Tevm Contracts](/reference/contract) * Explore the bundler in depth: * [Bundler Overview](/reference/bundler/overview) - Key benefits and features * [Bundler Internals](/reference/bundler/internals) - How the bundler works * [Advanced Methods & APIs](/reference/bundler/methods) - For custom implementations * [Troubleshooting](/reference/bundler/troubleshooting) - Solutions for common issues * [Build applications with Tevm Node](/core/create-tevm-node) For complete project examples, check out the [Tevm examples repository](https://github.com/evmts/tevm-monorepo/tree/main/examples). import { Callout, Steps, Button } from 'vocs/components' import { TabGroup } from '../../../components' ## Getting Started with Ethers.js While Tevm is built with viem as its primary API, it provides **full compatibility** with [ethers.js](https://docs.ethers.org/v6/) through its EIP-1193 provider interface! This makes Tevm a perfect fit for existing ethers.js projects. This guide shows you how to use Tevm Node with ethers.js, allowing you to leverage Tevm's powerful features with the ethers library you already know. ### Installation #### Install Dependencies First, install Tevm and ethers as dependencies:
```bash npm install tevm ethers@latest ```
```bash pnpm add tevm ethers@latest ```
```bash yarn add tevm ethers@latest ```
```bash bun add tevm ethers@latest ```
#### Create Tevm Client Set up your Tevm client and enable the EIP-1193 interface: ```ts import { createMemoryClient, http } from 'tevm' import { optimism } from 'tevm/chains' import { requestEip1193 } from 'tevm/decorators' // Create a client (optionally forking from a network) const client = createMemoryClient({ fork: { transport: http('https://mainnet.optimism.io'), common: optimism }, }) // Enable EIP-1193 compatibility for ethers client.transport.tevm.extend(requestEip1193()) await client.tevmReady() ``` #### Connect Ethers Create an ethers provider that uses your Tevm node: ```ts import { BrowserProvider, Wallet } from 'ethers' // Create ethers provider using the Tevm node const provider = new BrowserProvider(client.transport.tevm) // Create a new wallet or connect an existing one const signer = Wallet.createRandom().connect(provider) // Fund the wallet using Tevm's direct state modification await client.setBalance({ address: signer.address, value: 1000000000000000000n // 1 ETH }) ``` #### Start Using Ethers You're now ready to use ethers.js with Tevm's local environment: ```ts // Check the current block const blockNumber = await provider.getBlockNumber() console.log(`Current block: ${blockNumber}`) // Check your wallet balance const balance = await provider.getBalance(signer.address) console.log(`Wallet balance: ${balance.toString()}`) ```
### Complete Example The following example demonstrates how to use ethers.js with Tevm to interact with a contract: ```ts showLineNumbers {3,12-13,27-28,47-48,66} filename="ethers-with-tevm.ts" import { createMemoryClient, http, parseAbi } from 'tevm' import { optimism } from 'tevm/chains' import { requestEip1193 } from 'tevm/decorators' // [!code focus] import { BrowserProvider, Wallet, Contract, formatEther } from 'ethers' import { parseUnits } from 'ethers/utils' // Step 1: Create a memory client forking from Optimism const client = createMemoryClient({ fork: { transport: http('https://mainnet.optimism.io'), common: optimism }, }) // Step 2: Enable EIP-1193 compatibility for ethers // [!code focus] client.transport.tevm.extend(requestEip1193()) // [!code focus] await client.tevmReady() // Step 3: Set up ethers provider and wallet const provider = new BrowserProvider(client.transport.tevm) const signer = Wallet.createRandom().connect(provider) // Verify connectivity by getting the block number const blockNumber = await provider.getBlockNumber() console.log(`Current block number: ${blockNumber}`) // Step 4: Set up contract interaction // [!code focus] const greeterContractAddress = "0x10ed0b176048c34d69ffc0712de06CbE95730748" // [!code focus] const greeterAbi = parseAbi([ 'function greet() view returns (string)', 'function setGreeting(string memory _greeting) public', ]) const greeter = new Contract(greeterContractAddress, greeterAbi, signer) // Helper functions for cleaner interaction const getGreeting = async () => await greeter.greet() const setGreeting = async (newGreeting) => { const tx = await greeter.setGreeting(newGreeting) return tx.hash } // Step 5: Fund our wallet (using Tevm's test actions) await client.setBalance({ address: signer.address, value: parseUnits("1.0", "ether") }) console.log(`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`) // Step 6: Read current greeting console.log(`Original greeting: ${await getGreeting()}`) // Step 7: Update the greeting // [!code focus] const txHash = await setGreeting("Hello from ethers.js and Tevm!") // [!code focus] console.log(`Transaction sent: ${txHash}`) // Step 8: Mine the block to include our transaction await client.mine({ blocks: 1 }) // Step 9: Verify the updated greeting console.log(`Updated greeting: ${await getGreeting()}`) ``` Unlike real networks, Tevm requires you to manually mine blocks after sending transactions. Don't forget to call `client.mine()` after each transaction!
Understanding the Code The integration between Tevm and ethers.js happens through these key components: #### EIP-1193 Decorator ```ts // Enable EIP-1193 standard provider interface client.transport.tevm.extend(requestEip1193()) ``` The `requestEip1193()` decorator transforms Tevm's internal interface into the standard provider interface that ethers.js expects. This crucial bridge allows the two libraries to communicate seamlessly. #### Provider Creation ```ts // Create ethers provider using the Tevm node const provider = new BrowserProvider(client.transport.tevm) ``` Ethers accepts the decorated Tevm node as a standard Ethereum provider, allowing all ethers functionality to work as expected. #### Hybrid API Approach The example demonstrates a hybrid approach: * **Ethers API** for contract interaction (`Contract`, `provider`, `signer`) * **Tevm API** for blockchain control (`client.setBalance()`, `client.mine()`) This gives you the best of both worlds - familiar ethers.js contract interaction with Tevm's powerful state control. #### Mining Control ```ts // Mine the block to include our transaction await client.mine({ blocks: 1 }) ``` Unlike when using real networks with ethers, you need to explicitly mine blocks with Tevm to include transactions. This gives you precise control over transaction execution timing.
### Common Patterns #### Contract Deployment Here's how to deploy a new contract using ethers.js with Tevm: ```ts showLineNumbers import { createMemoryClient } from 'tevm' import { requestEip1193 } from 'tevm/decorators' import { BrowserProvider, Wallet, ContractFactory, formatEther } from 'ethers' // Set up Tevm and ethers const client = createMemoryClient() client.transport.tevm.extend(requestEip1193()) await client.tevmReady() const provider = new BrowserProvider(client.transport.tevm) const signer = Wallet.createRandom().connect(provider) // Fund the signer await client.setBalance({ address: signer.address, value: 10000000000000000000n // 10 ETH }) console.log(`Wallet funded with: ${formatEther(await provider.getBalance(signer.address))} ETH`) // Contract ABI and bytecode const counterAbi = [ 'function count() view returns (uint256)', 'function increment() public' ] const counterBytecode = '0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5fb46adf6ce0cfd90fa4324ffd8c48b0fc6fb6c4cac9ca2c69c97e25f355c9d64736f6c63430008110033' // Deploy contract const counterFactory = new ContractFactory(counterAbi, counterBytecode, signer) const counterDeploy = await counterFactory.deploy() console.log(`Deployment transaction: ${counterDeploy.deploymentTransaction().hash}`) // Mine to include the deployment transaction await client.mine({ blocks: 1 }) // Get contract at deployed address const counter = await counterDeploy.waitForDeployment() const counterAddress = await counter.getAddress() console.log(`Counter deployed at: ${counterAddress}`) // Use the contract console.log(`Initial count: ${await counter.count()}`) const tx = await counter.increment() console.log(`Increment transaction: ${tx.hash}`) await client.mine({ blocks: 1 }) console.log(`New count: ${await counter.count()}`) ``` #### Reading and Writing Contract Data Interact with an existing contract using ethers.js: ```ts import { createMemoryClient, http, parseAbi } from 'tevm' import { mainnet } from 'tevm/chains' import { requestEip1193 } from 'tevm/decorators' import { BrowserProvider, Wallet, Contract, formatEther, formatUnits } from 'ethers' // Setup client with a mainnet fork const client = createMemoryClient({ fork: { transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY'), common: mainnet } }) client.transport.tevm.extend(requestEip1193()) await client.tevmReady() // Setup provider and wallet const provider = new BrowserProvider(client.transport.tevm) const signer = Wallet.createRandom().connect(provider) // Fund the wallet await client.setBalance({ address: signer.address, value: 100000000000000000000n // 100 ETH }) // Connect to USDC contract const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' const usdcAbi = parseAbi([ 'function name() view returns (string)', 'function symbol() view returns (string)', 'function decimals() view returns (uint8)', 'function totalSupply() view returns (uint256)', 'function balanceOf(address owner) view returns (uint256)' ]) const usdc = new Contract(usdcAddress, usdcAbi, provider) // Read-only operation const [name, symbol, decimals, totalSupply] = await Promise.all([ usdc.name(), usdc.symbol(), usdc.decimals(), usdc.totalSupply() ]) console.log(`Contract: ${name} (${symbol})`) console.log(`Decimals: ${decimals}`) console.log(`Total Supply: ${formatUnits(totalSupply, decimals)} ${symbol}`) // Check DAI's USDC balance const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F' const daiUsdcBalance = await usdc.balanceOf(daiAddress) console.log(`DAI contract's USDC balance: ${formatUnits(daiUsdcBalance, decimals)} ${symbol}`) ``` #### Working with Events Listen for and query contract events: ```ts import { createMemoryClient } from 'tevm' import { requestEip1193 } from 'tevm/decorators' import { BrowserProvider, Wallet, Contract, formatUnits } from 'ethers' import { parseAbi } from 'tevm' // Setup const client = createMemoryClient() client.transport.tevm.extend(requestEip1193()) await client.tevmReady() const provider = new BrowserProvider(client.transport.tevm) const signer = Wallet.createRandom().connect(provider) // Fund account await client.setBalance({ address: signer.address, value: 10000000000000000000n }) // Deploy a simple ERC20 token const tokenAbi = parseAbi([ 'constructor(string name, string symbol, uint8 decimals)', 'function transfer(address to, uint256 amount) returns (bool)', 'function balanceOf(address owner) view returns (uint256)', 'event Transfer(address indexed from, address indexed to, uint256 value)' ]) const tokenBytecode = '0x608060405234801561001057600080fd5b506040516107fa3803806107fa83398101604081905261002f91610215565b600380546001600160a01b031916331790558151610052906004906020850190610076565b5081516100669060059060208401906100fd565b506006805460ff191660ff929092169190911790555061030c565b828054610082906102c8565b90600052602060002090601f0160209004810192826100a4576000855561010a565b82601f106100bd57805160ff19168380011785556100ea565b828001600101855582156100ea579182015b828111156100ea5782518255916020019190600101906100cf565b506100f69291506100f6565b5090565b5b808211156100f657600081556001016100f7565b828054610109906102c8565b90600052602060002090601f01602090048101928261012b5760008552610167565b82601f1061014457805160ff1916838001178555610171565b82800160010185558215610171579182015b82811115610171578251825591602001919060010190610156565b5061017d9291506101bd565b5090565b5b8082111561017d576000815560010161017e565b6000602082840312156101a657600080fd5b81516001600160a01b03811681146101bd57600080fd5b9392505050565b5b8082111561017d57600081556001016101be565b80516001600160a01b03811681146101eb57600080fd5b919050565b600080600060608486031215610205578283fd5b833590925060208401359160408401359050509250925092565b60008060006060848603121561022a578081fd5b835160208501516040860151919450919290910181111581146102ad57634e487b7160e01b600052601160045260246000fd5b809150509250925092565b634e487b7160e01b600052602260045260246000fd5b600181811c908216806102dc57607f821691505b6020821081036102fc57634e487b7160e01b600052602260045260246000fd5b50919050565b6104df8061031b6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806370a082311461004657806395d89b4114610079578063a9059cbb14610081575b600080fd5b610066610054366004610356565b6001600160a01b031660009081526001602052604090205490565b60405190815260200160405180910390f35b61007b610094565b005b61007b61008f366004610379565b610121565b6005805461009f906104a0565b80601f01602080910402602001604051908101604052809291908181526020018280546100cb906104a0565b80156101185780601f106100ed57610100808354040283529160200191610118565b820191906000526020600020905b8154815290600101906020018083116100fb57829003601f168201915b505050505081565b6001600160a01b03821660009081526001602052604081205461014a908363ffffffff61022916565b6001600160a01b038416600090815260016020526040812091909155610177908263ffffffff61024116565b6001600160a01b0383166000908152600160205260409020556101d0816040518060600160405280602381526020016104876023913960405180604001604052806029815260200161045e60299139600090339161025a565b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610216918252602082015260400190565b60405180910390a35050565b600081831061023857506000610239565b5b92915050565b6000828201610239825b8351602g2b6020600020905b905090565b60408051808201909152602d8152600080516020610457833981519152602082015290565b9050919050565b841515610297576001600160e01b031916600052601160045260246000fd5b5063ffffffff1690565b6000602082840312156102b357600080fd5b81356001600160a01b03811681146102ca57600080fd5b9392505050565b6000602082840312156102e357600080fd5b813560fc81168114t12870be52600052602260045260246000d5b602082108a5734e487b7160q0b600052602260045260246000fd5b50919050565b6004f3fe68747470733a2f2f6769646c6572732e696f2f6a616d657368756768657369o97365722f7468696e67736e6f626f6479656c7365686173a2646970667358221220f3acb75fca24514561f12a53c4a92042a9a6c524895dc1b01f3c3d2cda5d8ff364736f6c634300080a0033' const tokenFactory = new ContractFactory(tokenAbi, tokenBytecode, signer) const token = await tokenFactory.deploy('Test Token', 'TST', 18) await client.mine({ blocks: 1 }) const tokenAddress = await token.getAddress() console.log(`Token deployed at: ${tokenAddress}`) // Set up event listener token.on('Transfer', (from, to, value, event) => { console.log(`Transfer detected in block ${event.blockNumber}:`) console.log(` From: ${from}`) console.log(` To: ${to}`) console.log(` Value: ${formatUnits(value, 18)} TST`) }) // Send a transfer transaction const recipient = Wallet.createRandom().address const tx = await token.transfer(recipient, 1000000000000000000n) // 1 TST console.log(`Transfer transaction: ${tx.hash}`) // Mine to include transaction await client.mine({ blocks: 1 }) // Check balance const balance = await token.balanceOf(recipient) console.log(`Recipient balance: ${formatUnits(balance, 18)} TST`) // Clean up event listener token.removeAllListeners() ``` ### Key Differences from Standard Ethers Usage When using ethers.js with Tevm, keep these key differences in mind: **Mining is manual!** Transactions won't be automatically mined - you need to call `client.mine()` to process them. 1. **Transaction Flow** * **Real Network**: Transactions are mined by the network automatically * **Tevm**: You must manually call `client.mine()` to process transactions 2. **Account Management** * **Real Network**: Accounts have real funds and nonces * **Tevm**: Create and fund accounts on demand with `client.setBalance()` 3. **Environment Control** * **Real Network**: Limited ability to manipulate state, block time, etc. * **Tevm**: Complete control over all aspects of the environment 4. **Performance** * **Real Network**: Operations require network calls and confirmations * **Tevm**: All operations happen locally with near-instant execution 5. **Debugging Capabilities** * **Real Network**: Limited visibility into transaction execution * **Tevm**: Step-by-step EVM tracing and execution insights 6. **Determinism** * **Real Network**: Network conditions can affect execution * **Tevm**: 100% deterministic execution for reliable testing ### Advanced Features #### EVM State Control Take advantage of Tevm's state manipulation with ethers: ```ts // Fund an account await client.setBalance({ address: '0x123...', value: parseUnits('1000', 'ether') }) // Set storage directly await client.setStorageAt({ address: contractAddress, index: 0, // Storage slot value: '0x1234...' }) // Adjust block timestamp await client.setNextBlockTimestamp(Date.now()) ``` #### Contract Debugging Debug ethers transactions with Tevm's EVM hooks: ```ts // Get underlying Tevm node for advanced operations const tevmNode = await client.getTevmNode() const vm = await tevmNode.getVm() // Listen to EVM execution steps vm.evm.events.on('step', (data, next) => { console.log(`EVM Step: ${data.opcode.name}`) next() }) // Now run a transaction with ethers as normal // You'll see detailed logs of each EVM step ``` #### Fork Management Work with mainnet contracts in your test environment: ```ts // Reset to a clean fork state await client.reset({ fork: { blockNumber: 42069n, transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY') } }) // After reset, your ethers provider still works // but with the newly reset chain state ``` ### Next Steps Now that you know how to use ethers.js with Tevm, you can:
import { Callout, Steps, Button } from "vocs/components"; ## Tevm Node Overview Tevm Node is a **full Ethereum execution environment** that runs natively in JavaScript. It provides the complete functionality of an Ethereum node without requiring any blockchain client installation. Conceptually, it works very similar to Anvil and Hardhat but with more powerful TypeScript native interop. :::tip[You might not need these docs?] If you already know how to use [`viem`](https://viem.sh) or [`ethers`](https://docs.ethers.org), you can start using Tevm Node right away with your existing knowledge! Tevm simply plugs into your library of choice. ::: ### What Makes Tevm Unique? Unlike traditional development environments like [Hardhat](https://hardhat.org/) or [Anvil](https://book.getfoundry.sh/anvil/), Tevm Node offers several distinct advantages: * **Cross-Platform Compatibility** โ€” The same code works everywhere JavaScript runs, with zero native dependencies including your applications in the browser * **Fine-grained EVM Control** โ€” Access the execution environment at any level of detail, from high-level transactions to individual EVM opcodes * **Enhanced User Experience** โ€” Enable advanced features like instantaneous gas estimation, optimistic UI updates, and sophisticated transaction simulation * **Type-safe Interactions** โ€” Full TypeScript support throughout the entire API surface * **Direct Solidity Imports** โ€” Import Solidity files directly into TypeScript with the [Tevm Bundler](/getting-started/bundler) ### Getting Started ::::steps #### Install Tevm First, install Tevm in your project: :::code-group ```bash [npm] npm install tevm ``` ```bash [pnpm] pnpm add tevm ``` ```bash [yarn] yarn add tevm ``` ```bash [bun] bun add tevm ``` ```ts [deno] // No installation needed - import directly from npm registry import { createMemoryClient } from "npm:tevm"; // Then use as normal const client = createMemoryClient(); ``` ::: #### Choose Your Client Tevm offers different client types depending on your needs: ```ts // In-memory client (fastest, fully isolated) import { createMemoryClient } from "tevm"; const memoryClient = createMemoryClient(); // Fork client (use existing chain state) 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, }, }); // Tree-shakable client (lighter weight) import { createClient, http, custom } from "viem"; import { createTevmNode } from "tevm"; import { optimism } from "tevm/common"; import { eip1193Request } from "tevm/decorators"; const node = createTevmNode({ fork: { transport: http("https://mainnet.optimism.io"), common: optimism, }, }).extend(eip1193Request()); const client = createClient({ transport: custom(node), }); import { getBlockNumber } from "viem"; await getBlockNumber(client); // Ethers client 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: 1. Add a contract to the Tevm state 2. Write to the contract with viem 3. Mine a new block with the transaction 4. Read from contract with viem ```ts showLineNumbers {1,11-13,22} filename="simple-contract.ts" 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 ``` :::details[How it works] 1. We create a `memoryClient` that runs an Ethereum node entirely in memory 2. We define a contract and its address 3. We deploy the contract using `setCode` (simpler than a full deployment transaction) 4. We write to the contract using a familiar viem-style interface 5. We mine the transaction to include it in a block 6. We read the updated value from the contract ::: #### Key Points: * **Familiar Interface** โ€” Tevm uses [viem](https://viem.sh) as its primary API, making it instantly familiar to viem users * **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 #### Start Building You're now ready to build with Tevm! ```ts // 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?.(); }, }); ``` :::: ### Learning Path If you're new to Ethereum development, don't worry! Tevm serves as an excellent learning platform for both Ethereum and TypeScript: :::info * **Isolated Environment** โ€” Experiment freely without worrying about network costs or confirmation times * **TypeScript Integration** โ€” Get full type safety and autocomplete for Ethereum interactions * **Simple API Surface** โ€” Focus on learning core concepts with a streamlined interface * **Step Debugger** โ€” Trace transaction execution at the opcode level for deep understanding ::: ### Ready to dive in right away?
import { Callout, Button } from "vocs/components"; ## Community Here's what developers and industry leaders are saying about Tevm ### Community Highlights :::tip[Developer Feedback] Tevm's unique approach of bringing Ethereum execution directly to JavaScript environments has resonated with most developers who have given Tevm a try. Developers report problems previously difficult being simple to solve with Tevm :::
"Fully @tevmtools pilled now. The beauty and harmony of the dev tooling ๐Ÿ˜."
EulerLagrange.eth - ver/acc
@Euler\_\_Lagrange
{" "}
"If you're building a blockchain application on the web, I'm almost certain there is a use case for Tevm. It might change everything, and at worse it would most likely improve your devX."
polarzero
@0xpolarzero
{" "}
"Yeah, using bundling tools like Tevm with Viem offers a very nice experience here."
"Incredible! And also very cursed haha"
Darryl Yeoใƒปd/acc
@darryl\_\_yeo
{" "}
"tevm is the accessible version of reth exex for data engineers"
{" "}
"Helios ๐Ÿค Tevm"
ncitron.eth
@NoahCitron
{" "}
"Please tell me this is a shitpost"
Patrick Collins
@PatrickAlphaC
"I don't know if I should be impressed or scared"
### Share Your Experience :::steps #### Join the Community We'd love to hear how Tevm has improved your development workflow: * [Telegram Group](https://t.me/+ANThR9bHDLAwMjUx) * [Twitter/X](https://twitter.com/tevmtools) #### Report Success Stories Have a compelling use case or success story? * [Create an issue](https://github.com/evmts/tevm-monorepo/issues/new?labels=testimonial\&template=testimonial.md) with the label "testimonial" * Email the team at [support@tevm.sh](mailto\:support@tevm.sh) ::: Your feedback helps us improve Tevm and guides our development priorities!
import { Callout, Steps, Button } from "vocs/components"; ## Getting Started with Viem :::tip[Perfect for Viem Users] If you're already familiar with [viem](https://viem.sh), Tevm works seamlessly with your existing knowledge, providing a nearly identical API. ::: This guide will help you integrate Tevm with [viem](https://viem.sh), the modern TypeScript interface for Ethereum. By the end, you'll have a working setup with Tevm Node and understand how to leverage viem's actions with Tevm. ### Installation ::::steps #### Install Dependencies First, install Tevm along with viem as a peer dependency: :::code-group ```bash [npm] npm install tevm viem@latest ``` ```bash [pnpm] pnpm add tevm viem@latest ``` ```bash [yarn] yarn add tevm viem@latest ``` ```bash [bun] bun add tevm viem@latest ``` ::: #### Create Your Client For the quickest start, create a memory client: ```ts import { createMemoryClient } from "tevm"; const client = createMemoryClient(); ``` Or, to fork from an existing chain: ```ts import { createMemoryClient } from "tevm"; import { optimism } from "tevm/chains"; import { http } from "viem"; const client = createMemoryClient({ fork: { transport: http("https://mainnet.optimism.io"), common: optimism, }, }); // Wait for the node to be ready before using it await client.tevmReady(); ``` #### You're Ready! Start using your client with familiar viem actions: ```ts // Get the current block number const blockNumber = await client.getBlockNumber(); console.log(`Current block: ${blockNumber}`); ``` :::: ### Complete Example The following example demonstrates the key capabilities of Tevm with viem: :::code-group ```ts [Forking Example] showLineNumbers {1-3,8-12,25-29,40,52} import { createMemoryClient, http } from "tevm"; // [!code focus] import { optimism } from "tevm/common"; // [!code focus] import { parseAbi, parseEther } from "viem"; // [!code focus] // 1. Create a memory client forked from Optimism mainnet const client = createMemoryClient({ fork: { transport: http("https://mainnet.optimism.io"), // [!code focus] common: optimism, // [!code focus] }, // [!code focus] }); // [!code focus] // Wait for the node to be ready await client.tevmReady(); // 2. Get current block number (from the fork point) const blockNumber = await client.getBlockNumber(); console.log(`Current block number: ${blockNumber}`); // Setup addresses and contract interfaces const account = `0x${"baD60A7".padStart(40, "0")}` as const; const greeterContractAddress = "0x10ed0b176048c34d69ffc0712de06CbE95730748"; // Define contract interfaces with parseAbi const greeterAbi = parseAbi([ "function greet() view returns (string)", // [!code focus] "function setGreeting(string memory _greeting) public", // [!code focus] ]); // [!code focus] // 3. Modify blockchain state with test actions // Fund our test account with 1 ETH await client.setBalance({ address: account, value: parseEther("1"), }); // Read the current greeting using viem's readContract const currentGreeting = await client.readContract({ address: greeterContractAddress, abi: greeterAbi, functionName: "greet", }); console.log(`Current greeting: ${currentGreeting}`); // Update the greeting with writeContract const txHash = await client.writeContract({ // [!code focus] account, address: greeterContractAddress, abi: greeterAbi, functionName: "setGreeting", args: ["Hello from Tevm!"], chain: optimism, }); console.log(`Transaction sent: ${txHash}`); // 4. Mine a new block to include our transaction await client.mine({ blocks: 1 }); // [!code focus] // Verify the greeting was updated const updatedGreeting = await client.readContract({ address: greeterContractAddress, abi: greeterAbi, functionName: "greet", }); console.log(`Updated greeting: ${updatedGreeting}`); ``` ```ts [Contract Deployment] showLineNumbers import { createMemoryClient } from "tevm"; import { parseAbi, parseEther, encodeAbiParameters } from "viem"; // Create a memory client (isolated environment) const client = createMemoryClient(); // 1. Create a test account with funds const deployerAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; await client.setBalance({ address: deployerAddress, value: parseEther("10"), }); // 2. Define a simple contract const counterAbi = parseAbi([ "function increment() public", "function count() view returns (uint256)", ]); // Counter contract bytecode (pre-compiled) const counterBytecode = "0x608060405234801561001057600080fd5b5060f78061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220d5fb46adf6ce0cfd90fa4324ffd8c48b0fc6fb6c4cac9ca2c69c97e25f355c9d64736f6c63430008110033"; // 3. Deploy the contract const deployHash = await client.sendTransaction({ account: deployerAddress, to: undefined, // Contract creation data: counterBytecode, }); // Mine to include the deployment await client.mine(); // 4. Get the deployment receipt to find contract address const receipt = await client.getTransactionReceipt({ hash: deployHash, }); const contractAddress = receipt.contractAddress; console.log(`Contract deployed at: ${contractAddress}`); // 5. Use the contract const initialCount = await client.readContract({ address: contractAddress, abi: counterAbi, functionName: "count", }); console.log(`Initial count: ${initialCount}`); // Increment the counter await client.writeContract({ account: deployerAddress, address: contractAddress, abi: counterAbi, functionName: "increment", }); // Mine the transaction await client.mine(); // Read the new count const newCount = await client.readContract({ address: contractAddress, abi: counterAbi, functionName: "count", }); console.log(`New count: ${newCount}`); ``` ::: :::details[Code Walkthrough] #### 1. Imports & Client Creation ```ts import { createMemoryClient, http } from "tevm"; import { optimism } from "tevm/common"; import { parseAbi, parseEther } from "viem"; const client = createMemoryClient({ fork: { transport: http("https://mainnet.optimism.io"), common: optimism, }, }); await client.tevmReady(); ``` * We create a client that **forks** from Optimism mainnet * This gives us a local sandbox with all of mainnet's state * `client.tevmReady()` ensures the fork is complete before proceeding #### 2. Contract Interaction ```ts // Define the contract interface const greeterAbi = parseAbi([ "function greet() view returns (string)", "function setGreeting(string memory _greeting) public", ]); // Read from contract const currentGreeting = await client.readContract({ address: greeterContractAddress, abi: greeterAbi, functionName: "greet", }); // Write to contract const txHash = await client.writeContract({ account, address: greeterContractAddress, abi: greeterAbi, functionName: "setGreeting", args: ["Hello from Tevm!"], chain: optimism, }); ``` * The API matches viem exactly - anyone familiar with viem can use this immediately * Write operations return a transaction hash just like on a real network #### 3. Mining Control ```ts // Mine a block to include our transaction await client.mine({ blocks: 1 }); ``` * Unlike real networks, you control exactly when blocks are mined * This gives you complete determinism for testing and development ::: ### Key Viem-Compatible Features Tevm's viem client implements the full viem API, maintaining compatibility while adding powerful features: :::code-group ```ts [Standard viem API] // These standard viem actions work exactly as expected await client.getBalance({ address: '0x...' }) await client.getBlockNumber() await client.readContract({ ... }) await client.writeContract({ ... }) await client.estimateGas({ ... }) await client.sendTransaction({ ... }) // And all other viem actions ``` ```ts [Tevm Extensions] // Tevm adds powerful extensions await client.tevmReady(); // Wait for fork initialization await client.setBalance({ address: "0x...", value: 100n }); await client.setCode({ address: "0x...", bytecode: "0x..." }); await client.mine({ blocks: 1 }); await client.reset(); // Reset to initial state await client.impersonateAccount({ address: "0x..." }); ``` ```ts [EVM Debugging] // Advanced EVM debugging and inspection await client.tevmContract({ address: '0x...', abi: [...], functionName: 'transfer', args: ['0x...', 100n], // Get step-by-step EVM execution details onStep: (data, next) => { console.log(`Opcode: ${data.opcode.name}`) console.log(`Stack: ${data.stack.join(', ')}`) next() // Continue execution } }) ``` ```ts [Complete tevmContract Example] // Complete example showing all available options and return values const { // Decoded return data based on the ABI data, // Raw return data as hex string rawData, // Any errors that occurred during execution error, // Event logs emitted during execution logs, // Addresses created during execution (for contract creation) createdAddresses, // Gas used by the execution only executionGasUsed, // Total gas spent (includes upfront costs) totalGasSpent, // Fee for L1 data (for L2 chains) l1Fee, // L1 gas used (for L2 chains) l1GasUsed, // L1 base fee (for L2 chains) l1BaseFee, // L1 blob fee (for L2 chains) l1BlobFee, // Detailed execution trace (if createTrace:true) trace, // EIP-2930 access list (if createAccessList:true) accessList, // Transaction hash (if createTransaction is set) txHash, // Amount spent on this transaction amountSpent, // Remaining gas gas, // Set of addresses to self-destruct selfdestruct } = await client.tevmContract({ // Contract details to: '0x1234...', // Contract address abi: [...], // Contract ABI functionName: 'myFunction', // Function to call args: [123n, 'hello'], // Function arguments deployedBytecode: '0x...', // Alternative to 'to' + 'abi' approach // Call configuration from: '0x5678...', // Caller address value: 1000000000000000000n, // ETH value to send (in wei) gas: 1000000n, // Gas limit gasPrice: 1000000000n, // Gas price blockTag: 'latest', // Block context for execution // Advanced options createTrace: true, // Generate execution trace createAccessList: true, // Generate EIP-2930 access list createTransaction: 'always', // Create tx ('always', 'on-success', or false) throwOnFail: false, // Throw on execution failure skipBalance: true, // Skip balance checks // EVM execution hooks onStep: (step, next) => { // Called for each EVM operation console.log(step.opcode.name); next(); }, onNewContract: (contract, next) => { // Called when a new contract is created console.log(contract.address); next(); }, onBeforeMessage: (msg, next) => { // Called before a message call console.log(msg.depth); next(); }, onAfterMessage: (result, next) => { // Called after a message call console.log(result.execResult.executionGasUsed); next(); } }); ``` ::: ### Tree-Shakeable API For production applications, especially in browser environments, you may want to use Tevm's tree-shakeable API to minimize bundle size: ```ts showLineNumbers {1-3,6-11} import { createClient, http } from "viem"; import { createTevmTransport } from "tevm/transport"; // [!code focus] import { tevmCall, tevmDumpState } from "tevm/actions"; // [!code focus] // Create a standard viem client with Tevm transport const client = createClient({ transport: createTevmTransport({ // [!code focus] fork: { // [!code focus] transport: http("https://mainnet.optimism.io"), // [!code focus] }, // [!code focus] }), // [!code focus] }); // Import only the actions you need await tevmDumpState(client); ``` :::tip[Tree-Shaking Benefit] Using the tree-shakeable API can significantly reduce your bundle size when you're using only a subset of Tevm's features. ::: ### EVM Execution Hooks One of Tevm's most powerful features is the ability to hook directly into EVM execution: ```ts showLineNumbers {7-16} await client.tevmContract({ address: greeterContractAddress, abi: greeterAbi, functionName: "setGreeting", args: ["Hello!"], // onStep is called for each EVM operation // [!code focus] onStep: (stepInfo, next) => { // [!code focus] console.log(`Executing: ${stepInfo.opcode.name} at PC=${stepInfo.pc}`); // [!code focus] console.log(`Stack: ${stepInfo.stack.map((val) => val.toString())}`); // [!code focus] console.log(`Memory: ${stepInfo.memory.toString("hex")}`); // [!code focus] // You can also modify EVM state here if needed // [!code focus] // Call next() to continue execution // [!code focus] next?.(); // [!code focus] }, // [!code focus] // You can also access the detailed result after execution onResult: (result) => { console.log(`Gas used: ${result.executionGasUsed}`); console.log(`Return value: 0x${result.returnValue?.toString("hex")}`); }, }); ``` This enables advanced use cases like: * **Visual Debuggers**: Create step-by-step transaction debuggers * **Educational Tools**: Explain EVM execution for learning purposes * **Custom Instrumentation**: Profile and analyze contract execution * **Intercepting Execution**: Modify execution behavior for testing ### Tevm-Specific Actions Tevm extends viem with powerful specialized actions, all prefixed with `tevm`. These actions provide direct access to Tevm's enhanced Ethereum Virtual Machine capabilities, giving you unprecedented control and visibility into blockchain execution. #### The Power of `tevmContract` Among all Tevm actions, `tevmContract` stands out as the most versatile and powerful. It can do almost anything with the Tevm EVM, combining the simplicity of high-level contract interactions with the depth of low-level EVM control: * **Complete EVM Access**: Hook into any phase of EVM execution at the opcode level * **Rich Result Data**: Get detailed execution results beyond just the return value * **Execution Visibility**: Capture traces, access lists, gas usage, and more * **Flexible Contract Interface**: Work with contracts using address+ABI or directly with bytecode * **Layer 2 Awareness**: Get L1 fee calculations and gas usage for L2 chains When you need maximum control and insight into contract execution, `tevmContract` is your go-to action. It seamlessly handles everything from simple function calls to complex debugging scenarios. #### Complete Action Reference Here's the full list of Tevm-specific actions: | Action | Description | Use Case | | ------------------- | -------------------------------------------- | --------------------------------------- | | `tevmCall` | Low-level EVM call with execution hooks | Deep inspection of contract execution | | `tevmContract` | Enhanced contract interaction with EVM hooks | Detailed debugging of contract calls | | `tevmDeploy` | Deploy with execution hooks | Understanding deployment execution flow | | `tevmMine` | Control block mining | Precise transaction inclusion control | | `tevmSetAccount` | Modify account state | Test different account scenarios | | `tevmGetAccount` | Read detailed account state | Inspect nonce, code, storage | | `tevmDumpState` | Export full EVM state | State persistence and analysis | | `tevmLoadState` | Import saved EVM state | Restore a specific state for testing | | `tevmReady` | Wait for fork to initialize | Ensure node is ready before use | | `tevmEnableTracing` | Enable detailed execution tracing | Capture full execution traces | | `tevmReset` | Reset node to initial state | Start fresh for new test scenarios | :::info All tevm-specific actions are also available as individual imports from `tevm/actions` for tree-shaking. ::: ### Next Steps Now that you're familiar with using Tevm with viem, you can: :::steps #### Explore More Tevm Features Dive deeper into Tevm's powerful capabilities: * [Forking capabilities](/core/forking) to simulate production chains * [State management](/core/managing-state) for manipulating the blockchain * [Mining modes](/core/mining-modes) for controlling transaction inclusion * [Direct Solidity imports](/getting-started/bundler) with the Tevm Bundler #### Check Out Examples See how Tevm solves real-world problems: * [Forking mainnet](/examples/forking-mainnet) for production simulation * [Building a debugger UI](/examples/debugger-ui) with EVM insights * [Local testing flows](/examples/local-testing) for development #### Advanced API Usage Master the Tevm API for more sophisticated applications: * [EVM events & hooks](/api/evm-events) for detailed execution analysis * [Custom precompiles](/advanced/custom-precompiles) for extending the EVM * [Transaction pool management](/advanced/txpool) for pending transaction control * [Contract Bundler](/reference/bundler) for importing Solidity files directly :::
## Building a Debugger UI These docs have not been checked for correctness yet. Use with caution This example demonstrates how to create a minimal EVM debugger interface using [Svelte](https://svelte.dev) and Tevm Node. The debugger will show: * Live opcode execution * Stack contents * Memory state * Error messages * Gas usage ### Project Setup First, create a new Svelte project and install dependencies: ```bash npm create vite@latest tevm-debugger -- --template svelte-ts cd tevm-debugger npm install tevm tevm/contract ``` ### Components #### 1. EVMDebugger.svelte ```svelte
Gas Used: {gasUsed.toString()}

Current Step

{#if currentStep}
PC: {currentStep.pc}
Opcode: {currentStep.opcode.name}
Gas Left: {currentStep.gasLeft.toString()}
Depth: {currentStep.depth}
{/if}

Stack

{#if currentStep?.stack}
{#each currentStep.stack as item}
{item.toString(16)}
{/each}
{/if}

Errors

{#each errors as error}
{error}
{/each}

Execution History ({steps.length} steps)

{#each steps as step}
{step.opcode.name} (Gas: {step.gasLeft.toString()})
{/each}
``` #### 2. App.svelte ```svelte

Tevm Debugger

``` ### Advanced Features #### Memory Viewer Component ```svelte
{#each rows as row, i}
{(startOffset + i * bytesPerRow).toString(16).padStart(8, '0')}: {#each row as byte} {formatByte(byte)} {/each} {#each row as byte} {formatAscii(byte)} {/each}
{/each}
``` #### Storage Viewer Component ```svelte
{#if storage.size > 0}
{#each [...storage] as [slot, value]}
{slot}: {value}
{/each}
{/if}
``` ### Usage 1. Create the project structure: ``` tevm-debugger/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ lib/ โ”‚ โ”‚ โ”œโ”€โ”€ EVMDebugger.svelte โ”‚ โ”‚ โ”œโ”€โ”€ MemoryViewer.svelte โ”‚ โ”‚ โ””โ”€โ”€ StorageViewer.svelte โ”‚ โ”œโ”€โ”€ App.svelte โ”‚ โ””โ”€โ”€ main.ts โ””โ”€โ”€ package.json ``` 2. Run the development server: ```bash npm run dev ``` 3. Use the debugger: ```ts // Example contract deployment const bytecode = '0x...' // Your contract bytecode await vm.runTx({ tx: { data: bytecode } }) // Example contract interaction await vm.runTx({ tx: { to: '0x...', // Contract address data: '0x...', // Encoded function call } }) ``` ### Customization #### Adding Transaction History ```svelte

Transaction History

{#each $transactions as tx}
Hash: {tx.hash}
To: {tx.to}
Data: {tx.data}
{/each}
``` #### Adding Gas Profiling ```svelte

Gas Profile

{#each [...gasProfile] as [opcode, stats]} {/each}
Opcode Count Total Gas
{opcode} {stats.count} {stats.totalGas.toString()}
``` ### Related Topics * [EVM Events](../api/evm-events) * [Performance Profiler](../advanced/performance-profiler) import { Callout, Steps, Step, Button } from 'vocs/components' import { Tab, TabGroup, Card, CardGrid } from '../../../components' ## Using with Ethers.js v6 Tevm integrates seamlessly with Ethers.js v6, allowing you to use your favorite Ethereum library alongside Tevm's powerful in-memory EVM capabilities. This guide demonstrates how to use Tevm Node with Ethers.js to build powerful Ethereum applications with a familiar, developer-friendly API. ### Setup First, install the required packages: ```bash npm install tevm ethers ```
Using Yarn or pnpm? ```bash # Yarn yarn add tevm ethers # pnpm pnpm add tevm ethers ```
Set up a Tevm node with EIP-1193 compatibility: ```ts import { createTevmNode } from 'tevm' import { requestEip1193 } from 'tevm/decorators' // Create a Tevm Node with optional configuration const node = createTevmNode({ // Configure mining behavior (auto or interval) miningConfig: { type: 'interval', interval: 2000 // Mine blocks every 2 seconds } }) // Add EIP-1193 compatibility layer for Ethers.js const nodeWithEip1193 = node.extend(requestEip1193()) // Wait for the node to be ready await node.ready() ``` The requestEip1193() decorator is essential - it adds the standard Ethereum provider interface that Ethers.js requires. Connect Ethers to your Tevm node: ```ts import { BrowserProvider } from 'ethers' // Create a provider using the EIP-1193 compatible node const provider = new BrowserProvider(nodeWithEip1193) // Test the connection const blockNumber = await provider.getBlockNumber() console.log(`Connected to block: ${blockNumber}`) ``` BrowserProvider is recommended for most applications - it's the modern Ethers.js provider and handles all async operations correctly. ```ts import { JsonRpcProvider } from 'ethers' // For legacy code bases that require JsonRpcProvider const legacyProvider = new JsonRpcProvider( // Pass the node as an endpoint nodeWithEip1193 ) // Test the connection const network = await legacyProvider.getNetwork() console.log(`Connected to network: ${network.name} (${network.chainId})`) ``` Create a wallet for transactions: ```ts import { Wallet } from 'ethers' // Generate a random wallet const wallet = Wallet.createRandom() console.log(`Generated wallet address: ${wallet.address}`) // Connect the wallet to your provider const signer = wallet.connect(provider) // The default balance will be zero const balance = await provider.getBalance(signer.address) console.log(`Initial wallet balance: ${balance} wei (${balance === 0n ? 'empty' : balance})`) ``` Use Tevm's state manipulation to fund your testing wallet: ```ts import { parseEther, formatEther } from 'ethers' // Manipulate blockchain state directly with Tevm await node.setAccount({ address: signer.address, balance: parseEther('100') // Add 100 ETH }) // Verify the new balance const newBalance = await provider.getBalance(signer.address) console.log(`New wallet balance: ${formatEther(newBalance)} ETH`) ``` This direct state manipulation is one of Tevm's powerful features - it allows you to set up any testing scenario without complex transactions.
### Core Functionality Query contract state using Ethers.js Contract objects Execute transactions and modify blockchain state Listen for and query contract events Deploy new contracts to your local Tevm environment ### Reading from Contracts Ethers.js Contract objects work seamlessly with Tevm, allowing you to query on-chain data with a simple, type-safe API. ```ts import { Contract } from 'ethers' import { parseAbi } from 'tevm' import { formatUnits } from 'ethers' // Define contract ABI const abi = parseAbi([ 'function balanceOf(address owner) view returns (uint256)', 'function decimals() view returns (uint8)', 'function symbol() view returns (string)', 'function name() view returns (string)' ]) // Create a contract instance (using USDC on mainnet for this example) const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' const usdc = new Contract(usdcAddress, abi, provider) // Read multiple values concurrently for efficiency const [ balance, decimals, symbol, name ] = await Promise.all([ usdc.balanceOf('0x6B175474E89094C44Da98b954EedeAC495271d0F'), // DAI address usdc.decimals(), usdc.symbol(), usdc.name() ]) // Format the results console.log(`${name} (${symbol})`) console.log(`Balance: ${formatUnits(balance, decimals)} ${symbol}`) ``` ## Forking Mainnet Example These docs have not been checked for correctness yet. Use with caution ### Basic Fork Setup ```ts import { createTevmNode } from 'tevm' import { http } from 'viem' const node = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/'), blockTag: 17_000_000n, }, loggingLevel: 'debug', }) await node.ready() // Now any calls to an unknown account or contract // will fetch the data from mainnet, but store the result locally for caching. ``` ### Account Impersonation ```ts // Impersonate a whale account node.setImpersonatedAccount('0x28C6c06298d514Db089934071355E5743bf21d60') // Binance 14 // Now you can run transactions "as" that address, no signature needed const tx = createImpersonatedTx({ to: '0x1234...', value: 1000000000000000000n, // 1 ETH }) const vm = await node.getVm() await vm.runTx({ tx }) ``` ### Working with Forked Contracts ```ts // Example: Interacting with USDC on mainnet const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // Get the contract state from mainnet const usdcContract = new Contract(USDC_ADDRESS, USDC_ABI) const balance = await usdcContract.read.balanceOf(['0x1234...']) // Modify state locally await usdcContract.write.transfer(['0x5678...', 1000000]) // 1 USDC // Changes only affect your local fork const newBalance = await usdcContract.read.balanceOf(['0x1234...']) ``` ### Fork at Specific Block ```ts const node = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/'), blockTag: 15_000_000n, // Fork from a specific block }, }) // Test historical state const historicalBalance = await getBalance(address) ``` ### Multiple Network Support ```ts // Fork from different networks const optimismNode = createTevmNode({ fork: { transport: http('https://mainnet.optimism.io'), }, }) const arbitrumNode = createTevmNode({ fork: { transport: http('https://arb1.arbitrum.io/rpc'), }, }) ``` **Related** * [Forking Guide](../core/forking) * [State Management](../core/managing-state) * [JSON-RPC Support](../api/json-rpc) ## Local Testing These docs have not been checked for correctness yet. Use with caution This guide demonstrates how to use Tevm Node for local testing of [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/) and [transactions](https://ethereum.org/en/developers/docs/transactions/). For more background on testing Ethereum applications, see the [Smart Contract Testing Guide](https://ethereum.org/en/developers/docs/smart-contracts/testing/). ### Basic Test Setup ```ts import { createTevmNode } from 'tevm' import { createImpersonatedTx } from 'tevm/tx' import { expect, test } from 'vitest' // or jest, mocha, etc. test('Basic ETH transfer', async () => { // Create a new node instance const node = createTevmNode({ miningConfig: { type: 'auto' }, // Mine blocks automatically }) await node.ready() const vm = await node.getVm() // Create and run a transaction const tx = createImpersonatedTx({ from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', value: 1000000000000000000n, // 1 ETH }) const result = await vm.runTx({ tx }) // Assert transaction succeeded expect(result.execResult.exceptionError).toBeUndefined() // Check recipient balance const account = await vm.stateManager.getAccount(tx.to) expect(account.balance).toBe(1000000000000000000n) }) ``` For more information on transaction execution, see the [EVM Execution Model](https://ethereum.org/en/developers/docs/evm/execution/). ### Contract Testing #### 1. Deployment & Interaction For background on contract deployment and interaction, see the [Contract Deployment Guide](https://ethereum.org/en/developers/docs/smart-contracts/deploying/). ```ts import { Contract } from 'tevm/contract' import { bytecode, abi } from './MyContract.json' test('Deploy and interact with contract', async () => { const node = createTevmNode() await node.ready() const vm = await node.getVm() // Deploy contract const deployTx = createImpersonatedTx({ data: bytecode, }) const result = await vm.runTx({ tx: deployTx }) expect(result.execResult.exceptionError).toBeUndefined() const contractAddress = result.createdAddress expect(contractAddress).toBeDefined() // Create contract instance const contract = new Contract(contractAddress, abi) // Call contract method const callResult = await contract.read.getValue() expect(callResult).toBe(expectedValue) // Send transaction to contract const tx = await contract.write.setValue([newValue]) const txResult = await vm.runTx({ tx }) expect(txResult.execResult.exceptionError).toBeUndefined() // Verify state change const updatedValue = await contract.read.getValue() expect(updatedValue).toBe(newValue) }) ``` #### 2. Event Testing For more information on events and logs, see the [Events and Logs Guide](https://ethereum.org/en/developers/docs/smart-contracts/anatomy/#events-and-logs). ```ts test('Contract events', async () => { const node = createTevmNode() await node.ready() // Deploy contract const contract = await deployContract(node) // Create event filter node.setFilter({ id: '0x1', address: contract.address, topics: [ contract.interface.getEventTopic('ValueChanged'), ], }) // Trigger event const tx = await contract.write.setValue([123]) await vm.runTx({ tx }) // Get event logs const receipts = await node.getReceiptsManager() const logs = await receipts.getLogs({ fromBlock: 0n, toBlock: 'latest', address: contract.address, }) expect(logs.length).toBe(1) expect(logs[0].topics[0]).toBe(contract.interface.getEventTopic('ValueChanged')) }) ``` ### Complex Testing Scenarios #### 1. State Management For more information on state management, see the [Ethereum State Guide](https://ethereum.org/en/developers/docs/evm/state-machine/). ```ts test('Complex state changes', async () => { const node = createTevmNode() await node.ready() const vm = await node.getVm() // Create checkpoint await vm.stateManager.checkpoint() try { // Perform multiple state changes await performStateChanges(vm) // Verify intermediate state const intermediateState = await getState(vm) expect(intermediateState).toMatchSnapshot() // More changes await performMoreChanges(vm) // Commit changes await vm.stateManager.commit() } catch (error) { // Revert on failure await vm.stateManager.revert() throw error } }) ``` #### 2. Fork Testing For more information on network forking, see the [Forking Guide](../core/forking). ```ts test('Mainnet fork testing', async () => { const node = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/YOUR-KEY'), blockTag: 17_000_000n, }, }) await node.ready() // Impersonate a whale account node.setImpersonatedAccount('0x28C6c06298d514Db089934071355E5743bf21d60') // Test DeFi interactions const uniswap = new Contract(UNISWAP_ADDRESS, UNISWAP_ABI) const tx = await uniswap.write.swapExactTokensForTokens([/* ... */]) const result = await vm.runTx({ tx }) expect(result.execResult.exceptionError).toBeUndefined() }) ``` #### 3. Time-based Testing For more information on block timestamps and time-based operations, see the [Block Time Guide](https://ethereum.org/en/developers/docs/blocks/blocks-and-time/). ```ts test('Time-dependent behavior', async () => { const node = createTevmNode({ miningConfig: { type: 'interval', interval: 1000 }, }) await node.ready() const vm = await node.getVm() // Deploy time-locked contract const contract = await deployTimeLock(vm) // Try to withdraw (should fail) let tx = await contract.write.withdraw() let result = await vm.runTx({ tx }) expect(result.execResult.exceptionError).toBeDefined() // Advance time by mining blocks for (let i = 0; i < 100; i++) { await vm.blockchain.putBlock(createBlock({ timestamp: Date.now() + i * 1000 })) } // Try withdraw again (should succeed) tx = await contract.write.withdraw() result = await vm.runTx({ tx }) expect(result.execResult.exceptionError).toBeUndefined() }) ``` ### Testing Utilities #### 1. Account Management For more information on Ethereum accounts, see the [Accounts Guide](https://ethereum.org/en/developers/docs/accounts/). ```ts // Helper to setup test accounts async function setupAccounts(vm) { const accounts = [ '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', ] for (const address of accounts) { await vm.stateManager.putAccount(address, { nonce: 0n, balance: 10000000000000000000n, // 10 ETH }) } return accounts } ``` #### 2. Transaction Helpers For more information on transaction types and formats, see the [Transaction Types Guide](https://ethereum.org/en/developers/docs/transactions/transaction-types/). ```ts // Helper to send ETH async function sendEth(vm, from, to, value) { const tx = createImpersonatedTx({ from, to, value, }) return vm.runTx({ tx }) } // Helper to deploy contract async function deployContract(vm, bytecode, args = []) { const tx = createImpersonatedTx({ data: bytecode + encodeConstructor(args), }) const result = await vm.runTx({ tx }) return result.createdAddress } ``` ### Related Topics * [Viem Testing Guide](https://viem.sh/docs/testing/overview.html) * [EthereumJS VM](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm) * [JSON-RPC Methods](../api/json-rpc) * [Contract reference](/reference/contract) * [State Management](../core/managing-state) ### Replaying Contracts with Shadow Events ```ts import { createTevmNode } from 'tevm' import { http } from 'viem' // Create a node that forks from mainnet const node = createTevmNode({ fork: { transport: http('https://mainnet.infura.io/v3/YOUR-KEY'), }, }) // Get the transaction receipt to find its index const receipt = await node.request({ method: 'eth_getTransactionReceipt', params: ['0x...'] // Original tx hash }) // Get the block and its transactions const block = await node.request({ method: 'eth_getBlockByNumber', params: [(receipt.blockNumber - 1n).toString(16), true] }) // Replay all transactions before our target tx for (let i = 0; i < receipt.transactionIndex; i++) { const tx = block.transactions[i] await node.getVm().runTx({ tx }) } // Deploy modified contract with new event const modifiedBytecode = '0x...' // Contract bytecode with new event await node.setAccount({ address: receipt.contractAddress, deployedBytecode: modifiedBytecode }) // Now run the target transaction const result = await node.getVm().runTx({ tx: block.transactions[receipt.transactionIndex] }) // The result will include the new shadow event console.log(result.execResult.logs) ``` ### Estimating Gas for Token Approval ```ts import { createTevmNode } from 'tevm/node' import { encodeFunctionData } from 'viem' const node = createTevmNode() const vm = await node.getVm() // First approve the token const approveTx = { to: tokenAddress, data: encodeFunctionData({ abi: erc20ABI, functionName: 'approve', args: [spenderAddress, amount] }) } // Estimate gas by running the tx const result = await vm.runTx({ tx: approveTx }) console.log('Gas used:', result.execResult.executionGasUsed) // Now we can estimate transferFrom const transferFromTx = { to: tokenAddress, data: encodeFunctionData({ abi: erc20ABI, functionName: 'transferFrom', args: [ownerAddress, recipientAddress, amount] }) } const transferResult = await vm.runTx({ tx: transferFromTx }) console.log('TransferFrom gas:', transferResult.execResult.executionGasUsed) ``` import { Callout } from 'vocs/components' import { Tab, TabGroup, Card, CardGrid } from '../../../components' ## Using with Viem Tevm integrates seamlessly with viem, providing a powerful local Ethereum environment with viem's familiar API surface. 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: ```ts 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) ``` This approach enables tree-shaking, resulting in smaller bundle sizes as only the actions you import are included in your final build. A more convenient approach when bundle size isn't a primary concern: ```ts 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() ``` This approach provides a more convenient developer experience with all actions available directly on the client object, ideal for rapid development and testing. ### Core Functionality Read blockchain state, query contracts, and estimate gas Send transactions, sign messages, and interact with accounts Manipulate blockchain state for testing and development Tevm-specific extensions for enhanced EVM capabilities ### Public Actions Public actions allow you to read data from the blockchain and interact with smart contracts in a read-only fashion. Use [viem's public actions](https://viem.sh/docs/actions/public/introduction) to query your local Tevm environment: ```ts // 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 Wallet actions enable you to simulate transactions, sign messages, and interact with accounts and contracts in a write capacity. Tevm supports all of [viem's wallet actions](https://viem.sh/docs/actions/wallet/introduction) with built-in prefunded accounts: ```ts 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 ```ts 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 Test actions allow you to manipulate the blockchain state for testing purposes, similar to using Anvil or Hardhat. All of [viem's test actions](https://viem.sh/docs/actions/test/introduction) are supported for comprehensive testing capabilities: ```ts 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 Tevm provides additional actions beyond standard viem functionality to give you enhanced control over the EVM environment. #### Contract Interactions ```ts 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 ```ts 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 ```ts 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 }) ``` The `tevmDumpState` and `tevmLoadState` actions are more powerful than regular snapshots, as they capture the complete VM state, including the fork cache. ### Inside the Memory Client Understanding how a MemoryClient is constructed helps you grasp Tevm's architecture and integration capabilities. A MemoryClient is essentially a viem client with Tevm's functionality added. Here's how you could build one from scratch: ```ts // 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: 1. **EIP-1193 Compatibility Layer**: Tevm implements the standard Ethereum provider interface 2. **In-Memory EVM**: Tevm runs a complete Ethereum Virtual Machine locally 3. **Viem Integration**: Tevm extends viem's functionality with EVM-specific capabilities ### Complete Action Reference
Public Actions - Read blockchain state ##### Contract Interactions * [`call`](https://viem.sh/docs/actions/public/call) - Call a contract method without sending a transaction * [`readContract`](https://viem.sh/docs/contract/readContract) - Read a contract's constant/view method * [`simulateContract`](https://viem.sh/docs/contract/simulateContract) - Simulate a contract write without executing * [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas) - Estimate gas for a contract call * [`estimateGas`](https://viem.sh/docs/actions/public/estimateGas) - Estimate gas for a transaction * [`getBytecode`](https://viem.sh/docs/contract/getBytecode) - Get a contract's bytecode ##### Block & Transaction * [`getBlock`](https://viem.sh/docs/actions/public/getBlock) - Get a block by number or hash * [`getBlockNumber`](https://viem.sh/docs/actions/public/getBlockNumber) - Get the latest block number * [`getBlockTransactionCount`](https://viem.sh/docs/actions/public/getBlockTransactionCount) - Get the transaction count for a block * [`getTransaction`](https://viem.sh/docs/actions/public/getTransaction) - Get transaction details by hash * [`getTransactionCount`](https://viem.sh/docs/actions/public/getTransactionCount) - Get the transaction count (nonce) for an address * [`getTransactionReceipt`](https://viem.sh/docs/actions/public/getTransactionReceipt) - Get a transaction receipt by hash * [`waitForTransactionReceipt`](https://viem.sh/docs/actions/public/waitForTransactionReceipt) - Wait for a transaction to be mined ##### Account & Chain * [`getBalance`](https://viem.sh/docs/actions/public/getBalance) - Get the balance of an address * [`getChainId`](https://viem.sh/docs/actions/public/getChainId) - Get the chain ID * [`getGasPrice`](https://viem.sh/docs/actions/public/getGasPrice) - Get the current gas price * [`estimateFeesPerGas`](https://viem.sh/docs/actions/public/estimateFeesPerGas) - Estimate fees per gas unit * [`getStorageAt`](https://viem.sh/docs/actions/public/getStorageAt) - Get the value from a storage slot
Test Actions - Manipulate blockchain state ##### Block & Mining * [`mine`](https://viem.sh/docs/actions/test/mine) - Mine a number of blocks * [`setAutomine`](https://viem.sh/docs/actions/test/setAutomine) - Enable/disable automatic mining * [`setIntervalMining`](https://viem.sh/docs/actions/test/setIntervalMining) - Set mining to occur at intervals * [`setBlockGasLimit`](https://viem.sh/docs/actions/test/setBlockGasLimit) - Set the block gas limit * [`setBlockTimestampInterval`](https://viem.sh/docs/actions/test/setBlockTimestampInterval) - Set increment for timestamps * [`setNextBlockBaseFeePerGas`](https://viem.sh/docs/actions/test/setNextBlockBaseFeePerGas) - Set the base fee for the next block * [`setNextBlockTimestamp`](https://viem.sh/docs/actions/test/setNextBlockTimestamp) - Set the timestamp for the next block ##### Account & State * [`setBalance`](https://viem.sh/docs/actions/test/setBalance) - Set an address's balance * [`setCode`](https://viem.sh/docs/actions/test/setCode) - Set contract bytecode at an address * [`setNonce`](https://viem.sh/docs/actions/test/setNonce) - Set the nonce for an address * [`setStorageAt`](https://viem.sh/docs/actions/test/setStorageAt) - Set a storage slot's value * [`setCoinbase`](https://viem.sh/docs/actions/test/setCoinbase) - Set the block miner address * [`setMinGasPrice`](https://viem.sh/docs/actions/test/setMinGasPrice) - Set the minimum gas price ##### State Management * [`snapshot`](https://viem.sh/docs/actions/test/snapshot) - Create a snapshot of the current state * [`revert`](https://viem.sh/docs/actions/test/revert) - Revert to a previous snapshot * [`reset`](https://viem.sh/docs/actions/test/reset) - Reset the fork to a fresh state * [`dumpState`](https://viem.sh/docs/actions/test/dumpState) - Export the current state * [`loadState`](https://viem.sh/docs/actions/test/loadState) - Import a previously exported state
Wallet Actions - Send transactions and interact with accounts ##### Account Management * [`getAddresses`](https://viem.sh/docs/actions/wallet/getAddresses) - Get available addresses * [`requestAddresses`](https://viem.sh/docs/actions/wallet/requestAddresses) - Request permission to view addresses ##### Transaction Operations * [`prepareTransactionRequest`](https://viem.sh/docs/actions/wallet/prepareTransactionRequest) - Prepare a transaction * [`sendTransaction`](https://viem.sh/docs/actions/wallet/sendTransaction) - Send a transaction * [`sendRawTransaction`](https://viem.sh/docs/actions/wallet/sendRawTransaction) - Send a signed transaction * [`signTransaction`](https://viem.sh/docs/actions/wallet/signTransaction) - Sign a transaction ##### Signing Operations * [`signMessage`](https://viem.sh/docs/actions/wallet/signMessage) - Sign a message * [`signTypedData`](https://viem.sh/docs/actions/wallet/signTypedData) - Sign typed data (EIP-712) ##### Chain Management * [`addChain`](https://viem.sh/docs/actions/wallet/addChain) - Add a chain to the wallet * [`switchChain`](https://viem.sh/docs/actions/wallet/switchChain) - Switch to a different chain ##### Permissions & Assets * [`getPermissions`](https://viem.sh/docs/actions/wallet/getPermissions) - Get wallet permissions * [`requestPermissions`](https://viem.sh/docs/actions/wallet/requestPermissions) - Request wallet permissions * [`watchAsset`](https://viem.sh/docs/actions/wallet/watchAsset) - Add a token to the wallet
Tevm Actions - Enhanced EVM capabilities * `tevmCall` - Low-level EVM call * `tevmContract` - Call a contract method with detailed EVM info * `tevmDeploy` - Deploy a contract with detailed results * `tevmGetAccount` - Get detailed account information * `tevmSetAccount` - Set up a complex account state * `tevmDumpState` - Export complete EVM state * `tevmLoadState` - Import complete EVM state * `tevmMine` - Mine blocks with additional options
### Next Steps Learn how to integrate Tevm with ethers.js Create a local fork of mainnet for testing Set up a comprehensive local testing environment Explore the low-level node interface import { Callout, Steps, Button } from 'vocs/components' import { TabGroup, FileTree, Card } from '../../../components' ## Tevm Architecture Overview This page covers the internal architecture of Tevm and is intended for advanced users, contributors, or those looking to understand how Tevm works under the hood. If you're just getting started, we recommend the [viem API guide](../getting-started/viem) instead. Tevm's architecture is designed to be modular, extensible, and compatible with the broader JavaScript ecosystem. This guide explains the core components and how they work together to create a complete Ethereum execution environment. ### Design Philosophy: Objects and Actions At its core, Tevm follows a clear separation between **Objects** (stateful components) and **Actions** (pure functions that operate on those objects). This pattern, inspired by [viem](https://viem.sh/), enables tree-shaking and a composable API.

Stateful components that encapsulate and maintain data.

  • TevmNode - The core Node interface
  • Evm - The Ethereum Virtual Machine
  • StateManager - Manages blockchain state
  • Blockchain - Handles blocks and chain state

Pure functions that take an object as their first parameter and perform operations.

  • Tree-shakable for minimal bundle size
  • Single-purpose with clear input/output
  • Composable for complex operations
  • Can be imported individually
#### Example: Using an Action Here's how to use a tree-shakable action with a Tevm Node: ```ts showLineNumbers {1-2,5,8,11} filename="action-example.ts" import { createTevmNode } from 'tevm' import { getAccountHandler } from 'tevm/actions' // [!code focus] // 1. Create the node object const node = createTevmNode() // [!code focus] // 2. Create a handler function by passing the node to the action const getAccount = getAccountHandler(node) // [!code focus] // 3. Use the handler function with specific parameters const account = await getAccount({ // [!code focus] address: '0x1234567890123456789012345678901234567890' }) console.log(account.balance) // Access account properties ``` This pattern allows you to: * Import only the actions you need * Create specialized handler functions for specific objects * Follow a consistent interface across the library ### Client Options: Convenience vs. Tree Shaking Tevm offers two main approaches for using its functionality:
```ts showLineNumbers {1,4-8,11-13,16-19} filename="memory-client.ts" import { createMemoryClient, http } from 'tevm' // Create a client with all actions pre-attached const client = createMemoryClient({ fork: { transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY') } }) // Use standard viem actions const code = await client.getContractCode({ // [!code focus] address: '0x1234567890123456789012345678901234567890' // [!code focus] }) // [!code focus] // Use Tevm-specific actions (prefixed with 'tevm') const state = await client.tevmDumpState() // [!code focus] const balance = await client.getBalance({ // [!code focus] address: '0x1234567890123456789012345678901234567890' // [!code focus] }) // [!code focus] ``` The MemoryClient approach is great for quick prototyping and applications where bundle size isn't critical.
```ts showLineNumbers {1-4,7-12,15-16,19-22} filename="tree-shakable.ts" import { createClient, http } from 'viem' import { createTevmTransport } from 'tevm/transport' import { getBlock, getBlockNumber } from 'viem/actions' // [!code focus] import { tevmSetAccount, tevmMine } from 'tevm/actions' // [!code focus] // Create a client with just the transport const client = createClient({ transport: createTevmTransport({ fork: { transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY') } }) }) // Import and use only the specific actions you need const blockNumber = await getBlockNumber(client) // [!code focus] const block = await getBlock(client) // [!code focus] // Tevm-specific actions await tevmSetAccount(client, { // [!code focus] address: '0x1234...', // [!code focus] balance: 1000000000000000000n // [!code focus] }) // [!code focus] await tevmMine(client, { blocks: 1 }) // [!code focus] ``` This approach is ideal for production applications and browser environments where bundle size matters.
  • โœ… Easy to get started
  • โœ… All methods available immediately
  • โœ… Less code to write
  • โŒ Larger bundle size
  • โœ… Smallest possible bundle size
  • โœ… Only include what you use
  • โœ… Works with code-splitting
  • โŒ More verbose imports
For more details on tree-shakable actions, see the [viem documentation on tree-shaking](https://wagmi.sh/react/guides/viem). ### Core Architecture Components Tevm's modular architecture comprises several key components that work together to provide a complete Ethereum execution environment: :::steps #### Virtual Machine (EVM) The execution engine that runs EVM bytecode with full support for all opcodes and precompiles. ```ts // Access via const evm = (await node.getVm()).evm // Features evm.events.on('step', (data, next) => { // Monitor every EVM operation console.log(`Opcode: ${data.opcode.name}`) next() }) // Control execution gas limits, precompiles, etc. ``` #### State Manager Maintains account balances, contract code, and storage state with forking capability from live networks. ```ts // Access via const stateManager = (await node.getVm()).stateManager // Features await stateManager.putAccount(address, account) await stateManager.getAccount(address) await stateManager.getContractStorage(address, key) await stateManager.checkpoint() // Create state snapshot await stateManager.revert(checkpointId) // Revert to snapshot ``` Unlike traditional Ethereum clients, Tevm uses simple key-value stores for state storage rather than a Merkle Patricia Trie. This design choice improves performance and simplifies the implementation, but it means Tevm doesn't support generating state proofs for newly mined blocks. If you need state proof functionality for your use case, please join our [Telegram community](https://t.me/+ANThR9bHDLAwMjUx) and share your requirements. We're open to adding this feature based on community needs. #### Blockchain Manages blocks, chain state, and handles block production with various mining strategies. ```ts // Access via const blockchain = (await node.getVm()).blockchain // Features await blockchain.getBlock(blockHash) await blockchain.getBlockByNumber(blockNumber) await blockchain.putBlock(block) await blockchain.getLatestBlock() ``` #### Transaction Pool Manages pending transactions, orders them by gas price, and handles transaction validation. ```ts // Access via const txPool = await node.getTxPool() // Features await txPool.add(transaction) await txPool.getTransactions() const pendingTxs = txPool.getPendingTransactions() const pendingNonces = txPool.getPendingNonce(address) ``` ::: ### Use Cases & Capabilities Tevm enables several powerful use cases that were previously difficult or impossible with traditional Ethereum clients: ```ts title="Advanced Debugging" import { createTevmNode } from 'tevm' import { runCall } from 'tevm/vm' const node = createTevmNode() const vm = await node.getVm() // Add EVM execution hooks vm.evm.events.on('step', (data, next) => { console.log(`Executing ${data.opcode.name} at PC=${data.pc}`) console.log(`Stack: ${data.stack.join(', ')}`) next?.() }) // Execute contract call await runCall(vm)({ to: '0x1234567890123456789012345678901234567890', data: '0xabcdef12' // Call data }) ``` ```ts title="Cross-Environment" // Works in Node.js const nodejsClient = createMemoryClient() // Works in browsers const browserClient = createMemoryClient() // Works in serverless functions export default async function handler(req, res) { const client = createMemoryClient() const balance = await client.getBalance({ address: req.body.address }) return res.json({ balance }) } ``` ```ts title="Custom Tools" import { createMemoryClient } from 'tevm' export async function simulateTransaction(tx) { const client = createMemoryClient() // Take snapshot of current state const snapshot = await client.tevmSnapshot() try { // Execute transaction const receipt = await client.sendTransaction(tx) await client.mine() // Check transaction results const logs = receipt.logs const gasUsed = receipt.gasUsed const events = parseEvents(logs) return { success: true, gasUsed, events } } finally { // Always revert to original state await client.tevmRevert(snapshot) } } ``` #### Custom Tool Opportunities

Preview transaction outcomes before sending to mainnet

Step through transactions with full state visibility

Build apps that work offline with optimistic updates

Create interactive EVM learning experiences

Test smart contracts in continuous integration pipelines

Analyze contract gas usage patterns with precision

Run Ethereum nodes in serverless or edge computing environments

Create, save, and restore blockchain state at precise points

For detailed examples of these use cases, see the [examples section](../examples/viem.mdx). ### API Interfaces Tevm provides multiple API layers to suit different needs and programming styles.
| API Level | Description | Best For | | ------------------------ | ------------------------------------------------------------------ | ------------------------------------- | | **Viem Client API** | Standard viem actions plus Tevm-specific actions | Most application development | | **JSON-RPC API** | Standard Ethereum RPC methods plus Anvil and Tevm-specific methods | Direct RPC integration, tooling | | **TevmNode API** | Direct access to the node and its components | Advanced use cases, custom extensions | | **Low-Level Components** | Direct access to EVM, StateManager, Blockchain, etc. | Tool developers, deep customization |
```ts showLineNumbers {1,3,6-7,10-13} filename="client-api.ts" import { createMemoryClient } from 'tevm' const client = createMemoryClient() // Standard viem actions const balance = await client.getBalance({ address: '0x123...' }) // [!code focus] const blockNumber = await client.getBlockNumber() // [!code focus] // Tevm-specific actions await client.tevmSetAccount({ // [!code focus] address: '0x123...', // [!code focus] balance: 1000000000000000000n // [!code focus] }) // [!code focus] await client.tevmMine() ``` This is the most developer-friendly API, perfect for most application development.
```ts showLineNumbers {1-2,4,7-10,13-16,19-22} filename="json-rpc-api.ts" import { createTevmNode } from 'tevm' import { requestEip1193 } from 'tevm/decorators' const node = createTevmNode().extend(requestEip1193()) // Standard Ethereum JSON-RPC methods const balance = await node.request({ // [!code focus] method: 'eth_getBalance', // [!code focus] params: ['0x123...', 'latest'] // [!code focus] }) // [!code focus] // Anvil-compatible methods await node.request({ // [!code focus] method: 'anvil_setBalance', // [!code focus] params: ['0x123...', '0x10000000000000000'] // [!code focus] }) // [!code focus] // Tevm-specific methods const state = await node.request({ // [!code focus] method: 'tevm_dumpState', // [!code focus] params: [] // [!code focus] }) // [!code focus] ```
```ts showLineNumbers {1-2,4-5,8-12,15-22,25} filename="low-level-api.ts" import { createTevmNode } from 'tevm' import { createAddress } from 'tevm/address' const node = createTevmNode() const vm = await node.getVm() // Direct EVM access vm.evm.events.on('step', (data, next) => { // [!code focus] // Inspect execution at each EVM step // [!code focus] console.log(data.opcode.name, data.stack) // [!code focus] next?.() // [!code focus] }) // [!code focus] // Direct state management await vm.stateManager.putAccount( // [!code focus] createAddress('0x123...'), // [!code focus] { // [!code focus] nonce: 0n, // [!code focus] balance: 10000000000000000000n, // [!code focus] storageRoot: '0x...', // [!code focus] codeHash: '0x...' // [!code focus] } // [!code focus] ) // [!code focus] // Direct blockchain control const block = await vm.blockchain.getBlockByNumber(1n) ``` This API provides maximum control but requires deeper understanding of EVM internals.
For component API details, see: * [State Manager](/reference/state) * [Transaction Pool](/reference/txpool) * [Blockchain](/reference/blockchain) * [EVM](/reference/evm) * [Receipt Manager](/reference/receipt-manager) ### Advanced Features Tevm includes several powerful features that enable advanced use cases: :::steps #### Custom Precompiles Extend the EVM with JavaScript functions that can be called from smart contracts: ```ts import { createTevmNode, definePrecompile } from 'tevm' import { createContract } from 'tevm/contract' import { parseAbi } from 'tevm' // Define a precompile that provides an off-chain data oracle const dataOraclePrecompile = definePrecompile({ contract: createContract({ abi: parseAbi(['function getCurrentPrice(string symbol) view returns (uint256)']), address: '0x0000000000000000000000000000000000000123' }), call: async ({ data }) => { // Parse input data and fetch external price data const symbol = parseInputData(data) const price = await fetchPriceData(symbol) return { returnValue: encodePrice(price), executionGasUsed: 3000n } } }) // Create a node with the custom precompile const node = createTevmNode({ customPrecompiles: [dataOraclePrecompile.precompile()] }) ``` #### EVM Execution Hooks Instrument the EVM to observe and measure execution: ```ts import { createTevmNode } from 'tevm' const node = createTevmNode() const vm = await node.getVm() // Add detailed execution hooks vm.evm.events.on('step', (step, next) => { console.log(` Operation: ${step.opcode.name} PC: ${step.pc} Gas Used: ${step.gasLeft} Stack: ${step.stack.join(', ')} Memory: ${step.memory.slice(0, 64)}... `) next?.() // Continue execution }) ``` #### Forking Capabilities Create local forks of any EVM chain with lazy-loading: ```ts import { createTevmNode, http } from 'tevm' import { mainnet } from 'tevm/common' // Fork from Ethereum mainnet const node = createTevmNode({ fork: { transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'), common: mainnet, blockTag: 18000000n // Optional: specific block number } }) // State is loaded lazily - only accessed accounts are loaded // This allows working with huge state trees efficiently ``` #### Contract Utilities Type-safe contract interactions with TypeScript support: ```ts import { createContract } from 'tevm/contract' // Define a contract interface const erc20Contract = createContract({ abi: [ 'function balanceOf(address owner) view returns (uint256)', 'function transfer(address to, uint256 amount) returns (bool)', 'event Transfer(address indexed from, address indexed to, uint256 value)' ], address: '0x123...' }) // Type-safe read and write operations const balance = await erc20Contract.read.balanceOf('0x456...') const txHash = await erc20Contract.write.transfer('0x789...', 1000n) ``` ::: For Tevm Bundler users, directly import Solidity with full type safety: ```ts // Import Solidity directly (with tevm bundler plugins) import { ERC20 } from './ERC20.sol' // Contract with full TypeScript types const token = ERC20.withAddress('0x123...') // Safe contract interaction const decimals = await token.read.decimals() ``` ### Extensibility Model :::details[Node Extension API] Tevm's plugin system allows adding new functionality to nodes: ```ts import { createTevmNode } from 'tevm' // Create a node with extensions const node = createTevmNode().extend((baseNode) => { // Add custom methods return { async simulateBulkTransactions(txs) { const results = [] for (const tx of txs) { const vm = await baseNode.getVm() results.push(await vm.runTx({ tx })) } return results }, async resetToSnapshot(snapshot) { const vm = await baseNode.getVm() return vm.stateManager.revert(snapshot) } } }) // Use the extended functionality const snapshot = await node.getVm().stateManager.checkpoint() const results = await node.simulateBulkTransactions([tx1, tx2, tx3]) await node.resetToSnapshot(snapshot) ``` This extension model allows for powerful customizations while maintaining the core API. ::: ### Further Resources | Resource | Description | | ----------------------------------------------------------- | -------------------------------------------------- | | [TevmNode Interface Reference](/reference/node) | Detailed API reference for the core node interface | | [GitHub Repository](https://github.com/evmts/tevm-monorepo) | Source code and contributions | | [Custom Precompiles Guide](../advanced/custom-precompiles) | Learn how to extend the EVM | | [Performance Profiling](../advanced/performance-profiler) | Optimize your Ethereum applications | import { Callout, Steps, Button } from 'vocs/components' ## What is Tevm Node? :::tip[Ethereum in JavaScript] Tevm Node is a complete Ethereum execution environment implemented entirely in JavaScript, providing full EVM functionality without any native dependencies. It brings the entire Ethereum stack to any JavaScript environment. ::: ### Universal JavaScript Compatibility Tevm's key innovation is bringing the Ethereum execution environment to **every JavaScript runtime**:
Node.js
For local development, testing, and CI/CD pipelines
Browser
For advanced dApps with offline capability and real-time simulation
Any JS Runtime
Works in Deno, Bun, Edge Functions, or any modern JavaScript environment
### Core Features :::steps #### Network Forking Create a local sandbox with the state of any EVM-compatible network: ```ts import { createMemoryClient, http } from 'tevm' import { optimism } from 'tevm/chains' // Fork from Optimism mainnet const client = createMemoryClient({ fork: { transport: http('https://mainnet.optimism.io'), common: optimism } }) // Access any contract or account state from the forked network const balance = await client.getBalance({ address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' // vitalik.eth }) ``` Tevm's forking implementation uses: * **Lazy-loading** - Only loads the state you actually access * **Efficient caching** - Keeps accessed state in memory for fast subsequent access * **Latest EVM version** - Supports the most recent Ethereum upgrades #### Transaction Pool Management Complete control over transaction submission and processing: ```ts // Submit a transaction to the mempool const hash = await client.sendTransaction({ account: '0x...', to: '0x...', value: 1000000000000000000n // 1 ETH }) // View pending transactions const pool = await client.getTxPool() const pendingTxs = pool.getContent() // Mine blocks to process transactions await client.mine({ blocks: 1 }) ``` Tevm gives you full control over: * **Transaction priority** - Based on gas price, nonce, etc. * **Validation rules** - Customize how transactions are validated * **Processing timing** - Mine exactly when you want #### Flexible Mining Control Choose your block production model based on your needs: ```ts // Manual mining (default) await client.sendTransaction({ ... }) await client.mine({ blocks: 1 }) // Explicitly mine when ready // Auto-mining client.setMining({ mode: 'auto' }) // Mine on each transaction // Interval mining client.setMining({ mode: 'interval', interval: 5000 // Mine every 5 seconds }) ``` #### Advanced Extensibility Customize every aspect of the EVM environment: ```ts import { createTevmNode } from 'tevm' // Create a custom node with specialized components const node = await createTevmNode({ evm: { // Customize EVM execution enableRuntimeTransform: true, allowUnlimitedContractSize: true }, precompiles: [ // Add custom precompiles { address: '0x0000000000000000000000000000000000000123', execute: async (input, gas) => { // Custom logic return { executionGasUsed: 0n, returnValue: '0x123' } } } ] }) ``` ::: ### Integration With Popular Libraries Tevm works with the libraries you already know and love: :::code-group ```ts [viem] import { createMemoryClient, http } from 'tevm' const client = createMemoryClient() // Use standard viem actions const balance = await client.getBalance({ address: '0x...' }) const blockNumber = await client.getBlockNumber() // Plus Tevm-specific actions await client.tevmMine({ blocks: 1 }) await client.tevmSetAccount({ address: '0x...', balance: 100000000000000000n // 0.1 ETH }) ``` ```ts [ethers] import { createMemoryClient } from 'tevm' import { requestEip1193 } from 'tevm/decorators' import { BrowserProvider, Wallet } from 'ethers' // Create client and enable EIP-1193 provider interface const client = createMemoryClient() client.transport.tevm.extend(requestEip1193()) // Use with ethers.js const provider = new BrowserProvider(client.transport.tevm) const signer = Wallet.createRandom().connect(provider) // Read chain data const blockNumber = await provider.getBlockNumber() // Don't forget to mine after transactions await client.mine({ blocks: 1 }) ``` ::: ### How Tevm Compares | Feature | Tevm | Anvil | Hardhat | Ganache | | ------------------------- | ---------- | ------- | ------------------- | ---------- | | **Language** | JavaScript | Rust | JavaScript/Solidity | JavaScript | | **Browser Compatible** | โœ… | โŒ | โŒ | โŒ | | **Zero Dependencies** | โœ… | โŒ | โŒ | โŒ | | **Mainnet Forking** | โœ… | โœ… | โœ… | โœ… | | **EVM Event Hooks** | โœ… | โŒ | Partial | โŒ | | **Custom Precompiles** | โœ… | โœ… | โŒ | โŒ | | **viem Integration** | Native | Basic | Basic | Basic | | **ethers Integration** | Native | Basic | Basic | Basic | | **Debugging** | Advanced | Basic | Advanced | Basic | | **TypeScript Support** | Full | Limited | Full | Limited | | **Serverless Compatible** | โœ… | โŒ | โŒ | โŒ | ### Why Choose Tevm?

๐Ÿš€ Enhanced Performance

Execute transactions locally with near-zero latency for gas estimation, transaction simulation, and debugging.

๐Ÿ’ป Browser Compatibility

Enable sophisticated dApp features like offline capabilities, optimistic UI updates, and real-time simulations.

๐Ÿ” Debug Superpowers

Step through EVM execution opcode by opcode to understand exactly what's happening in your smart contracts.

๐Ÿ› ๏ธ Familiar Developer Experience

Works seamlessly with the libraries you already know - viem, ethers, or any EIP-1193 compatible tool.

:::info[Did you know?] Tevm Node is part of a larger ecosystem that also includes [Tevm Bundler](https://tevm.sh/bundler), which allows for direct Solidity imports into JavaScript/TypeScript. ::: ### Library Compatibility | Library | Support Level | Notes | | ---------------------------------------------- | ------------- | ----------------------------------------- | | [**viem**](../getting-started/viem.mdx) | First-class | Native integration with all viem features | | [**ethers.js**](../getting-started/ethers.mdx) | Full | Both v5 and v6 via EIP-1193 provider | | [**web3.js**](https://github.com/web3/web3.js) | Full | Via EIP-1193 provider | | [**wagmi**](https://wagmi.sh/) | Full | Works as a wagmi connector | | [**thirdweb**](https://thirdweb.com/) | Full | Compatible with thirdweb's SDK | | Any EIP-1193 library | Full | Standard provider interface | ### Next Steps
import { Callout, Steps, Button } from "vocs/components"; import { Card, TabGroup, FileTree } from "../../../components"; ## Why Run Ethereum in JavaScript? JavaScript has emerged as an ideal environment for running Ethereum, offering unique advantages for both developers and end-users. Here's why running an Ethereum node in JavaScript unlocks powerful new capabilities. ### Performance & Efficiency

Running the EVM locally eliminates round-trip delays to remote nodes, enabling near-instantaneous transaction simulations and gas estimations.

Simulate multiple transactions plugging directly into the evm with JS or even writing custom contracts in JS.

#### Real-World Performance Benefits Tevm's local execution provides instantanious gas estimation! ```typescript showLineNumbers {1-4,8-11} filename="performance-comparison.ts" const gasEstimate0 = await provider.estimateGas({ ... }) // ~200ms as it fetches state const gasEstimate0 = await provider.estimateGas({ ... }) // ~Instant on future estimations with cache saved const gasEstimate0 = await provider.estimateGas({ ... }) // ~Instant on future estimations with cache saved ``` ### Enhanced User Experiences JavaScript-based EVM execution enables entirely new categories of dApp features:

Enable local-first applications that work without constant network connectivity.

{" "}

Show users the likely outcome of transactions before they're mined on-chain.

{" "}

Reduce dependency on external infrastructure, making dApps more resilient.

{" "}

Simulate complex interactions and preview results before sending transactions.

Process sensitive data locally without sending it to external services.

Ship advanced UX faster and safer using Tevm bundler's typesafe Solidity imports, eliminating contract integration errors.

### Developer Experience Advantages #### Advanced Debugging ๐Ÿ”ฌ Step through EVM execution opcode by opcode, inspect memory and stack, and see exactly what happens in your contracts. ```typescript filename="debug-example.ts" // Use tevmContract for easy debugging with execution hooks const result = await client.tevmContract({ to: contractAddress, abi: contractAbi, functionName: 'myFunction', args: [param1, param2], // Listen to every EVM instruction onStep: (data, next) => { console.log( `${data.pc.toString().padStart(5)}:`, `${data.opcode.name.padEnd(10)}`, `gas: ${data.gasLeft.toString().padStart(8)}`, `stack: ${data.stack.join(', ')}` ) next() } }) ``` #### Deterministic Testing ๐Ÿงช Create fully reproducible environments for testing with complete control over blockchain state, time, and mining. ```typescript filename="testing-example.ts" // Create a snapshot before test const snapshotId = await client.tevmSnapshot() // Run the test await client.setBalance({ address: testAccount, value: parseEther("100") }) await client.sendTransaction({ ... }) await client.mine({ blocks: 1 }) const result = await client.call({ ... }) // Restore state after test await client.tevmRevert({ snapshotId }) ``` #### Portable Environment ๐Ÿ’ผ The same Ethereum environment works across Node.js, browsers, serverless functions, and other JavaScript runtimes.
```ts import { createMemoryClient } from 'tevm' // Create an in-browser Ethereum node const client = createMemoryClient() document.querySelector('#button').addEventListener('click', async () => { const balance = await client.getBalance({ address: '0x...' }) console.log(`Balance: ${formatEther(balance)} ETH`) }) ```
```ts import { createMemoryClient } from 'tevm' // Create a Node.js Ethereum node const client = createMemoryClient() async function main() { const balance = await client.getBalance({ address: '0x...' }) console.log(`Balance: ${formatEther(balance)} ETH`) } main().catch(console.error) ```
```ts import { createMemoryClient } from 'tevm' export async function handler(event) { // Create a serverless Ethereum node const client = createMemoryClient() const balance = await client.getBalance({ address: event.address }) return { statusCode: 200, body: JSON.stringify({ balance: balance.toString() }) } } ```
### Solidity Imports Tevm Bundler (optional feature) creates the best devx for working with solidity files in TypeScript ```typescript // Import solidity files directly into TS files import { MyContract } from "./MyContract.sol"; ``` The devx is optimized in following ways: * Natspec on hover * Typesafe contract * TRPC like experience. You will see red underlines before you even save a solidity file * No need to set up external build tools. Plugs directly into your existing js pipeline ### JavaScript Ecosystem Integration Running Ethereum in JavaScript means you can leverage the entire JavaScript ecosystem effortlessly.

Type-safe contract interactions with full IntelliSense support

{" "}

React, Vue, Svelte and other frontend libraries

{" "}

Vite, Webpack, ESBuild and other bundlers

{" "}

Vitest support via Vite

{" "}

Node.js, browsers, Electron, serverless functions

{" "}

Access to millions of packages and libraries in the npm registry

Integration with browser storage, WebSockets, service workers, and more

### Ready to Get Started?
## @tevm/actions The `@tevm/actions` package provides a comprehensive set of actions for interacting with the Tevm client. It includes both standard Ethereum JSON-RPC methods and Tevm-specific functionality. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/actions/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs) folder. ### Installation ```bash npm install @tevm/actions ``` ### Overview The `@tevm/actions` package provides handlers for: * Executing EVM calls and contract interactions * Managing blockchain state and accounts * Standard Ethereum JSON-RPC methods * Testing and development utilities (Anvil-compatible) * Debugging and tracing functionality ### API Reference #### Error Classes * [BlobGasLimitExceededError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/BlobGasLimitExceededError.md) - Error thrown when blob gas limit is exceeded * [MissingAccountError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/MissingAccountError.md) - Error thrown when an account doesn't exist * [NoForkUrlSetError](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/classes/NoForkUrlSetError.md) - Error thrown when fork URL is required but not set #### Core Types * [Address](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Address.md) - Ethereum address type * [Abi](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Abi.md) - Contract ABI type * [Block](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/Block.md) - Ethereum block type * [BlockTag](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/BlockTag.md) - Block reference tag (latest, earliest, etc) #### Base Actions ##### Call Actions * [CallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallHandler.md) - Handler for executing EVM calls * [CallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallParams.md) - Parameters for call operations * [CallResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/CallResult.md) - Result of call execution * [BaseCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/BaseCallParams.md) - Common parameters for all call operations ##### Contract Actions * [ContractHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractHandler.md) - Handler for contract interactions * [ContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractParams.md) - Parameters for contract calls * [ContractResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/ContractResult.md) - Result of contract execution ##### Deploy Actions * [DeployHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployHandler.md) - Handler for contract deployment * [DeployParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployParams.md) - Parameters for deployment * [DeployResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DeployResult.md) - Result of deployment #### Validation Functions * [validateBaseCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateBaseCallParams.md) - Validate base call parameters * [validateCallParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateCallParams.md) - Validate call parameters * [validateContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateContractParams.md) - Validate contract parameters * [validateGetAccountParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateGetAccountParams.md) - Validate get account parameters * [validateLoadStateParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateLoadStateParams.md) - Validate load state parameters * [validateMineParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateMineParams.md) - Validate mine parameters * [validateSetAccountParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/validateSetAccountParams.md) - Validate set account parameters #### JSON-RPC Procedures * [anvilImpersonateAccountJsonRpcProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/anvilImpersonateAccountJsonRpcProcedure.md) - Impersonate account procedure * [callProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/callProcedure.md) - Call procedure * [getAccountProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/getAccountProcedure.md) - Get account procedure * [mineProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/mineProcedure.md) - Mine procedure * [requestProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/requestProcedure.md) - Request procedure * [requestBulkProcedure](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/requestBulkProcedure.md) - Bulk request procedure #### Internal Utilities * [forkAndCacheBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/forkAndCacheBlock.md) - Fork and cache block utility * [handlePendingTransactionsWarning](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/handlePendingTransactionsWarning.md) - Handle pending transactions warning * [shouldCreateTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/functions/shouldCreateTransaction.md) - Check if transaction should be created #### Ethereum JSON-RPC Actions ##### Account & Network * [EthAccountsHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthAccountsHandler.md) - List available accounts * [EthChainIdHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthChainIdHandler.md) - Get current chain ID * [EthCoinbaseHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthCoinbaseHandler.md) - Get coinbase address * [EthGasPriceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGasPriceHandler.md) - Get current gas price * [EthBlockNumberHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthBlockNumberHandler.md) - Get current block number ##### State Reading * [EthGetBalanceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBalanceHandler.md) - Get account balance * [EthGetCodeHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetCodeHandler.md) - Get contract code * [EthGetStorageAtHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetStorageAtHandler.md) - Get storage at position * [EthCallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthCallHandler.md) - Execute call without state changes ##### Block Operations * [EthGetBlockByHashHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBlockByHashHandler.md) - Get block by hash * [EthGetBlockByNumberHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/EthGetBlockByNumberHandler.md) - Get block by number #### Anvil (Testing & Development) Actions ##### State Manipulation * [AnvilSetBalanceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetBalanceHandler.md) - Set account balance * [AnvilSetCodeHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetCodeHandler.md) - Set contract code * [AnvilSetNonceHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetNonceHandler.md) - Set account nonce * [AnvilSetStorageAtHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetStorageAtHandler.md) - Set storage at position ##### Mining & Chain Control * [AnvilMineHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilMineHandler.md) - Mine blocks * [AnvilSetChainIdHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilSetChainIdHandler.md) - Set chain ID * [AnvilResetHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/AnvilResetHandler.md) - Reset to initial state #### Debug Actions * [DebugTraceCallHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DebugTraceCallHandler.md) - Trace a call execution * [DebugTraceTransactionHandler](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/type-aliases/DebugTraceTransactionHandler.md) - Trace a transaction execution ### Usage Examples #### Basic Call Example ```typescript import { createTevmNode } from 'tevm/node' import { callHandler } from '@tevm/actions' const client = createTevmNode() const call = callHandler(client) const result = await call({ to: '0x123...', data: '0x456...', value: 1000n }) ``` #### Contract Interaction Example ```typescript import { contractHandler } from '@tevm/actions' const contract = contractHandler(client) const result = await contract({ to: '0x123...', abi: [...], function: 'transfer', args: ['0x456...', 1000n] }) ``` #### Deployment Example ```typescript import { deployHandler } from '@tevm/actions' const deploy = deployHandler(client) const result = await deploy({ bytecode: '0x...', abi: [...], args: ['constructor arg'] }) ``` #### JSON-RPC Example ```typescript import { ethCallHandler } from '@tevm/actions' const ethCall = ethCallHandler(client) const result = await ethCall({ to: '0x123...', data: '0x456...' }) ``` ### Error Handling All actions support a `throwOnFail` parameter to control error handling: ```typescript const result = await call({ to: '0x123...', throwOnFail: false // Return errors in result instead of throwing }) ``` ### See Also * [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) * [Anvil Documentation](https://book.getfoundry.sh/reference/anvil/) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/actions/docs/globals.md) ## @tevm/address The `@tevm/address` package provides utilities for handling Ethereum addresses. It wraps the functionality from [`@ethereumjs/utils` address](https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/util/src/address.ts) with a Tevm-style API. ### Installation ```bash npm install @tevm/address ``` ### Overview This package provides a set of utilities for working with Ethereum addresses, including: * Creating and validating addresses * Generating contract addresses (both regular and CREATE2) * Converting between different address formats * Handling checksummed addresses ### API Reference #### Classes ##### `Address` A utility class for Ethereum addresses that extends `EthjsAddress`. It provides a more user-friendly API and ensures checksummed address output. ```typescript import { createAddress } from '@tevm/address' // Create from hex string const address = createAddress(`0x${'00'.repeat(20)}`) // Create from number or bigint const address2 = createAddress(0n) // Create from bytes const address3 = createAddress(new Uint8Array(20)) // Create from non-hex string const address4 = createAddress('55'.repeat(20)) ``` **Methods:** * `toString()`: Returns the checksummed address as a string * `toBytes()`: Returns the address as a Uint8Array * `equals(address: Address)`: Checks if two addresses are equal * `isZero()`: Checks if the address is zero * `isPrecompileOrSystemAddress()`: Checks if the address is a precompile or system address #### Functions ##### `createAddress(address: string | number | bigint | Address | Uint8Array): Address` Creates an `Address` instance from various input types. ```typescript import { createAddress } from '@tevm/address' // From hex string const address = createAddress('0x1234...') // From number const address2 = createAddress(123) // From bytes const address3 = createAddress(new Uint8Array(20)) ``` **Parameters:** * `address`: The input to create an address from (hex string, number, bigint, Address instance, or Uint8Array) **Returns:** * An `Address` instance **Throws:** * `InvalidAddressError` if the input is not a valid address ##### `createContractAddress(from: Address, nonce: bigint): Address` Generates an address for a newly created contract using the standard CREATE operation. ```typescript import { createAddress, createContractAddress } from '@tevm/address' const from = createAddress('0x1234...') const nonce = 0n const contractAddress = createContractAddress(from, nonce) ``` **Parameters:** * `from`: The address of the account creating the contract * `nonce`: The nonce of the account creating the contract **Returns:** * The generated contract address **Throws:** * `InvalidAddressError` if the 'from' parameter is not a valid address ##### `create2ContractAddress(from: Address, salt: string, code: string): Address` Generates an address for a contract created using CREATE2 (EIP-1014). ```typescript import { createAddress, create2ContractAddress } from '@tevm/address' const from = createAddress('0x1234...') const salt = `0x${'00'.repeat(32)}` const code = '0x...' // Contract creation code const contractAddress = create2ContractAddress(from, salt, code) ``` **Parameters:** * `from`: The address creating the contract * `salt`: A 32-byte salt value as a hex string * `code`: The contract creation code **Returns:** * The generated contract address **Throws:** * `InvalidSaltError` if the salt is not 32 bytes * `InvalidAddressError` if inputs are invalid ### Error Handling The package provides specific error types for different validation scenarios: * `InvalidAddressError`: Thrown when an invalid address is provided * `InvalidSaltError`: Thrown when an invalid salt is provided for CREATE2 * `Create2ContractAddressError`: Union type of both error types above ### Examples #### Basic Address Creation and Validation ```typescript import { createAddress } from '@tevm/address' // Create an address const address = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e') // Get checksummed string representation console.log(address.toString()) // '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' // Check if it's a zero address console.log(address.isZero()) // false // Compare addresses const address2 = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e') console.log(address.equals(address2)) // true ``` #### Contract Address Generation ```typescript import { createAddress, createContractAddress, create2ContractAddress } from '@tevm/address' // Generate regular contract address const from = createAddress('0x742d35Cc6634C0532925a3b844Bc454e4438f44e') const nonce = 1n const contractAddress = createContractAddress(from, nonce) // Generate CREATE2 contract address const salt = `0x${'00'.repeat(32)}` const code = '0x608060405234801561001057600080fd5b506101...' // Contract bytecode const create2Address = create2ContractAddress(from, salt, code) ``` ### See Also * [Ethereum Address Format (EIP-55)](https://eips.ethereum.org/EIPS/eip-55) * [CREATE2 Opcode (EIP-1014)](https://eips.ethereum.org/EIPS/eip-1014) * [@tevm/utils Documentation](https://tevm.sh/reference/tevm/utils/) ## @tevm/block The `@tevm/block` package provides functionality for working with Ethereum blocks. It includes classes and utilities for handling block headers, block data, and block-related operations. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/block/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs) folder. ### Installation ```bash npm install @tevm/block ``` ### Overview The `@tevm/block` package provides: * Block creation and manipulation * Block header management * RLP serialization/deserialization * JSON-RPC block formatting * Verkle tree support ### API Reference #### Core Classes * [Block](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/Block.md) - Main class for Ethereum block operations * [BlockHeader](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/BlockHeader.md) - Class for managing block headers * [ClRequest](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/classes/ClRequest.md) - Class for client requests #### Interfaces * [BlockData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/BlockData.md) - Block data structure * [BlockOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/BlockOptions.md) - Options for block creation * [HeaderData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/HeaderData.md) - Block header data structure * [JsonBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonBlock.md) - JSON representation of a block * [JsonHeader](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonHeader.md) - JSON representation of a header * [JsonRpcBlock](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/JsonRpcBlock.md) - JSON-RPC block format #### Verkle Tree Types * [VerkleExecutionWitness](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleExecutionWitness.md) - Verkle execution witness * [VerkleProof](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleProof.md) - Verkle proof structure * [VerkleStateDiff](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/interfaces/VerkleStateDiff.md) - Verkle state difference #### Block Types * [BlockBodyBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockBodyBytes.md) - Block body byte representation * [BlockBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockBytes.md) - Full block byte representation * [BlockHeaderBytes](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BlockHeaderBytes.md) - Block header byte representation * [ExecutionPayload](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/ExecutionPayload.md) - Execution payload structure * [BeaconPayloadJson](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/type-aliases/BeaconPayloadJson.md) - Beacon chain payload JSON #### Utility Functions * [blockFromRpc](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/blockFromRpc.md) - Create block from RPC response * [executionPayloadFromBeaconPayload](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/executionPayloadFromBeaconPayload.md) - Convert beacon payload to execution payload * [getDifficulty](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/getDifficulty.md) - Calculate block difficulty * [valuesArrayToHeaderData](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/functions/valuesArrayToHeaderData.md) - Convert array to header data ### Usage Examples #### Creating a New Block ```typescript import { Block } from '@tevm/block' import { createCommon } from '@tevm/common' import { mainnet } from 'viem/chains' // Create a new block const block = new Block({ common: createCommon({ ...mainnet }) }) ``` #### Creating a Block from Data ```typescript import { Block } from '@tevm/block' import { createCommon } from '@tevm/common' import { mainnet } from 'viem/chains' import { EthjsAddress } from '@tevm/utils' const common = createCommon({ ...mainnet }) const blockData = { header: { parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000', uncleHash: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', coinbase: EthjsAddress.fromString('0x0000000000000000000000000000000000000000'), stateRoot: '0x0000000000000000000000000000000000000000000000000000000000000000', transactionsTrie: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', receiptTrie: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', difficulty: 0n, number: 0n, gasLimit: 30000000n, gasUsed: 0n, timestamp: BigInt(Math.floor(Date.now() / 1000)), extraData: '0x', mixHash: '0x0000000000000000000000000000000000000000000000000000000000000000', nonce: '0x0000000000000000', baseFeePerGas: 1000000000n } } const block = Block.fromBlockData(blockData, { common }) ``` #### Working with Block Headers ```typescript // Get block hash const hash = block.hash() // Serialize block const serialized = block.serialize() // Convert to JSON const json = block.toJSON() ``` #### Creating from RLP Data ```typescript import { Block } from '@tevm/block' import { createCommon } from '@tevm/common' import { mainnet } from 'viem/chains' const common = createCommon({ ...mainnet }) const serializedBlock = new Uint8Array([/* ... */]) const block = Block.fromRLPSerializedBlock(serializedBlock, { common }) ``` ### See Also * [Ethereum JSON-RPC Specification](https://ethereum.org/en/developers/docs/apis/json-rpc/) * [Block Structure](https://ethereum.org/en/developers/docs/blocks/) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/block/docs/globals.md) ## @tevm/blockchain The `@tevm/blockchain` package provides a custom implementation of the Ethereum blockchain, extending the functionality of `ethereumjs/blockchain`. It's responsible for managing blocks, handling chain reorganizations, and maintaining the blockchain state. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/blockchain/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/blockchain/docs) folder. ### Installation ```bash npm install @tevm/blockchain ``` ### Overview The blockchain package provides: * Block management and validation * Chain reorganization handling * Support for forking from live networks * Block iteration and traversal * Chain state management ### API Reference #### Core Types ##### `Chain` The main blockchain interface that provides methods for interacting with the blockchain. ```typescript import { createChain } from '@tevm/blockchain' import { createCommon } from '@tevm/common' const chain = await createChain({ common: createCommon({ /* chain config */ }), }) ``` **Methods:** * `putBlock(block: Block)`: Adds a block to the blockchain * `delBlock(blockHash: Uint8Array)`: Deletes a block and its children * `getBlock(blockId: number | bigint | Uint8Array)`: Gets a block by hash or number * `getBlockByTag(blockTag: BlockTag)`: Gets a block by tag (latest, earliest, etc.) * `getCanonicalHeadBlock()`: Gets the latest block in the canonical chain * `validateHeader(header: BlockHeader)`: Validates a block header * `iterator(name: string, onBlock: OnBlock)`: Iterates through blocks * `deepCopy()`: Creates a deep copy of the blockchain * `shallowCopy()`: Creates a shallow copy sharing state with original ##### `ChainOptions` Configuration options for creating a blockchain instance. ```typescript type ChainOptions = { common: Common // Chain configuration loggingLevel?: LogLevel // Logging configuration genesisBlock?: Block // Custom genesis block genesisStateRoot?: Uint8Array // Custom genesis state fork?: { // Fork configuration transport: { request: EIP1193RequestFn } blockTag?: BlockTag | bigint | `0x${string}` } } ``` #### Functions ##### `createChain(options: ChainOptions): Promise` Creates a new blockchain instance. ```typescript import { createChain } from '@tevm/blockchain' import { createCommon } from '@tevm/common' import { mainnet } from '@tevm/common' const chain = await createChain({ common: createCommon({ ...mainnet }), loggingLevel: 'debug', fork: { transport: { request: /* EIP-1193 provider */ }, blockTag: 'latest' } }) ``` ##### `createBaseChain(options: ChainOptions): BaseChain` Creates the internal blockchain implementation used by `createChain`. #### Block Operations ##### Getting Blocks ```typescript // Get by number const block = await chain.getBlock(1234n) // Get by hash const block = await chain.getBlock(blockHash) // Get by tag const latest = await chain.getBlockByTag('latest') const pending = await chain.getBlockByTag('pending') ``` ##### Adding & Removing Blocks ```typescript // Add a block await chain.putBlock(block) // Delete a block and its children await chain.delBlock(blockHash) ``` ##### Block Validation ```typescript // Validate a block header await chain.validateHeader(header) ``` #### Chain Traversal ##### Block Iterator ```typescript // Iterate through blocks await chain.iterator('vm', async (block, reorg) => { // Process each block console.log(block.header.number) }, maxBlocks) ``` ##### Iterator Head Management ```typescript // Get iterator head const head = await chain.getIteratorHead('vm') // Set iterator head await chain.setIteratorHead('vm', blockHash) ``` #### Forking The blockchain can be forked from a live network: ```typescript import { createChain } from '@tevm/blockchain' import { http } from 'viem' const chain = await createChain({ common: createCommon({ /* chain config */ }), fork: { transport: { request: http('https://mainnet.infura.io/v3/YOUR-KEY') }, blockTag: 'latest' // or specific block number/hash } }) ``` #### Error Handling The package throws specific error types for different scenarios: * `BlockNotFoundError`: When a requested block doesn't exist * `InvalidBlockError`: When block validation fails * `InvalidHeaderError`: When header validation fails * `InvalidChainError`: When chain configuration is invalid ### Examples #### Basic Chain Management ```typescript import { createChain } from '@tevm/blockchain' import { createCommon } from '@tevm/common' import { Block } from '@tevm/block' // Create chain const chain = await createChain({ common: createCommon({ /* chain config */ }) }) // Add blocks await chain.putBlock(block1) await chain.putBlock(block2) // Get latest block const head = await chain.getCanonicalHeadBlock() // Validate headers await chain.validateHeader(newBlock.header) ``` #### Chain Forking ```typescript import { createChain } from '@tevm/blockchain' import { http } from 'viem' const chain = await createChain({ common: createCommon({ /* chain config */ }), fork: { transport: { request: http('https://mainnet.infura.io/v3/YOUR-KEY') }, blockTag: 15000000n // Fork from specific block } }) // Work with forked chain const block = await chain.getBlock(15000000n) ``` #### Chain Iteration ```typescript import { createChain } from '@tevm/blockchain' const chain = await createChain({ /* options */ }) // Process blocks sequentially await chain.iterator('vm', async (block, reorg) => { if (reorg) { console.log('Chain reorganization detected') } // Process block console.log(`Processing block ${block.header.number}`) }, 1000) // Process up to 1000 blocks ``` ### See Also * [EthereumJS Blockchain Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/blockchain) * [Block Package Documentation](https://tevm.sh/reference/tevm/block/) * [Common Package Documentation](https://tevm.sh/reference/tevm/common/) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/blockchain/docs/globals.md) ## Bundler Plugins Tevm provides optional integration with popular JavaScript bundlers through a suite of plugins. These plugins enable direct importing of Solidity files into your TypeScript/JavaScript code, automatically compiling those contracts into type-safe Tevm contract instances. :::note Tevm Node is the primary product. Using the bundler is optional. You can also generate contract types with `npx tevm gen` (see [Codegen Approach](#codegen-approach)). ::: ### Available Plugins Tevm provides plugins for most popular JavaScript bundlers: | Bundler | Plugin Import Path | Repository | | ------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | | Vite | `tevm/bundler/vite-plugin` | [@tevm/vite-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/vite) | | Webpack | `tevm/bundler/webpack-plugin` | [@tevm/webpack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/webpack) | | Rollup | `tevm/bundler/rollup-plugin` | [@tevm/rollup-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rollup) | | ESBuild | `tevm/bundler/esbuild-plugin` | [@tevm/esbuild-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/esbuild) | | Bun | `tevm/bundler/bun-plugin` | [@tevm/bun-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/bun) | | Rspack | `tevm/bundler/rspack-plugin` | [@tevm/rspack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rspack) | All plugins share a similar usage pattern and a single configuration interface. ### Prerequisites & Key Points #### Optional Bundler You can compile contracts manually or use `npx tevm gen`. The plugins are purely optional if you prefer a different workflow. #### TypeScript Integration For best results, add the Tevm TS plugin to your tsconfig.json. This allows your editor to resolve Solidity imports, provide NatSpec docs on hover, go-to-definition on solidity methods, and more. #### .s.sol for Bytecode By default, Tevm only generates bytecode for Solidity files ending in `.s.sol`. Regular `.sol` files still compile for ABIs, but omit deployable bytecode. This helps differentiate purely interface-like contracts from ones you intend to deploy or call as scripts. #### Tevm Cache Compiled artifacts and metadata are stored in a `.tevm` folder. It is strongly recommended to `.gitignore` this directory. #### Foundry/Remappings If you have a Foundry setup or custom remappings, you can enable them in a `tevm.config.json`. #### Next.js Next.js can conflict with the TS plugin's type-checking. If you wish to keep type-checking turned on for Next.js, consider the [Codegen Approach](#codegen-approach). Otherwise, you may disable type-checking in your Next config. ### Plugin Configuration All Tevm bundler plugins accept a similar configuration object: ```ts type PluginOptions = { // (Optional) solc compiler version solc?: '0.8.19' | '0.8.20' | /* other supported solc versions */ } ``` For more complex, global settings (e.g., Foundry integration, custom libs, or remappings), place them in `tevm.config.json` (see [Configuration with tevm.config.json](#configuration-with-tevmconfigjson)). ### Bundler-Specific Setup #### 1. Vite Example ```ts // vite.config.ts import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { vitePluginTevm } from 'tevm/bundler/vite-plugin' export default defineConfig({ plugins: [ react(), vitePluginTevm({ solc: '0.8.20', // optional }), ], }) ``` #### 2. Webpack Example ```js // webpack.config.js const { webpackPluginTevm } = require('tevm/bundler/webpack-plugin') module.exports = { // other config... plugins: [ webpackPluginTevm({ solc: '0.8.20', // optional }), ], } ``` #### 3. Rollup Example ```js // rollup.config.js import { rollupPluginTevm } from 'tevm/bundler/rollup-plugin' export default { // other Rollup options... plugins: [ rollupPluginTevm({ solc: '0.8.20', }), ], } ``` #### 4. ESBuild Example ```js // build.js const { build } = require('esbuild') const { esbuildPluginTevm } = require('tevm/bundler/esbuild-plugin') build({ entryPoints: ['src/index.js'], outdir: 'dist', bundle: true, plugins: [ esbuildPluginTevm({ solc: '0.8.20', }), ], }) ``` #### 5. Bun Example ```ts // plugins.ts import { plugin } from 'bun' import { tevmBunPlugin } from 'tevm/bundler/bun-plugin' plugin(tevmBunPlugin({ solc: '0.8.20', })) ``` ```toml # bunfig.toml preload = ["./plugins.ts"] [test] preload = ["./plugins.ts"] ``` #### 6. Rspack Example ```js // rspack.config.js const { rspackPluginTevm } = require('tevm/bundler/rspack-plugin') module.exports = { // other Rspack config... plugins: [ rspackPluginTevm({ solc: '0.8.20', }), ], } ``` ### TypeScript Plugin Alongside the bundler plugin, configure your TypeScript environment to benefit from editor-level Solidity integration (NatSpec hovers, jump-to-definition, type-checking, etc.): ```json { "compilerOptions": { "plugins": [ { "name": "@tevm/ts-plugin" } ] // ...rest of your TS options } } ``` **VSCode**: Make sure you use the "workspace version" of TypeScript to see accurate IntelliSense. ### How the Bundler Works All Tevm bundler plugins share a unified core (`@tevm/base-bundler`): #### Import Detection & Resolution * Scans for `.sol` imports; merges in tsconfig.json paths, foundry remappings, and your tevm.config.json. #### Compilation * Uses solc to compile the full dependency graph. * If the file ends in `.s.sol`, bytecode is generated; otherwise only ABI is produced. #### Code Generation * Creates a TypeScript module that exports a Tevm Contract instance (with `.read` and `.write` methods, abi, and optionally bytecode). #### Caching * Stores results in `.tevm` to avoid unnecessary recompilation. Consider ignoring `.tevm` in your `.gitignore`. #### LSP & TS Plugin * The `@tevm/ts-plugin` references bundler outputs, enabling advanced IDE features like contract-level auto-completion, hover docs, and go-to-definition directly into Solidity code. ### Configuration with tevm.config.json Create a `tevm.config.json` in your project root for advanced bundler behaviors: ```json { "foundryProject": true, "libs": ["./lib", "./node_modules"], "remappings": { "foo": "vendored/foo" }, "debug": false, "cacheDir": ".tevm", "jsonAsConst": ["**/*.abi.json"] } ``` | Option | Type | Description | | ---------------- | ------------------------ | ---------------------------------------------------------------- | | `foundryProject` | `boolean \| string` | Enable Foundry integration (`true`) or path to foundry.toml | | `libs` | `string[]` | Library paths for Solidity imports (used alongside Foundry libs) | | `remappings` | `Record` | Custom import remappings | | `debug` | `boolean` | Output extra debug logs and files in `.tevm` | | `cacheDir` | `string` | Location for compiled artifacts (default: `.tevm`) | | `jsonAsConst` | `string \| string[]` | Glob patterns for JSON files to be typed as const in TS | ### Codegen Approach If you prefer not to rely on bundler-based transformations (for instance, if you're using Next.js with strict type-checking enabled), Tevm also supports an offline code generation approach: ```bash npx tevm gen ``` * Generates `.ts` files next to each `.sol` file (or wherever configured). * You can commit these `.ts` files to source control. * The result is effectively the same as bundler output but doesn't require hooking into your build process. This is especially helpful when: * You have a framework that tightly controls its build pipeline (e.g., Next.js with enforced type-checking). * You prefer explicit, committed TypeScript artifacts for contracts. * You want a stable CI pipeline or want to avoid runtime resolution. ### Troubleshooting #### 1. Missing or Red Underlines in Editor * Confirm you have `"plugins": [{ "name": "@tevm/ts-plugin" }]` in tsconfig.json. * In VSCode, switch to the "workspace version" of TypeScript. #### 2. Type-check Errors with Next.js * Next's built-in type checker might not handle dynamic `.sol` imports well. * Either disable type-checking in next.config.mjs or use [Codegen Approach](#codegen-approach). #### 3. "File Not Found" Errors * Check that your libraries or local imports are accounted for in libs or remappings. * If you're using Foundry, ensure `foundryProject: true` is set in tevm.config.json. #### 4. Cache Stale Issues * If a contract's changes don't appear, remove the `.tevm` folder and rebuild. ### Examples and Further Reading * [Examples in Monorepo](https://github.com/evmts/tevm-monorepo/tree/main/examples): Vite, Next, Bun, ESBuild * [Tevm Contract Reference](/reference/contract): Usage docs for contract reads, writes, events * [Wagmi + Tevm](https://github.com/evmts/tevm-monorepo/tree/main/examples): See WagmiReads.tsx and WagmiWrites.tsx examples for real usage * [Inline Solidity (Coming Soon)](#): ` sol` \`\` inline code snippet approach * [Globals.md](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/base-bundler/docs/globals.md): Core reference for bundler internals :::tip Remember: Tevm's bundler is optionalโ€”it's there to make importing Solidity as straightforward as possible. If your project setup or environment constraints make the bundler difficult, consider the `tevm gen` codegen workflow or a purely manual approach to contract artifacts. ::: ## Tevm Contract Bundler The Tevm Contract Bundler allows you to import Solidity files directly into your TypeScript code, enabling a seamless development experience with type safety and IDE integration. :::note This page has been split into smaller, more manageable sections. Please visit the new [Bundler Reference](/reference/bundler/) for the updated documentation. ::: ### Quick Navigation The bundler documentation is now split into several sections for better organization: * **[Overview](/reference/bundler/overview)** - Introduction, key benefits, available plugins * **[Internals](/reference/bundler/internals)** - How the bundler works under the hood * **[Methods & Exports](/reference/bundler/methods)** - Key APIs for advanced usage * **[Troubleshooting](/reference/bundler/troubleshooting)** - Common issues and solutions ### What is the Tevm Bundler? The Tevm bundler transforms Solidity `.sol` files into TypeScript modules at build time. When you import a contract, the bundler: 1. Reads and compiles the Solidity source code 2. Extracts the ABI, bytecode, and other contract information 3. Generates a TypeScript module with a [Tevm Contract](/reference/contract) instance This means you can interact with your contracts in a fully type-safe way, with editor features like auto-completion, go-to-definition, and inline documentation. ### Getting Started For a quick introduction to using the bundler, see the [Bundler Quickstart](/getting-started/bundler) guide. If you're looking for detailed reference information, explore the sections linked above. ### Coming Soon Features Tevm is actively developing new bundler features: * **Inline Solidity with `sol` Tag** - Define contracts directly in your JS/TS using template literals * **CAIP-10 Contract Imports** - Import contracts from any chain using standardized identifiers Learn more about these upcoming features in the [Upcoming Features](/reference/bundler/overview#coming-soon-features) section. ## @tevm/common The `@tevm/common` package provides chain-specific configuration and utilities for Tevm clients. It extends the functionality of `ethereumjs/common` and integrates with Viem's chain definitions. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/common/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs) folder. ### Installation ```bash npm install @tevm/common ``` ### Overview The `@tevm/common` package provides: * Chain configuration management * Hardfork settings and EIP support * Consensus algorithm configuration * Network-specific settings * Extensive chain definitions ### API Reference #### Enumerations * [ConsensusAlgorithm](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/enumerations/ConsensusAlgorithm.md) - Supported consensus algorithms * [ConsensusType](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/enumerations/ConsensusType.md) - Types of consensus mechanisms #### Interfaces * [CustomCrypto](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/CustomCrypto.md) - Custom cryptographic implementations * [EvmStateManagerInterface](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/EvmStateManagerInterface.md) - EVM state manager interface * [StorageDump](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/StorageDump.md) - Storage dump format * [StorageRange](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/interfaces/StorageRange.md) - Storage range specification #### Type Aliases * [AccountFields](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/AccountFields.md) - Account field definitions * [CliqueConfig](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/CliqueConfig.md) - Clique consensus configuration * [Common](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/Common.md) - Main chain configuration type * [CommonOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/CommonOptions.md) - Configuration options * [Hardfork](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/Hardfork.md) - Supported hardforks * [MockKzg](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/type-aliases/MockKzg.md) - Mock KZG implementation #### Functions * [createCommon](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/functions/createCommon.md) - Create a new Common instance * [createMockKzg](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/functions/createMockKzg.md) - Create a mock KZG implementation #### Supported Networks The package includes configurations for numerous networks, including: ##### Layer 1 Networks * [mainnet](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/mainnet.md) - Ethereum Mainnet * [sepolia](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/sepolia.md) - Sepolia Testnet * [goerli](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/goerli.md) - Goerli Testnet * [holesky](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/holesky.md) - Holesky Testnet ##### Layer 2 Networks * [arbitrum](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/arbitrum.md) - Arbitrum One * [optimism](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/optimism.md) - Optimism * [base](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/base.md) - Base * [zksync](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/zksync.md) - zkSync Era * [polygon](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/polygon.md) - Polygon PoS * [polygonZkEvm](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/polygonZkEvm.md) - Polygon zkEVM ##### Alternative Networks * [bsc](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/bsc.md) - BNB Smart Chain * [avalanche](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/avalanche.md) - Avalanche C-Chain * [fantom](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/fantom.md) - Fantom Opera * [gnosis](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/gnosis.md) - Gnosis Chain ##### Development Networks * [hardhat](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/hardhat.md) - Hardhat Network * [foundry](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/foundry.md) - Foundry Network * [anvil](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/anvil.md) - Anvil Network * [localhost](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/variables/localhost.md) - Local Network ### Usage Examples #### Basic Configuration ```typescript import { createCommon } from '@tevm/common' import { mainnet } from '@tevm/common' const common = createCommon({ ...mainnet, hardfork: 'shanghai' }) ``` #### Custom Chain Configuration ```typescript import { createCommon } from '@tevm/common' const common = createCommon({ name: 'Custom Chain', chainId: 1337, networkId: 1337, defaultHardfork: 'shanghai', consensus: { type: 'poa', algorithm: 'clique', clique: { period: 15, epoch: 30000 } } }) ``` #### Using with Custom Crypto ```typescript import { createCommon, createMockKzg } from '@tevm/common' const common = createCommon({ ...mainnet, customCrypto: { kzg: createMockKzg() } }) ``` #### Network-Specific Configuration ```typescript import { createCommon, optimism, arbitrum } from '@tevm/common' // Optimism configuration const optimismCommon = createCommon({ ...optimism, hardfork: 'bedrock' }) // Arbitrum configuration const arbitrumCommon = createCommon({ ...arbitrum, hardfork: 'nitro' }) ``` ### Error Handling The package throws specific errors for invalid configurations: ```typescript try { const common = createCommon({ chainId: -1 // Invalid chain ID }) } catch (error) { if (error.code === 'INVALID_CHAIN_ID') { console.error('Invalid chain ID provided') } } ``` ### See Also * [EthereumJS Common Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/common) * [Viem Chains Documentation](https://viem.sh/docs/chains/introduction.html) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/common/docs/globals.md) ## @tevm/contract The `@tevm/contract` package provides powerful utilities for working with Ethereum smart contracts, offering type-safe contract interactions and simplified deployment workflows. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/contract/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs) folder. ### Installation ```bash npm install @tevm/contract ``` ### Overview The contract package provides: * Type-safe contract interactions * Support for human-readable and JSON ABIs * Contract deployment utilities * Read and write method handling * Event filtering and subscription * Pre-built contract templates (ERC20, ERC721) ### API Reference #### Type Aliases * [Contract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/Contract.md) - Core contract type definition * [CreateContractFn](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/CreateContractFn.md) - Contract creation function type * [CreateContractParams](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/CreateContractParams.md) - Parameters for contract creation * [EventActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/EventActionCreator.md) - Event action creator type * [MaybeExtractEventArgsFromAbi](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/MaybeExtractEventArgsFromAbi.md) - Event args extraction utility * [ReadActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/ReadActionCreator.md) - Read action creator type * [ValueOf](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/ValueOf.md) - Utility type for value extraction * [WriteActionCreator](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/type-aliases/WriteActionCreator.md) - Write action creator type #### Functions * [createContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/functions/createContract.md) - Create a new contract instance #### Pre-built Contracts * [ERC20](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/ERC20.md) - Standard ERC20 token implementation * [ERC721](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/ERC721.md) - Standard ERC721 NFT implementation * [SimpleContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/variables/SimpleContract.md) - Basic contract for testing ### Usage Examples #### Creating a Contract Instance ```typescript import { createContract } from '@tevm/contract' // Using human-readable ABI const contract = createContract({ // Use as const for type safety humanReadableAbi: [ 'function balanceOf(address) view returns (uint256)', 'function transfer(address to, uint256 amount) returns (bool)', 'event Transfer(address indexed from, address indexed to, uint256 value)', ] as const, name: 'MyToken', }) // Type-safe read actions const readAction = contract.read.balanceOf('0x...') // Type-safe write actions const writeAction = contract.write.transfer('0x...', 1000n) ``` #### Contract with Address ```typescript const token = contract.withAddress('0x1234...') // Now includes address in all actions const balanceAction = token.read.balanceOf('0x...') // balanceAction.to will be set to the token address ``` #### Using Standard Contracts ```typescript import { ERC20, ERC721 } from '@tevm/contract' // ERC20 token with all standard methods const token = ERC20.withAddress('0x...') // Read token info const nameAction = token.read.name() const symbolAction = token.read.symbol() const supplyAction = token.read.totalSupply() // Transfer tokens const transferAction = token.write.transfer('0x...', 1000n) // ERC721 NFT contract const nft = ERC721.withAddress('0x...') const ownerAction = nft.read.ownerOf(1n) ``` #### Deployless Scripts ```typescript import { ERC20 } from '@tevm/contract' // Create a script that deploys and initializes a token const script = ERC20.script({ bytecode: '0x...', // Contract bytecode args: ['MyToken', 'MTK', 1000000n], // Constructor args }) // Use with any compatible client const name = await client.contract(script.read.name()) const symbol = await client.contract(script.read.symbol()) ``` #### Event Handling ```typescript // Create event filter const filter = contract.events.Transfer({ fromBlock: 'latest', }) // Process events client.watchEvent(filter, (event) => { console.log('Transfer:', { from: event.args.from, to: event.args.to, value: event.args.value, }) }) ``` ### Best Practices #### 1. Type Safety Always use `as const` with ABIs to get full type inference: ```typescript const abi = [ 'function example(uint256 value) returns (bool)', ] as const const contract = createContract({ humanReadableAbi: abi, name: 'Example', }) // contract.write.example will have proper types ``` #### 2. Error Handling Handle contract errors appropriately: ```typescript try { const result = await client.contract(contract.write.transfer('0x...', 1000n)) } catch (e) { if (e.message.includes('insufficient balance')) { // Handle specific error case } throw e } ``` #### 3. Gas Management Consider gas costs in write operations: ```typescript const tx = contract.write.complexOperation('0x...', { gas: 500000n, // Set gas limit maxFeePerGas: 30000000000n, // Set max fee }) ``` ### Contract Types The package exports useful types for contract development: ```typescript import type { Contract, CreateContractFn, CreateContractParams, EventActionCreator, ReadActionCreator, WriteActionCreator, } from '@tevm/contract' // Use with your own contracts type MyContract = Contract ``` ### See Also * [ERC-20 Token Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) * [ERC-721 NFT Standard](https://ethereum.org/en/developers/docs/standards/tokens/erc-721/) * [Solidity Documentation](https://docs.soliditylang.org/) * [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/contract/docs/globals.md) ## @tevm/decorators The `@tevm/decorators` package provides action decorators and utilities for extending Tevm clients with additional functionality. It includes EIP-1193 compatible providers, Ethereum JSON-RPC methods, and Tevm-specific actions. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/decorators/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs) folder. ### Installation ```bash npm install @tevm/decorators ``` ### Overview The `@tevm/decorators` package provides: * EIP-1193 compatible provider decorators * Ethereum JSON-RPC method implementations * Tevm-specific action decorators * Type-safe request and response handling * Chain and network utilities ### API Reference #### Core Functions ##### [`ethActions()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/ethActions.md) Creates an extension providing standard Ethereum JSON-RPC methods. ##### [`requestEip1193()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/requestEip1193.md) Creates an EIP-1193 compatible provider extension. ##### [`tevmActions()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/tevmActions.md) Creates an extension providing Tevm-specific actions. ##### [`tevmSend()`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/functions/tevmSend.md) Creates an extension for sending Tevm JSON-RPC requests. #### Core Types ##### Provider Types * [`Eip1193RequestProvider`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/Eip1193RequestProvider.md) - EIP-1193 compatible provider interface * [`EIP1193Parameters`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193Parameters.md) - Parameters for EIP-1193 requests * [`EIP1193RequestFn`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193RequestFn.md) - Request function type for EIP-1193 * [`EIP1193RequestOptions`](https://github.com/evmts/tevm-monorepo/tree/main/packages/decorators/docs/type-aliases/EIP1193RequestOptions.md) - Options for EIP-1193 requests ##### Action Types * [`EthActionsApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/EthActionsApi.md) - Standard Ethereum JSON-RPC actions * [`TevmActionsApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TevmActionsApi.md) - Tevm-specific actions * [`TevmSendApi`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TevmSendApi.md) - API for sending Tevm requests ##### RPC Schema Types * [`RpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/RpcSchema.md) - Base RPC schema type * [`RpcSchemaOverride`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/RpcSchemaOverride.md) - Schema override type * [`DerivedRpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/DerivedRpcSchema.md) - Derived schema type * [`JsonRpcSchemaPublic`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaPublic.md) - Public JSON-RPC schema * [`JsonRpcSchemaTevm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaTevm.md) - Tevm-specific JSON-RPC schema * [`JsonRpcSchemaWallet`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/JsonRpcSchemaWallet.md) - Wallet JSON-RPC schema * [`TestRpcSchema`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/TestRpcSchema.md) - Test-specific RPC schema ##### Ethereum Types * [`AddEthereumChainParameter`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/AddEthereumChainParameter.md) - Parameters for adding a chain * [`WatchAssetParams`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WatchAssetParams.md) - Parameters for watching assets * [`WalletPermission`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WalletPermission.md) - Wallet permission type * [`WalletPermissionCaveat`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/WalletPermissionCaveat.md) - Wallet permission caveat type ##### Utility Types * [`Hash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/Hash.md) - Ethereum hash type * [`LogTopic`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/LogTopic.md) - Log topic type * [`NetworkSync`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/NetworkSync.md) - Network sync status type * [`Quantity`](https://github.com/evmts/tevm-monorepo/blob/main/packages/decorators/docs/type-aliases/Quantity.md) - Ethereum quantity type ### Usage Examples #### Creating an EIP-1193 Provider ```typescript import { requestEip1193 } from '@tevm/decorators' const provider = requestEip1193() const client = createClient({ transport: provider }) // Make EIP-1193 requests const result = await client.request({ method: 'eth_call', params: [{ to: '0x...', data: '0x...' }] }) ``` #### Using Ethereum Actions ```typescript import { ethActions } from '@tevm/decorators' const eth = ethActions() const client = createClient({ transport: eth }) // Use standard Ethereum methods const balance = await client.eth.getBalance({ address: '0x...' }) const code = await client.eth.getCode({ address: '0x...' }) ``` #### Using Tevm Actions ```typescript import { tevmActions } from '@tevm/decorators' const tevm = tevmActions() const client = createClient({ transport: tevm }) // Use Tevm-specific actions const result = await client.transport.tevm.call({ to: '0x...', data: '0x...' }) const state = await client.transport.tevm.dumpState() ``` #### Error Handling ```typescript try { const result = await client.request({ method: 'eth_call', params: [{ to: '0x...', data: '0x...' }] }) } catch (error) { if (error.code === -32000) { // Handle execution error } throw error } ``` ### Best Practices #### 1. Type Safety Always leverage TypeScript types for request parameters and responses: ```typescript import type { EIP1193RequestFn } from '@tevm/decorators' const request: EIP1193RequestFn = async (params) => { // Type-safe parameters and return value } ``` #### 2. Error Handling Handle both standard JSON-RPC errors and Tevm-specific errors: ```typescript try { await client.request(...) } catch (error) { if (error.code === 4001) { // User rejected request } else if (error.code === -32000) { // Execution error } } ``` #### 3. Chain Management Use proper chain parameters when adding new chains: ```typescript const chainParams: AddEthereumChainParameter = { chainId: '0x1', chainName: 'Ethereum Mainnet', nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, rpcUrls: ['https://...'] } ``` ### See Also * [JSON-RPC API](/api/json-rpc) * [Actions Guide](/reference/actions) * [EIP-1193 Specification](https://eips.ethereum.org/EIPS/eip-1193) * [Ethereum JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) ## @tevm/evm The `@tevm/evm` package provides a custom implementation of the Ethereum Virtual Machine (EVM), extending the functionality of `ethereumjs/evm`. It's responsible for executing Ethereum bytecode and managing the execution environment. > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/evm/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs) folder. ### Installation ```bash npm install @tevm/evm ``` ### API Reference #### Enumerations * [EvmErrorMessage](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/enumerations/EvmErrorMessage.md) - EVM error message types #### Core Classes * [Evm](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/Evm.md) - Main EVM implementation class * [EthjsMessage](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/EthjsMessage.md) - EVM message handling * [EvmError](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/classes/EvmError.md) - EVM error handling #### Interfaces * [EvmResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/EvmResult.md) - Result of EVM execution * [EvmRunCallOpts](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/EvmRunCallOpts.md) - Options for running calls * [ExecResult](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/ExecResult.md) - Execution result details * [InterpreterStep](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/InterpreterStep.md) - Interpreter step information * [PrecompileInput](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/interfaces/PrecompileInput.md) - Input for precompiles #### Type Aliases * [CreateEvmOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/CreateEvmOptions.md) - Options for creating an EVM * [CustomPrecompile](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/CustomPrecompile.md) - Custom precompile definition * [EVMOpts](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/type-aliases/EVMOpts.md) - EVM configuration options #### Variables * [Eof](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/variables/Eof.md) - EOF-related constants #### Functions * [createEvm](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/functions/createEvm.md) - Create a new EVM instance * [getActivePrecompiles](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/functions/getActivePrecompiles.md) - Get active precompiles ### Usage Examples #### Creating an EVM Instance ```typescript import { createEvm } from '@tevm/evm' import { mainnet } from '@tevm/common' import { createStateManager } from '@tevm/state' import { createChain } from '@tevm/chain' const evm = await createEvm({ common: mainnet, stateManager: createStateManager, blockchain: await createChain({ common: mainnet }), customPrecompiles: [], // Optional custom precompiles profiler: false, // Enable/disable profiling loggingLevel: 'warn', // Logging configuration }) ``` #### Running EVM Calls ```typescript const result = await evm.runCall({ to: '0x1234...', caller: '0x5678...', data: new Uint8Array([/* bytecode */]), value: 1000n, gasLimit: 100000n }) console.log(result.execResult.returnValue) console.log(result.execResult.executionGasUsed) ``` #### Custom Precompiles ```typescript import { definePrecompile, defineCall, createContract, parseAbi } from '@tevm/evm' import { createAddress } from '@tevm/utils' const MyContract = createContract({ address: createAddress(2424).toString(), abi: parseAbi([ 'function addTwo(uint256) returns (uint256)', ]) }) const customPrecompile = definePrecompile({ contract: MyContract, call: defineCall(MyContract.abi, { addTwo: async ({ args }) => { return { returnValue: args[0] + 5n, executionGasUsed: 0n } } }) }) evm.addCustomPrecompile(customPrecompile.precompile()) ``` #### Error Handling ```typescript try { const result = await evm.runCall({ to: '0x1234...', gasLimit: 100n // Very low gas limit }) } catch (error) { if (error instanceof EvmError) { console.log(error.error) // e.g. EvmErrorMessage.OUT_OF_GAS } } ``` #### Debugging and Performance ```typescript // Enable debug logging const evm = await createEvm({ loggingLevel: 'trace' }) // Get performance logs const logs = evm.getPerformanceLogs() console.log(logs.opcodes) // Opcode execution stats console.log(logs.precompiles) // Precompile execution stats ``` ### See Also * [EthereumJS EVM Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) * [Tevm State Documentation](https://tevm.sh/reference/tevm/state/) * [Tevm Precompiles Documentation](https://tevm.sh/reference/tevm/precompiles/) * [Full API Reference](https://github.com/evmts/tevm-monorepo/tree/main/packages/evm/docs/globals.md) ## Package Reference Documentation This section contains detailed API reference documentation for all Tevm packages. * [@tevm/actions](/reference/actions) - Core action handlers for interacting with the EVM * [@tevm/address](/reference/address) - Ethereum address utilities * [@tevm/block](/reference/block) - Block creation and manipulation * [@tevm/blockchain](/reference/blockchain) - Blockchain data structures and utilities * [@tevm/common](/reference/common) - Shared utilities and constants * [@tevm/contract](/reference/contract) - Library for using contracts in typesafe way * [Contract Bundler](/reference/bundler) - Import Solidity files directly in TypeScript * [@tevm/decorators](/reference/decorators) - Extension decorators * [@tevm/evm](/reference/evm) - Core EVM implementation * [@tevm/memory-client](/reference/memory-client) - Batteries included viem client along with tree shakable actions * [@tevm/receipt-manager](/reference/receipt-manager) - Transaction receipt handling * [@tevm/state](/reference/state) - State management utilities * [@tevm/tx](/reference/tx) - Transaction handling utilities * [@tevm/txpool](/reference/txpool) - Transaction pool management * [@tevm/utils](/reference/utils) - General utility functions * [@tevm/vm](/reference/vm) - Virtual Machine implementation Each package's documentation includes: * Complete API reference * Type definitions * Usage examples * Configuration options ## @tevm/memory-client The `@tevm/memory-client` package provides a powerful in-memory Ethereum client implementation for Tevm. It combines the capabilities of viem with Tevm's custom EVM implementation to offer a complete testing and development environment. ### Installation ```bash npm install @tevm/memory-client ``` ### API Reference #### Type Aliases * [CreateMemoryClientFn](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/CreateMemoryClientFn.md) - Function type for creating memory clients * [MemoryClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/MemoryClient.md) - Core memory client type * [MemoryClientOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/MemoryClientOptions.md) - Configuration options * [TevmActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmActions.md) - Tevm-specific actions * [TevmContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmContract.md) - Contract interaction type * [TevmRpcSchema](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmRpcSchema.md) - RPC schema definition * [TevmTransport](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmTransport.md) - Transport layer type * [TevmViemActionsApi](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/type-aliases/TevmViemActionsApi.md) - Viem actions API #### Actions * [createClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createClient.md) - Create a new client instance * [createMemoryClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createMemoryClient.md) - Create a memory client * [createTevmTransport](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/createTevmTransport.md) - Create transport layer * [publicActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/publicActions.md) - Public action creators * [testActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/testActions.md) - Test action creators * [tevmCall](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmCall.md) - Execute EVM call * [tevmContract](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmContract.md) - Contract interaction * [tevmDeploy](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmDeploy.md) - Deploy contract * [tevmDumpState](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmDumpState.md) - Export state * [tevmGetAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmGetAccount.md) - Get account state * [tevmLoadState](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmLoadState.md) - Import state * [tevmMine](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmMine.md) - Mine blocks * [tevmReady](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmReady.md) - Check client readiness * [tevmSetAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmSetAccount.md) - Set account state * [tevmViemActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/tevmViemActions.md) - Viem action creators * [walletActions](https://github.com/evmts/tevm-monorepo/tree/main/packages/memory-client/docs/functions/walletActions.md) - Wallet action creators ### Overview The memory client package provides: * A complete in-memory Ethereum client implementation * Support for forking existing networks * Automatic and manual mining modes * Full compatibility with viem's actions * Extended functionality through Tevm-specific actions ### Core Components #### MemoryClient The main client class that provides Ethereum client functionality with in-memory state management. ```typescript import { createMemoryClient } from '@tevm/memory-client' const client = createMemoryClient({ fork: { transport: http("https://mainnet.optimism.io") } }) ``` #### Client Options ```typescript interface MemoryClientOptions { // Chain configuration common?: TCommon // Fork configuration fork?: { transport: Transport blockTag?: string | number } // Client configuration name?: string account?: TAccountOrAddress pollingInterval?: number cacheTime?: number } ``` ### Features #### Mining Modes The client supports two mining modes: ```typescript // Auto mining (default) const client = createMemoryClient() // Manual mining const client = createMemoryClient({ mining: { mode: 'manual' } }) // Mine blocks manually await client.mine() ``` #### Network Forking Fork any EVM-compatible network: ```typescript import { createMemoryClient, http } from '@tevm/memory-client' import { optimism } from '@tevm/common' const client = createMemoryClient({ fork: { transport: http("https://mainnet.optimism.io"), blockTag: '0xa6a63cd70fbbe396321ca6fe79e1b6735760c03538208b50d7e3a5dac5226435' }, common: optimism }) ``` #### State Management ```typescript // Set account state await client.setAccount({ address: '0x...', balance: 100n, nonce: 1n, deployedBytecode: '0x...', state: { '0x...': '0x...' } }) // Get account state const account = await client.getAccount({ address: '0x...', returnStorage: true }) ``` #### Contract Interactions ```typescript // Deploy contract const deployResult = await client.tevmDeploy({ abi: contractAbi, bytecode: contractBytecode, args: ['Constructor', 'Args'] }) // Call contract const result = await client.tevmCall({ to: contractAddress, data: encodeFunctionData(...) }) // Contract interaction with high-level API const contractResult = await client.tevmContract({ contract: myContract, method: 'myMethod', args: [arg1, arg2] }) ``` #### Transaction Management ```typescript // Send transaction const hash = await client.writeContract({ address: contractAddress, abi: contractAbi, functionName: 'myFunction', args: [arg1, arg2] }) // Wait for receipt const receipt = await client.waitForTransactionReceipt({ hash }) ``` ### Actions API The client includes several sets of actions: #### Tevm Actions * `tevmCall`: Low-level EVM call execution * `tevmContract`: High-level contract interaction * `tevmDeploy`: Contract deployment * `tevmGetAccount`: Account state retrieval * `tevmSetAccount`: Account state modification * `tevmMine`: Manual block mining * `tevmDumpState`: State export * `tevmLoadState`: State import #### Viem Actions * Public actions (e.g., `getBlockNumber`, `getBalance`) * Wallet actions (e.g., `sendTransaction`, `signMessage`) * Test actions (e.g., `setBalance`, `impersonateAccount`) ### Advanced Features #### State Persistence ```typescript import { createMemoryClient, createSyncPersister } from '@tevm/memory-client' const client = createMemoryClient({ persister: createSyncPersister({ storage: localStorage }) }) ``` #### HTTP Server Integration ```typescript import { createServer } from '@tevm/server' import { createMemoryClient } from '@tevm/memory-client' const client = createMemoryClient() const server = createServer({ request: client.request }) server.listen(8545) ``` ### Network Support Officially supported networks: * Ethereum mainnet * Standard OP Stack chains Other EVM-compatible chains may work but are not officially supported. ### EIP Support Always-enabled EIPs: * EIP-1559 (Fee Market) * EIP-4895 * EIP-4844 (Blob Transactions) * EIP-4788 ### Types The package exports several important types: ```typescript type MemoryClient type MemoryClientOptions type TevmActions type TevmContract type TevmRpcSchema type TevmTransport ``` ### License This package is licensed under the MIT License. ### See Also * [What is Tevm Node?](../introduction/what-is-tevm-node) * [Creating a Node](../core/create-tevm-node) * [Viem Documentation](https://viem.sh) ## @tevm/node API Reference This documentation is generated from the source code. View the full API documentation on GitHub: [View @tevm/node API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/globals.md) ### Quick Links * [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/type-aliases) * [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/classes) * [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/functions) * [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/node/docs/variables) ### Return to Documentation * [Back to Getting Started](/getting-started/overview) * [Package Overview](/reference) ## @tevm/receipt-manager The `@tevm/receipt-manager` package provides a robust transaction receipt management system for the Tevm blockchain. It handles the storage, retrieval, and management of transaction receipts, logs, and related data. ### Installation ```bash npm install @tevm/receipt-manager ``` ### Overview The receipt manager package is responsible for: * Managing transaction receipts and their storage * Handling transaction logs and bloom filters * Supporting different receipt types (Pre-Byzantium, Post-Byzantium, EIP4844) * Providing efficient receipt lookup and retrieval ### Core Components #### ReceiptsManager The main class that handles receipt management operations. ```typescript import { ReceiptsManager } from '@tevm/receipt-manager' const receiptsManager = new ReceiptsManager(mapDb, chain) ``` ##### Key Methods * `saveReceipts(block, receipts)`: Saves receipts to the database * `getReceipts(blockHash)`: Retrieves receipts for a given block hash * `getReceiptByTxHash(txHash)`: Gets a receipt by transaction hash * `getLogs(from, to, addresses?, topics?)`: Returns logs based on filter criteria * `deleteReceipts(block)`: Removes receipts for a given block #### Receipt Types The package supports multiple receipt types to accommodate different Ethereum protocol versions: ##### BaseTxReceipt ```typescript interface BaseTxReceipt { cumulativeBlockGasUsed: bigint bitvector: Uint8Array logs: Log[] } ``` ##### PreByzantiumTxReceipt ```typescript interface PreByzantiumTxReceipt extends BaseTxReceipt { stateRoot: Uint8Array } ``` ##### PostByzantiumTxReceipt ```typescript interface PostByzantiumTxReceipt extends BaseTxReceipt { status: 0 | 1 } ``` ##### EIP4844BlobTxReceipt ```typescript interface EIP4844BlobTxReceipt extends PostByzantiumTxReceipt { blobGasUsed: bigint blobGasPrice: bigint } ``` ### Database Management #### MapDb The package includes a MapDb implementation for storing receipt data: ```typescript import { createMapDb } from '@tevm/receipt-manager' const mapDb = createMapDb({ cache: new Map() }) ``` #### Configuration Options ```typescript interface MetaDBManagerOptions { cache: Map<`0x${string}`, Uint8Array> } ``` ### Usage Examples #### Saving and Retrieving Receipts ```typescript // Save receipts await receiptsManager.saveReceipts(block, receipts) // Retrieve receipts by block hash const receipts = await receiptsManager.getReceipts(blockHash) // Get receipt by transaction hash const receipt = await receiptsManager.getReceiptByTxHash(txHash) ``` #### Working with Logs ```typescript // Query logs with filters const logs = await receiptsManager.getLogs( fromBlock, toBlock, addresses, topics ) ``` ### Constants and Limits The ReceiptsManager includes several important limits: ```typescript GET_LOGS_LIMIT = 10000 // Maximum number of logs to return GET_LOGS_LIMIT_MEGABYTES = 150 // Size limit for getLogs response GET_LOGS_BLOCK_RANGE_LIMIT = 2500 // Block range limit for getLogs ``` ### Error Handling The package includes proper error handling for common scenarios: ```typescript try { const receipt = await receiptsManager.getReceiptByTxHash(txHash) if (!receipt) { // Handle missing receipt } } catch (error) { // Handle errors } ``` ### Types The package exports several important types: ```typescript type TxReceipt = PreByzantiumTxReceipt | PostByzantiumTxReceipt | EIP4844BlobTxReceipt type TxReceiptWithType = PreByzantiumTxReceiptWithType | PostByzantiumTxReceiptWithType type DbType = "Receipts" | "TxHash" | "SkeletonBlock" | "SkeletonBlockHashToNumber" | "SkeletonStatus" | "SkeletonUnfinalizedBlockByHash" | "Preimage" ``` ### License This package is licensed under the MIT License. Some files are adapted from [ethereumjs](https://github.com/ethereumjs/ethereumjs-monorepo) and are licensed under the Mozilla Public License 2.0. ## @tevm/rlp API Reference This documentation is generated from the source code. View the full API documentation on GitHub: [View @tevm/rlp API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/globals.md) ### Quick Links * [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/type-aliases) * [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/classes) * [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/functions) * [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/rlp/docs/variables) ### Return to Documentation * [Back to Getting Started](/getting-started/overview) * [Package Overview](/reference) ## @tevm/state The `@tevm/state` package provides a robust state management system for Tevm, handling Ethereum account states, contract storage, and state transitions. It offers both synchronous and asynchronous APIs for managing the EVM state. ### Installation ```bash npm install @tevm/state ``` ### Overview The state package is responsible for: * Managing account states (balance, nonce, code, storage) * Handling state transitions and checkpoints * Caching and persistence of state data * Supporting forked chain states * Providing efficient state access and modification ### API Reference #### Enumerations * [CacheType](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/enumerations/CacheType.md) - Types of caching mechanisms #### Core Types ##### State Management * [StateManager](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/StateManager.md) - Main interface for state operations * [BaseState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/BaseState.md) - Core state data structure * [TevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/TevmState.md) - Tevm-specific state representation * [StateAction](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateAction.md) - Type for state modification actions * [StateOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateOptions.md) - State configuration options * [StateRoots](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateRoots.md) - State roots mapping * [ParameterizedTevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/ParameterizedTevmState.md) - Parameterized state type ##### Storage Types * [AccountStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/AccountStorage.md) - Account storage structure * [ParameterizedAccountStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/ParameterizedAccountStorage.md) - Parameterized account storage interface * [SerializableTevmState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/SerializableTevmState.md) - Serializable state format * [ForkOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/interfaces/ForkOptions.md) - Options for forking state ##### Caching * [StateCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/type-aliases/StateCache.md) - State caching structure * [AccountCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/AccountCache.md) - Account-level cache * [ContractCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/ContractCache.md) - Contract-level cache * [StorageCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/classes/StorageCache.md) - Storage-level cache #### Core Functions ##### State Creation and Management * [createStateManager](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/createStateManager.md) - Creates a new state manager instance * [createBaseState](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/createBaseState.md) - Creates the core state data structure * [deepCopy](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/deepCopy.md) - Creates deep copy of state * [shallowCopy](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/shallowCopy.md) - Creates shallow copy ##### State Operations * [getAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAccount.md) - Retrieves account state * [putAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putAccount.md) - Updates account state * [deleteAccount](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/deleteAccount.md) - Removes an account * [getContractCode](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getContractCode.md) - Gets contract bytecode * [putContractCode](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putContractCode.md) - Updates contract bytecode * [getContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getContractStorage.md) - Gets contract storage * [putContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/putContractStorage.md) - Updates contract storage * [clearContractStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/clearContractStorage.md) - Clears contract storage * [getAccountAddresses](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAccountAddresses.md) - Gets account addresses * [getAppliedKey](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getAppliedKey.md) - Gets applied storage key * [modifyAccountFields](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/modifyAccountFields.md) - Modifies account fields ##### State Root Management * [getStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getStateRoot.md) - Gets current state root * [setStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/setStateRoot.md) - Sets new state root * [hasStateRoot](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/hasStateRoot.md) - Checks if state root exists ##### Checkpointing and Committing * [checkpoint](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/checkpoint.md) - Creates a state checkpoint * [commit](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/commit.md) - Commits state changes * [revert](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/revert.md) - Reverts to previous checkpoint ##### Cache Management * [clearCaches](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/clearCaches.md) - Clears all state caches * [originalStorageCache](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/originalStorageCache.md) - Manages original storage cache ##### Genesis and Forking * [dumpCanonicalGenesis](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpCanonicalGenesis.md) - Dumps canonical genesis state * [generateCanonicalGenesis](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/generateCanonicalGenesis.md) - Generates canonical genesis * [getForkBlockTag](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getForkBlockTag.md) - Gets fork block tag * [getForkClient](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getForkClient.md) - Gets fork client ##### Storage Operations * [dumpStorage](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpStorage.md) - Dumps storage state * [dumpStorageRange](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/dumpStorageRange.md) - Dumps storage range * [getProof](https://github.com/evmts/tevm-monorepo/tree/main/packages/state/docs/functions/getProof.md) - Gets state proof ### Usage Examples #### Creating a State Manager ``` ``` ## @tevm/trie API Reference This documentation is generated from the source code. View the full API documentation on GitHub: [View @tevm/trie API Reference on GitHub](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/globals.md) ### Quick Links * [Type Aliases](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/type-aliases) * [Classes](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/classes) * [Functions](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/functions) * [Variables](https://github.com/evmts/tevm-monorepo/tree/main/packages/trie/docs/variables) ### Return to Documentation * [Back to Getting Started](/getting-started/overview) * [Package Overview](/reference) ## @tevm/tx > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/tx/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs) folder. The `@tevm/tx` package provides a custom implementation of Ethereum transactions, extending the functionality of `@ethereumjs/tx`. It includes support for various transaction types and introduces impersonated transactions unique to Tevm. ### Installation ```bash npm install @tevm/tx ``` ### API Reference #### Enumerations * [Capability](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/enumerations/Capability.md) - Transaction capabilities * [TransactionType](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/enumerations/TransactionType.md) - Supported transaction types #### Classes * [AccessListEIP2930Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/AccessListEIP2930Transaction.md) - Type 1 transaction implementation * [BlobEIP4844Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/BlobEIP4844Transaction.md) - Type 3 transaction implementation * [FeeMarketEIP1559Transaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/FeeMarketEIP1559Transaction.md) - Type 2 transaction implementation * [LegacyTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/LegacyTransaction.md) - Legacy transaction implementation * [TransactionFactory](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/classes/TransactionFactory.md) - Factory for creating transactions #### Interfaces * [EIP1559CompatibleTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/EIP1559CompatibleTx.md) - EIP-1559 transaction interface * [EIP4844CompatibleTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/EIP4844CompatibleTx.md) - EIP-4844 transaction interface * [ImpersonatedTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/ImpersonatedTx.md) - Impersonated transaction interface * [JsonRpcTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/JsonRpcTx.md) - JSON-RPC transaction format * [JsonTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/JsonTx.md) - JSON transaction format * [TxData](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/TxData.md) - Transaction data interface * [TxOptions](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/interfaces/TxOptions.md) - Transaction options interface #### Type Aliases * [AccessList](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/AccessList.md) - Access list type definition * [AccessListItem](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/AccessListItem.md) - Access list item type * [TypedTransaction](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/type-aliases/TypedTransaction.md) - Union type of all transaction types #### Functions * [createImpersonatedTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/createImpersonatedTx.md) - Create an impersonated transaction * [isAccessListEIP2930Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isAccessListEIP2930Tx.md) - Type guard for EIP-2930 transactions * [isBlobEIP4844Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isBlobEIP4844Tx.md) - Type guard for EIP-4844 transactions * [isFeeMarketEIP1559Tx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isFeeMarketEIP1559Tx.md) - Type guard for EIP-1559 transactions * [isLegacyTx](https://github.com/evmts/tevm-monorepo/tree/main/packages/tx/docs/functions/isLegacyTx.md) - Type guard for legacy transactions ### Main Components #### Transaction Types The package supports multiple transaction types: * **LegacyTransaction**: Pre-EIP-2718 transactions * **AccessListEIP2930Transaction**: Type 1 transactions with access lists (EIP-2930) * **FeeMarketEIP1559Transaction**: Type 2 transactions with fee market (EIP-1559) * **BlobEIP4844Transaction**: Type 3 transactions with blob data (EIP-4844) * **ImpersonatedTx**: Tevm-specific transaction type for impersonating accounts #### TransactionFactory A utility class for creating transactions from various data formats: ```typescript import { TransactionFactory } from '@tevm/tx' // Create from serialized data const tx = TransactionFactory.fromSerializedTx(serializedData) // Create from RPC data const tx = await TransactionFactory.fromRPC(rpcTxData) // Create from block body data const tx = TransactionFactory.fromBlockBodyData(blockData) ``` #### Impersonated Transactions A unique feature of Tevm that allows simulating transactions as if they were sent from any address: ```typescript import { createImpersonatedTx } from '@tevm/tx' const tx = createImpersonatedTx({ impersonatedAddress: address, to: recipient, value: value, data: data, // ... other EIP-1559 transaction fields }) ``` ### Transaction Types #### Legacy Transactions Pre-EIP-2718 transactions with basic fields: ```typescript interface LegacyTxData { nonce: bigint gasPrice: bigint gasLimit: bigint to?: Address value: bigint data: Uint8Array v?: bigint r?: bigint s?: bigint } ``` #### EIP-2930 Transactions Type 1 transactions with access lists: ```typescript interface AccessListEIP2930TxData extends LegacyTxData { chainId: bigint accessList: AccessList } ``` #### EIP-1559 Transactions Type 2 transactions with fee market: ```typescript interface FeeMarketEIP1559TxData extends AccessListEIP2930TxData { maxFeePerGas: bigint maxPriorityFeePerGas: bigint } ``` #### EIP-4844 Transactions Type 3 transactions with blob data: ```typescript interface BlobEIP4844TxData extends FeeMarketEIP1559TxData { maxFeePerBlobGas: bigint blobVersionedHashes: Uint8Array[] blobs?: Uint8Array[] kzgCommitments?: Uint8Array[] kzgProofs?: Uint8Array[] } ``` ### Common Operations #### Creating Transactions ```typescript import { TransactionFactory, LegacyTransaction } from '@tevm/tx' // Using factory const tx = TransactionFactory.fromTxData({ nonce: 0n, gasPrice: 20000000000n, gasLimit: 21000n, to: '0x...', value: 1000000000000000000n, data: new Uint8Array() }) // Direct instantiation const legacyTx = new LegacyTransaction({ nonce: 0n, gasPrice: 20000000000n, gasLimit: 21000n, to: '0x...', value: 1000000000000000000n, data: new Uint8Array() }) ``` #### Signing Transactions ```typescript const signedTx = tx.sign(privateKey) ``` #### Transaction Methods All transaction types provide common methods: * `hash()`: Get transaction hash * `getBaseFee()`: Get minimum required gas * `getDataFee()`: Get gas cost for data * `getUpfrontCost()`: Get total required balance * `isSigned()`: Check if transaction is signed * `serialize()`: Get RLP encoded transaction * `toJSON()`: Get JSON representation ### Error Handling The package includes custom error types: ```typescript import { InvalidGasLimitError } from '@tevm/tx' try { const tx = createImpersonatedTx(txData) } catch (e) { if (e instanceof InvalidGasLimitError) { // Handle invalid gas limit } } ``` ### See Also * [EIP-2718: Typed Transaction Envelope](https://eips.ethereum.org/EIPS/eip-2718) * [EIP-2930: Optional access lists](https://eips.ethereum.org/EIPS/eip-2930) * [EIP-1559: Fee market change](https://eips.ethereum.org/EIPS/eip-1559) * [EIP-4844: Shard Blob Transactions](https://eips.ethereum.org/EIPS/eip-4844) ## @tevm/txpool The `@tevm/txpool` package provides a transaction pool (mempool) implementation for Tevm, managing pending transactions and their lifecycle within the Ethereum Virtual Machine. ### Installation ```bash npm install @tevm/txpool ``` ### Overview The transaction pool is responsible for: * Managing pending transactions * Validating transaction requirements * Ordering transactions by price and nonce * Handling transaction replacement * Cleaning up stale transactions * Supporting transaction lifecycle management ### API Reference #### Core Class ##### TxPool [`TxPool`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts) - The main transaction pool class with the following key features: ##### Constructor ```typescript new TxPool(options: TxPoolOptions) ``` ##### Properties * `BLOCKS_BEFORE_TARGET_HEIGHT_ACTIVATION`: Number of blocks before chain head to start tx pool preparation * `POOLED_STORAGE_TIME_LIMIT`: Number of minutes to keep txs in the pool * `HANDLED_CLEANUP_TIME_LIMIT`: Number of minutes to forget about handled txs * `pool`: The central pool dataset mapping addresses to transactions * `running`: Boolean indicating if the pool is running * `txsInPool`: Number of transactions currently in the pool ##### Methods * [`add`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L310) - Adds a transaction to the pool * [`addUnverified`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L280) - Adds an unverified transaction to the pool * [`getByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L320) - Retrieves transactions by their hashes * [`removeByHash`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L341) - Removes a transaction by its hash * [`txsByPriceAndNonce`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L477) - Returns transactions sorted by price and nonce * [`cleanup`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L374) - Performs pool cleanup * [`open`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L141) - Opens the transaction pool * [`close`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L581) - Closes the transaction pool * [`start`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L153) - Starts transaction processing * [`stop`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L570) - Stops transaction processing * [`deepCopy`](https://github.com/evmts/tevm-monorepo/blob/main/packages/txpool/src/TxPool.ts#L128) - Creates a deep copy of the pool ### Usage Examples #### Creating and Managing a Transaction Pool ```typescript import { TxPool } from '@tevm/txpool' import { createCommon } from '@tevm/common' const common = createCommon({ chain: 'mainnet' }) const txPool = new TxPool({ common, maxPoolSize: 5000, minGasPriceBump: 10 // 10% price bump for replacement }) // Start the pool txPool.open() txPool.start() ``` #### Adding Transactions ```typescript // Add a new transaction await txPool.add(transaction, { requireSignature: true, skipBalance: false }) // Add an unverified transaction await txPool.addUnverified(transaction) ``` #### Retrieving Transactions ```typescript // Get transactions by hash const txs = txPool.getByHash(txHashes) // Get transactions by sender const senderTxs = await txPool.getBySenderAddress(senderAddress) // Get transactions ordered by price and nonce const orderedTxs = await txPool.txsByPriceAndNonce({ baseFee: 1000000000n, allowedBlobs: 3 }) ``` #### Managing Transaction Lifecycle ```typescript // Remove a transaction txPool.removeByHash(txHash) // Remove transactions included in new blocks txPool.removeNewBlockTxs(newBlocks) // Perform cleanup of stale transactions txPool.cleanup() ``` #### Pool Lifecycle Management ```typescript // Start the pool txPool.open() txPool.start() // Stop the pool txPool.stop() txPool.close() // Create a copy of the pool const poolCopy = txPool.deepCopy(options) ``` ### Configuration The transaction pool can be configured with various options: ```typescript interface TxPoolOptions { common: Common maxPoolSize?: number minGasPriceBump?: number minFeeBump?: number maxPendingTotal?: number maxPendingPerAccount?: number maxQueuedTotal?: number maxQueuedPerAccount?: number minPendingNodeBalance?: bigint minRemainingGasLimit?: bigint } ``` #### Configuration Options * `maxPoolSize`: Maximum number of transactions in the pool * `minGasPriceBump`: Minimum price bump percentage for transaction replacement * `minFeeBump`: Minimum fee bump for transaction replacement * `maxPendingTotal`: Maximum number of pending transactions * `maxPendingPerAccount`: Maximum pending transactions per account * `maxQueuedTotal`: Maximum number of queued transactions * `maxQueuedPerAccount`: Maximum queued transactions per account * `minPendingNodeBalance`: Minimum balance required for pending transactions * `minRemainingGasLimit`: Minimum gas limit for remaining transactions ### Error Handling The transaction pool throws specific errors for various failure scenarios: ```typescript try { await txPool.add(transaction) } catch (error) { if (error.code === 'POOL_FULL') { console.error('Transaction pool is full') } else if (error.code === 'UNDERPRICED') { console.error('Transaction is underpriced') } else if (error.code === 'NONCE_TOO_LOW') { console.error('Transaction nonce is too low') } } ``` ### Best Practices 1. **Regular Cleanup**: Call `cleanup()` periodically to remove stale transactions 2. **Transaction Replacement**: Use appropriate gas price bumps for replacement transactions 3. **Pool Size Management**: Monitor and adjust pool size limits based on network conditions 4. **Error Handling**: Implement proper error handling for transaction additions and removals 5. **Lifecycle Management**: Properly manage pool lifecycle with `open()`, `start()`, `stop()`, and `close()` ### Related Packages * [@tevm/vm](./vm) - Virtual Machine implementation * [@tevm/state](./state) - State management * [@tevm/common](./common) - Chain configuration ### License MIT ## Utilities & Addresses Tevm exports a set of lightweight utility functions and classes. Many of these build upon lower-level packages like [`tevm/utils`](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils) and [`viem`](https://viem.sh) while standardizing usage for Tevm Node. Below are the most commonly used. ### createAddress Creates an [Ethereum address](https://ethereum.org/en/developers/docs/accounts/#account-creation) from various input formats: ```ts import { createAddress } from 'tevm/address' // Creates a TEVM-style Address object from various input forms let addr = createAddress(`0x${"00".repeat(20)}`) // from a hex string addr = createAddress(420n) // from a bigint addr = createAddress(new Uint8Array(20)) // from a 20-byte array ``` #### Signature ```ts declare function createAddress( address: number | bigint | string | Uint8Array | EthjsAddress ): Address ``` #### Behavior & Notes * Accepts various input types: [`0x`-prefixed hex strings](https://ethereum.org/en/developers/docs/data-structures-and-encoding/hex/), unprefixed hex, numbers, bigints, `Uint8Array`s, or `EthjsAddress`. * Throws `InvalidAddressError` if it can't parse a valid 20-byte address from the input. ### Address Class A thin wrapper around [`EthjsAddress`](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util) offering a simple, consistent shape for Tevm Node. Created by `createAddress` or by forging your own: ```ts import { Address } from 'tevm/address' const a = new Address(Uint8Array.from([ /* 20 bytes */ ])) console.log(a.bytes) // a raw 20-byte address console.log(a.toString()) // 0x.... ``` ### createContractAddress Creates a contract address following [EIP-1014](https://eips.ethereum.org/EIPS/eip-1014): ```ts import { createContractAddress } from 'tevm/address' // from an existing address + nonce, produce the CREATE address const from = createAddress("0x1111...1111") const nonce = 1n const contractAddr = createContractAddress(from, nonce) ``` #### Usage 1. Follows standard Ethereum's [`keccak256(rlp([senderAddress, nonce]))[-20..]`](https://ethereum.org/en/developers/docs/smart-contracts/deploying/#how-to-deploy-a-smart-contract). 2. Ideal for simulating `CREATE` addresses in test or dev flows. #### Error Handling * Throws `InvalidAddressError` if the `from` address is invalid. ### Common Errors * **InvalidAddressError** Thrown when a string/bytes input fails to parse as a valid 20-byte address. * **UnreachableCodeError** Thrown internally if a code path was unexpectedly reached. Generally wrapped into a more descriptive error. ### Other Handy Utilities Some additional lower-level utility re-exports from [`tevm/utils`](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils) or [`viem`](https://viem.sh): * `hexToBytes(hex: string): Uint8Array` Convert a hex string to raw bytes (with optional size checks). * `keccak256(data: Uint8Array | HexString, 'bytes' | 'hex')` A standard [keccak256](https://ethereum.org/en/developers/docs/smart-contracts/security/#keccak256-and-sha-3) hasher. * `encodeFunctionData(...)`, `toRlp(...)`, etc. Various encoding helpers used by Tevm Node internally. > **Tip:** If you are building higher-level code in the browser or Node, you may prefer [`viem`](https://viem.sh)'s standardized utilities for bytes conversions, hashing, and ABIs. Tevm re-exports many of these for convenience. ## @tevm/utils > **Generated API Documentation**: View the full API documentation in the [evmts/tevm-monorepo/packages/utils/docs](https://github.com/evmts/tevm-monorepo/tree/main/packages/utils/docs) folder. The `@tevm/utils` package provides a comprehensive collection of utility functions and types for working with Ethereum data structures, encoding/decoding, and common operations. It combines functionality from various Ethereum libraries and adds Tevm-specific utilities. ### Installation ```bash npm install @tevm/utils ``` ### Main Components #### Data Types and Encoding ##### Hex and Bytes Conversion ```typescript import { bytesToHex, hexToBytes, bytesToBigInt, bytesToNumber, hexToBigInt, hexToNumber, numberToHex, stringToHex, hexToString } from '@tevm/utils' // Convert bytes to hex const hex = bytesToHex(new Uint8Array([1, 164])) // '0x01a4' // Convert hex to bytes const bytes = hexToBytes('0x01a4') // Uint8Array([1, 164]) // Convert to/from numbers const num = hexToNumber('0x01a4') // 420 const hex2 = numberToHex(420) // '0x01a4' // String conversion const str = hexToString('0x48656c6c6f') // 'Hello' const hex3 = stringToHex('Hello') // '0x48656c6c6f' ``` ##### Type Checking ```typescript import { isHex, isBytes, isAddress } from '@tevm/utils' isHex('0x123') // true isBytes(new Uint8Array()) // true isAddress('0x123...') // true ``` #### Ethereum Specific ##### Unit Conversion ```typescript import { formatEther, parseEther, formatGwei, parseGwei } from '@tevm/utils' // Convert wei to ether formatEther(1000000000000000000n) // '1.0' // Convert ether to wei parseEther('1.0') // 1000000000000000000n // Work with gwei formatGwei(1000000000n) // '1.0' parseGwei('1.0') // 1000000000n ``` ##### Cryptographic Functions ```typescript import { keccak256, ecrecover, ecsign, randomBytes } from '@tevm/utils' // Generate keccak256 hash const hash = keccak256('0x1234') // Sign data const signature = ecsign(messageHash, privateKey) // Recover address from signature const address = ecrecover(messageHash, v, r, s) // Generate random bytes const random = randomBytes(32) ``` #### ABI Encoding/Decoding ```typescript import { encodeAbiParameters, decodeAbiParameters, encodeFunctionData, decodeFunctionData, encodeEventTopics, decodeEventLog } from '@tevm/utils' // Encode function data const data = encodeFunctionData({ abi: [...], functionName: 'transfer', args: [address, amount] }) // Decode function data const result = decodeFunctionData({ abi: [...], data: '0x...' }) // Work with events const topics = encodeEventTopics({ abi: [...], eventName: 'Transfer', args: [from, to, null] }) ``` #### RLP Encoding/Decoding ```typescript import { toRlp, fromRlp } from '@tevm/utils' // Encode to RLP const rlp = toRlp(['0x123', '0x456']) // Decode from RLP const decoded = fromRlp(rlp) ``` #### Memory Database ```typescript import { createMemoryDb } from '@tevm/utils' // Create an in-memory database const db = createMemoryDb() // Initialize with existing data const initialData = new Map() const db2 = createMemoryDb(initialData) ``` #### Event Emitter ```typescript import { AsyncEventEmitter } from '@tevm/utils' const emitter = new AsyncEventEmitter() // Add listener emitter.on('event', async (data) => { // Handle event }) // Emit event await emitter.emit('event', data) ``` ### Types #### Basic Types ```typescript import type { Address, Hex, BlockTag, BlockNumber, BytesLike, BigIntLike } from '@tevm/utils' // Example type usage const address: Address = '0x...' const hex: Hex = '0x...' const blockTag: BlockTag = 'latest' ``` #### ABI Types ```typescript import type { Abi, AbiFunction, AbiEvent, AbiConstructor, ParseAbi, FormatAbi } from '@tevm/utils' // Parse ABI type ParsedAbi = ParseAbi // Format ABI type FormattedAbi = FormatAbi ``` #### Contract Types ```typescript import type { ContractFunctionName, ContractConstructorArgs, ExtractAbiFunction, ExtractAbiEvent } from '@tevm/utils' // Extract function from ABI type TransferFunction = ExtractAbiFunction // Extract event from ABI type TransferEvent = ExtractAbiEvent ``` ### Constants ```typescript import { GWEI_TO_WEI, KECCAK256_RLP, KECCAK256_RLP_ARRAY } from '@tevm/utils' // Common conversion factors and constants console.log(GWEI_TO_WEI) // 1000000000n ``` ### Error Handling The package uses the `@tevm/errors` package for standardized error handling: ```typescript import { invariant } from '@tevm/utils' // Assert conditions invariant(condition, 'Error message') ``` ### See Also * [Viem Documentation](https://viem.sh/docs/utilities/fromBytes) * [EthereumJS Util Documentation](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util) * [ABI Specification](https://docs.soliditylang.org/en/latest/abi-spec.html) ## @tevm/vm The `@tevm/vm` package provides a high-performance Ethereum Virtual Machine (EVM) implementation specifically designed for Tevm. It extends the functionality of the base EVM with additional features for testing, debugging, and development purposes. ### Installation ```bash npm install @tevm/vm ``` ### Overview The VM package is a core component of Tevm that handles the execution of EVM bytecode, transaction processing, and block building. It provides a robust set of tools for: * Executing EVM bytecode and smart contracts * Processing transactions and blocks * Managing state transitions * Supporting various hardforks and EIPs * Debugging and profiling execution ### API Reference #### Core Types ##### Vm The main VM type that extends the base VM with Tevm-specific functionality: * [`Vm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/Vm.ts#L6) - Core VM type with methods for block building, transaction execution, and state management ##### Block Building * [`BlockBuilder`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BlockBuilder.ts) - Class for building and managing blocks * [`BuildBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/buildBlock.ts#L5) - Function type for block building operations * [`BuildBlockOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BuildBlockOpts.ts) - Options for block building * [`BuildStatus`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/BuildStatus.ts) - Enumeration of block building states ##### Transaction Processing * [`RunTx`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/runTx.ts) - Function type for transaction execution * [`RunTxOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunTxOpts.ts) - Options for transaction execution * [`RunTxResult`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunTxResult.ts) - Result of transaction execution ##### Block Processing * [`RunBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/runBlock.ts) - Function type for block execution * [`RunBlockOpts`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunBlockOpts.ts) - Options for block execution * [`RunBlockResult`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/RunBlockResult.ts) - Result of block execution ##### Events * [`AfterTxEvent`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/events/AfterTxEvent.ts) - Event emitted after transaction execution * [`AfterBlockEvent`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/events/AfterBlockEvent.ts) - Event emitted after block execution * [`VMEvents`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/utils/VMEvents.ts) - VM event types and handlers #### Core Functions ##### VM Creation and Management * [`createVm`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/createVm.js#L11) - Creates a new VM instance * [`deepCopy`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/deepCopy.js#L20) - Creates a deep copy of a VM instance ##### Block Operations * [`applyBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/applyBlock.ts#L24) - Applies a block to the current state * [`buildBlock`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/buildBlock.ts#L8) - Creates a new block builder instance * [`genTxTrie`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/genTxTrie.ts#L6) - Generates transaction trie for a block ##### Transaction Operations * [`validateRunTx`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/validateRunTx.js#L10) - Validates transaction parameters before execution ##### State Management * [`applyDAOHardfork`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/applyDAOHardfork.js) - Applies the DAO hardfork state changes * [`execHardfork`](https://github.com/evmts/tevm-monorepo/blob/main/packages/vm/src/actions/execHardfork.js#L14) - Executes hardfork-specific operations ### Usage Examples #### Creating a VM Instance ```typescript import { createVm } from '@tevm/vm' import { Common } from '@tevm/common' const common = new Common({ chain: 'mainnet' }) const vm = createVm({ common }) ``` #### Building and Executing a Block ```typescript import { createVm, BlockBuilder } from '@tevm/vm' const vm = createVm({ /* options */ }) const blockBuilder = await vm.buildBlock({ parentBlock: block, blockOpts: { /* options */ } }) // Add transactions to the block await blockBuilder.addTransaction(tx) // Build and execute the block const block = await blockBuilder.build() const result = await vm.runBlock({ block }) ``` #### Executing a Transaction ```typescript import { createVm } from '@tevm/vm' const vm = createVm({ /* options */ }) const txResult = await vm.runTx({ tx }) console.log('Gas used:', txResult.gasUsed.toString()) console.log('Return value:', txResult.execResult.returnValue) ``` ### Error Handling The VM package throws specific error types for different failure scenarios: * Transaction execution errors (invalid nonce, insufficient balance, etc.) * Block validation errors (invalid state root, gas limit, etc.) * VM execution errors (out of gas, invalid opcode, etc.) Example error handling: ```typescript try { await vm.runTx({ tx }) } catch (error) { if (error.code === 'INVALID_OPCODE') { console.error('Invalid operation in contract code') } else if (error.code === 'OUT_OF_GAS') { console.error('Transaction ran out of gas') } } ``` ### Configuration The VM can be configured with various options through the `VMOpts` interface: ```typescript const vm = createVm({ common, // Chain configuration stateManager, // Custom state manager blockchain, // Custom blockchain activatePrecompiles: true, // Activate precompiled contracts // ... other options }) ``` ### Related Packages * [@tevm/state](./state) - State management for the VM * [@tevm/common](./common) - Chain configuration and utilities * [@tevm/blockchain](./blockchain) - Blockchain management ### License MIT ## Tevm Contract Bundler The Tevm Contract Bundler allows you to import Solidity files directly into your TypeScript code, enabling a seamless development experience with type safety and IDE integration. :::note Tevm Node is the primary product. Using the bundler is optional. You can also generate contract types with `npx tevm gen` (see [Codegen Approach](/reference/bundler/troubleshooting#codegen-approach)). ::: ### Quick Navigation The bundler documentation is split into several sections for better organization: * **[Overview](/reference/bundler/overview)** - Introduction, key benefits, available plugins * **[Internals](/reference/bundler/internals)** - How the bundler works under the hood * **[Methods & Exports](/reference/bundler/methods)** - Key APIs for advanced usage * **[Troubleshooting](/reference/bundler/troubleshooting)** - Common issues and solutions ### What is the Tevm Bundler? The Tevm bundler transforms Solidity `.sol` files into TypeScript modules at build time. When you import a contract, the bundler: 1. Reads and compiles the Solidity source code 2. Extracts the ABI, bytecode, and other contract information 3. Generates a TypeScript module with a [Tevm Contract](/reference/contract) instance This means you can interact with your contracts in a fully type-safe way, with editor features like auto-completion, go-to-definition, and inline documentation. ### Getting Started For a quick introduction to using the bundler, see the [Bundler Quickstart](/getting-started/bundler) guide. If you're looking for detailed reference information, explore the sections above. ### Coming Soon Features Tevm is actively developing new bundler features: * **Inline Solidity with `sol` Tag** - Define contracts directly in your JS/TS using template literals * **CAIP-10 Contract Imports** - Import contracts from any chain using standardized identifiers Learn more about these upcoming features in the [Upcoming Features](/reference/bundler/overview#coming-soon-features) section. ## Bundler Internals This page explains the internal workings of the Tevm bundler to help you understand how it transforms Solidity contracts into TypeScript modules. ### Architecture Overview All Tevm bundler plugins share a unified core (`@tevm/base-bundler`). This architecture ensures consistent behavior across different build tools while allowing each plugin to adapt to its specific environment. The bundler pipeline consists of several key stages: ### 1. Import Detection & Resolution When the bundler encounters a `.sol` import, it: * Parses the import statement using regex to extract the import path * Converts relative paths to absolute paths * Applies custom remappings from various sources (tsconfig.json paths, foundry remappings, tevm.config.json) * Handles node\_modules resolution for npm packages * Supports special import formats like `.s.sol` for bytecode inclusion The import resolution mechanism merges multiple sources of configuration: ``` User Import โ†’ Node.js Resolution + Foundry Remappings โ†’ Absolute File Path ``` This allows the bundler to support both traditional JavaScript module resolution and Solidity/Foundry-style import resolution simultaneously. ### 2. Compilation Once all imports are resolved, the bundler: * Creates a dependency graph of all imported Solidity files * Passes the full source code and import structure to solc (the Solidity compiler) * If the file ends in `.s.sol`, both ABI and bytecode are generated; otherwise only ABI is produced * Extracts metadata, NatSpec documentation, and optionally the AST (Abstract Syntax Tree) The compilation step handles error reporting and version management for the Solidity compiler. ### 3. Code Generation After compilation, the bundler: * Generates a TypeScript (or JavaScript) module that exports a Tevm Contract instance * Creates type definitions for all contract methods, events, and errors * Maps Solidity types to TypeScript types (uint256 โ†’ bigint, address โ†’ string, etc.) * Adds metadata, ABI, and optionally bytecode as properties of the exported contract * Generates `.read` and `.write` method interfaces for type-safe contract interaction The code generation step preserves NatSpec documentation as JSDoc comments, allowing editors to display contract documentation on hover. ### 4. Caching To improve build performance, the bundler: * Stores compiled artifacts in a `.tevm` directory * Caches based on file content hashes, not just timestamps * Avoids unnecessary recompilation when source files haven't changed * Stores intermediate artifacts (ABI, bytecode) separately from generated code * Handles cache invalidation when dependencies or compiler settings change The caching system balances build speed with correctness by selectively invalidating only affected parts of the cache. ### 5. LSP & TS Plugin Integration The final piece of the bundler architecture is the TypeScript Language Service Plugin: * The `@tevm/ts-plugin` hooks into the TypeScript compiler * References bundler outputs to provide type information for `.sol` imports * Enables advanced IDE features: * Contract-level auto-completion * Go-to-definition for contract methods * Hover documentation showing NatSpec comments * Type checking for contract method arguments and return values The plugin bridges the gap between the build-time bundler process and the development-time editor experience. ### Internal Implementation Details #### Module Factory The bundler uses a module factory pattern to create a dependency graph of all Solidity files: ```ts // Internal implementation (simplified) function moduleFactory(entry, source, remappings, libs) { const modules = new Map() const queue = [{ path: entry, source }] while (queue.length > 0) { const { path, source } = queue.shift() if (modules.has(path)) continue // Find all imports in the source const imports = resolveImports(path, source, remappings, libs) // Add to module map modules.set(path, { id: path, source, imports: imports.map(i => i.path) }) // Queue imports for processing for (const imp of imports) { if (!modules.has(imp.path)) { queue.push({ path: imp.path, source: readFile(imp.path) }) } } } return modules } ``` #### Solidity Compiler Integration The bundler interfaces with the Solidity compiler through a standardized API: ```ts // Internal implementation (simplified) function compile(sources, settings) { const input = { language: 'Solidity', sources: Object.fromEntries( Array.from(sources.entries()).map( ([path, { content }]) => [path, { content }] ) ), settings: { outputSelection: { '*': { '*': ['abi', 'evm.bytecode', 'evm.deployedBytecode', 'metadata', 'userdoc', 'devdoc'] } }, ...settings } } return solc.compile(JSON.stringify(input), { import: resolveImport }) } ``` #### Contract Instance Generation The bundler generates a TypeScript module that exports a Tevm Contract instance: ```ts // Generated code (simplified) import { createContract } from 'tevm/contract' export const MyContract = createContract({ abi: [...], // ABI from compiler bytecode: '0x...', // Optional, only for .s.sol files deployedBytecode: '0x...', // Optional, only for .s.sol files }) // Strongly typed methods available via: // MyContract.read.methodName // MyContract.write.methodName ``` ### Further Reading * [Methods & Exports](/reference/bundler/methods) - Learn about the key APIs exposed by the bundler * [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler ## Notable Methods & Exports Beyond the high-level bundler plugins, Tevm exposes several internal utility methods and functions for advanced use cases. These are useful if you're building custom bundling workflows, debugging compilation issues, or extending Tevm's functionality. ### Core Bundler (`@tevm/base-bundler`) The base bundler handles the complete process of transforming Solidity files into JavaScript/TypeScript modules: ```ts import { bundler } from '@tevm/base-bundler' import { createCache } from '@tevm/bundler-cache' import { createSolc } from '@tevm/solc' import { loadConfig } from '@tevm/config' // Create a bundler instance const tevmBundler = bundler( config, // from loadConfig() console, // logger fileAccessObj, // file system access solcInstance, // from createSolc() cacheInstance, // from createCache() ) // Convert a Solidity file to TypeScript const { code, modules } = await tevmBundler.resolveTsModule( 'MyContract.s.sol', process.cwd(), false, // includeAst true // includeBytecode ) ``` The `bundler()` factory creates an object with methods for different output formats: * `resolveTsModule` - Generates TypeScript * `resolveEsmModule` - Generates ES modules * `resolveCjsModule` - Generates CommonJS * `resolveDts` - Generates TypeScript declarations Each method also has a synchronous version with `Sync` suffix (e.g., `resolveTsModuleSync`). [Source: bundler.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/base-bundler/src/bundler.js) ### Import Resolution (`@tevm/resolutions`) The resolutions package provides tools for parsing and resolving Solidity import statements: ```ts import { moduleFactory, resolveImports } from '@tevm/resolutions' // Find all imports in a Solidity file const imports = await resolveImports( '/path/to/Contract.sol', // absolute path contractSourceCode, // raw source code { '@openzeppelin/': 'node_modules/@openzeppelin/' }, // remappings ['lib', 'node_modules'], // library search paths false // async mode ) // Build a module dependency graph const modules = await moduleFactory( '/path/to/Contract.sol', contractSourceCode, remappings, libraryPaths, fileAccessObj, false // async mode ) ``` * `resolveImports()` - Parses import statements and resolves them to absolute file paths * `moduleFactory()` - Builds a complete dependency graph of Solidity modules [Source: moduleFactory.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/resolutions/src/moduleFactory.js) | [Source: resolveImports.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/resolutions/src/resolveImports.js) ### Compilation (`@tevm/compiler`) The compiler package handles the interaction with the Solidity compiler: ```ts import { resolveArtifacts } from '@tevm/compiler' // Compile a Solidity file and generate artifacts const { artifacts, modules, asts } = await resolveArtifacts( 'MyContract.s.sol', // entry point process.cwd(), // base directory console, // logger config, // compiler config true, // include AST true, // include bytecode fileAccessObj, // file access solcInstance // solc compiler instance ) // Result includes: console.log(artifacts) // Contract artifacts with ABI, bytecode, etc. console.log(modules) // Module dependency graph console.log(asts) // Abstract Syntax Tree (if requested) ``` * `resolveArtifacts()` - Compiles a Solidity file and its dependencies into ABI, bytecode, and AST * Also has a synchronous version: `resolveArtifactsSync()` [Source: resolveArtifacts.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/compiler/src/resolveArtifacts.js) ### Caching (`@tevm/bundler-cache`) The cache system improves build performance by storing compiled artifacts: ```ts import { createCache } from '@tevm/bundler-cache' // Create a cache instance const cache = createCache( '.tevm', // cache directory fileAccessObj, // file system access process.cwd() // current working directory ) // Read/write artifacts and generated code await cache.writeArtifacts(contractPath, compiledContracts) const artifacts = await cache.readArtifacts(contractPath) await cache.writeDts(contractPath, dtsContent) const dts = await cache.readDts(contractPath) ``` The cache provides methods for reading and writing various types of files: * `readArtifacts`/`writeArtifacts` - Compiled contract artifacts (ABI, bytecode, etc.) * `readDts`/`writeDts` - TypeScript declaration files * `readMjs`/`writeMjs` - ESM JavaScript files * `readCjs`/`writeCjs` - CommonJS JavaScript files Each method also has a synchronous version with `Sync` suffix. [Source: createCache.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/bundler-cache/src/createCache.js) ### Configuration (`@tevm/config`) The config package handles loading and merging configuration from various sources: ```ts import { loadConfig } from '@tevm/config' // Load configuration from tevm.config.json, tsconfig.json, foundry.toml, etc. const config = await loadConfig(process.cwd()) console.log(config.remappings) // Merged remappings from all sources console.log(config.libs) // Library search paths console.log(config.cacheDir) // Cache directory ``` * `loadConfig()` - Loads and merges configuration from multiple sources * Handles Foundry remappings, TypeScript config, and tevm.config.json [Source: loadConfig.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/config/src/loadConfig.js) ### Runtime (`@tevm/runtime`) The runtime package generates the actual TypeScript/JavaScript code for compiled Solidity contracts: ```ts import { generateTevmBody, generateTevmBodyDts } from '@tevm/runtime' // Generate TypeScript implementation const tsCode = generateTevmBody( 'MyContract', // contract name artifacts, // compilation artifacts 'tevm/contract', // import path for createContract true, // include bytecode ) // Generate TypeScript declaration const dtsCode = generateTevmBodyDts( 'MyContract', // contract name artifacts, // compilation artifacts 'tevm/contract', // import path for createContract true, // include bytecode ) ``` * `generateTevmBody()` - Generates TypeScript/JavaScript implementation * `generateTevmBodyDts()` - Generates TypeScript declaration file (.d.ts) [Source: generateTevmBody.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/runtime/src/generateTevmBody.js) | [Source: generateTevmBodyDts.js](https://github.com/evmts/tevm-monorepo/blob/main/bundler-packages/runtime/src/generateTevmBodyDts.js) ### Creating a Custom Bundler If you need to create a custom bundler pipeline, you can combine these utilities: ```ts import { loadConfig } from '@tevm/config' import { createSolc } from '@tevm/solc' import { createCache } from '@tevm/bundler-cache' import { bundler } from '@tevm/base-bundler' import { resolveArtifacts } from '@tevm/compiler' import fs from 'node:fs/promises' // Step 1: Create a file access object const fileAccess = { readFile: (path, enc) => fs.readFile(path, { encoding: enc }), writeFile: fs.writeFile, // Add other required methods... } // Step 2: Load configuration const config = await loadConfig(process.cwd()) // Step 3: Create a cache const cache = createCache('.tevm', fileAccess, process.cwd()) // Step 4: Create a solc instance const solc = await createSolc() // Step 5: Create a bundler const myBundler = bundler(config, console, fileAccess, solc, cache) // Step 6: Process a Solidity file const result = await myBundler.resolveTsModule( 'path/to/Contract.sol', process.cwd(), false, true ) // Step 7: Use the generated code console.log(result.code) // TypeScript code for Contract ``` ### Further Reading * [Bundler Internals](/reference/bundler/internals) - Learn how the bundler works under the hood * [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler * [GitHub Repository](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages) - Source code for all bundler packages ## Bundler Overview ### Key Benefits The Tevm bundler offers numerous advantages for Ethereum developers: * **Direct Imports**: Import Solidity files just like any other TypeScript module * **Type Safety**: Full TypeScript type checking for contract methods, arguments, and return values * **IDE Integration**: Go-to-definition, auto-completion, and hover documentation * **NatSpec Support**: Contract documentation appears in your editor * **Hot Module Reloading**: Changes to Solidity files update immediately during development * **Framework Integration**: Works with popular bundlers like Vite, Webpack, Next.js, Bun, and more ### Available Plugins Tevm provides plugins for most popular JavaScript bundlers: | Bundler | Plugin Import Path | Repository | | ------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | | Vite | `tevm/bundler/vite-plugin` | [@tevm/vite-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/vite) | | Webpack | `tevm/bundler/webpack-plugin` | [@tevm/webpack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/webpack) | | Rollup | `tevm/bundler/rollup-plugin` | [@tevm/rollup-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rollup) | | ESBuild | `tevm/bundler/esbuild-plugin` | [@tevm/esbuild-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/esbuild) | | Bun | `tevm/bundler/bun-plugin` | [@tevm/bun-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/bun) | | Rspack | `tevm/bundler/rspack-plugin` | [@tevm/rspack-plugin](https://github.com/evmts/tevm-monorepo/tree/main/bundler-packages/rspack) | All plugins share a similar usage pattern and a single configuration interface. For detailed plugin configuration, see the [Quickstart Guide](/getting-started/bundler). ### Prerequisites & Key Points #### Optional Bundler You can compile contracts manually or use `npx tevm gen`. The plugins are purely optional if you prefer a different workflow. #### TypeScript Integration For best results, add the Tevm TS plugin to your tsconfig.json. This allows your editor to resolve Solidity imports, provide NatSpec docs on hover, go-to-definition on solidity methods, and more. #### .s.sol for Bytecode By default, Tevm only generates bytecode for Solidity files ending in `.s.sol`. Regular `.sol` files still compile for ABIs, but omit deployable bytecode. This helps differentiate purely interface-like contracts from ones you intend to deploy or call as scripts. #### Tevm Cache Compiled artifacts and metadata are stored in a `.tevm` folder. It is strongly recommended to `.gitignore` this directory. The cache directory is ephemeral - you can safely delete it if you encounter stale builds or caching issues. #### Foundry/Remappings If you have a Foundry setup or custom remappings, you can enable them in a `tevm.config.json`. For detailed configuration, see the [Bundler Configuration](#bundler-configuration) section. #### Next.js Next.js can conflict with the TS plugin's type-checking. If you wish to keep type-checking turned on for Next.js, consider the [Codegen Approach](/reference/bundler/troubleshooting#codegen-approach). Otherwise, you may disable type-checking in your Next config. ### Basic Usage Once configured, you can import Solidity contracts directly: ```ts // Import a contract directly from a .sol file import { Counter } from './Counter.sol' import { createMemoryClient } from 'tevm' // Create a client to interact with the contract const client = createMemoryClient() // Deploy the contract const deployed = await client.deployContract(Counter) // Call read methods const count = await deployed.read.count() // Call write methods const tx = await deployed.write.increment() await client.mine({ blocks: 1 }) // Check the updated state const newCount = await deployed.read.count() ``` The imported `Counter` object is a [Tevm Contract](/reference/contract) instance that provides: * Type-safe access to all contract methods * Contract ABI and bytecode * Deployment helpers * Read and write method interfaces * Event filters ### Importing Dependencies You can import contracts from: * Local `.sol` files in your project * npm packages like OpenZeppelin contracts * External libraries ```ts // Local import import { MyToken } from './contracts/MyToken.sol' // npm package import import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol' ``` ### Integration with Viem and Ethers Tevm contracts work seamlessly with popular Ethereum libraries: #### Viem Example ```ts import { createPublicClient, http } from 'viem' import { mainnet } from 'viem/chains' import { MyToken } from './contracts/MyToken.sol' const client = createPublicClient({ chain: mainnet, transport: http(), }) // Read methods const balance = await client.readContract({ ...MyToken.read.balanceOf('0x123...'), address: '0x456...', }) // Write methods const hash = await client.writeContract({ ...MyToken.write.transfer('0x789...', 1000n), address: '0x456...', }) ``` #### Ethers Example ```ts import { ethers } from 'ethers' import { MyToken } from './contracts/MyToken.sol' const provider = new ethers.BrowserProvider(window.ethereum) const signer = await provider.getSigner() // Create contract instance const tokenContract = new ethers.Contract( '0x456...', MyToken.abi, signer ) // Call methods const balance = await tokenContract.balanceOf('0x123...') const tx = await tokenContract.transfer('0x789...', 1000n) ``` ### Bundler Configuration For complex projects, create a `tevm.config.json` file in your project root: ```json { "foundryProject": true, "libs": ["lib", "node_modules"], "remappings": { "@openzeppelin/": "node_modules/@openzeppelin/" }, "debug": false, "cacheDir": ".tevm", "jsonAsConst": ["**/*.abi.json"] } ``` #### Configuration Options | Option | Type | Description | | ---------------- | ------------------------ | ---------------------------------------------------------------- | | `foundryProject` | `boolean \| string` | Enable Foundry integration (`true`) or path to foundry.toml | | `libs` | `string[]` | Library paths for Solidity imports (used alongside Foundry libs) | | `remappings` | `Record` | Custom import remappings | | `debug` | `boolean` | Output extra debug logs and files in `.tevm` | | `cacheDir` | `string` | Location for compiled artifacts (default: `.tevm`) | | `jsonAsConst` | `string \| string[]` | Glob patterns for JSON files to be typed as const in TS | ### Coming Soon Features #### Inline Solidity with `sol` Tag :::info This feature is coming soon and is not yet available in the current release. ::: For quick prototyping or one-off contracts, you'll soon be able to use the `sol` tag to define Solidity code inline: ```ts import { sol } from 'tevm' // Define contract inline const { bytecode, abi } = sol` pragma solidity ^0.8.19; contract Counter { uint256 private count = 0; function increment() public { count += 1; } function count() public view returns (uint256) { return count; } } ` // Use with client.readContract, writeContract, etc. ``` #### CAIP-10 Contract Imports :::info This feature is coming soon and is not yet available in the current release. ::: Tevm will soon support importing contracts based on the CAIP-10 Account ID Specification, which will allow you to import contracts from any chain using a standardized identifier format: ```ts // Import a contract from Ethereum mainnet using a CAIP-10 identifier import { WETH } from 'caip10:eip155:1:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' ``` ### Further Reading * [Bundler Internals](/reference/bundler/internals) - Learn how the bundler works under the hood * [Methods & Exports](/reference/bundler/methods) - Advanced APIs for custom implementations * [Troubleshooting](/reference/bundler/troubleshooting) - Solve common issues with the bundler ## Bundler Troubleshooting This guide addresses common issues that may arise when using the Tevm bundler. ### Common Issues #### 1. Missing or Red Underlines in Editor **Symptoms**: Your editor shows red underlines for Solidity imports, or auto-completion doesn't work. **Solution**: * Confirm you have `"plugins": [{ "name": "@tevm/ts-plugin" }]` in tsconfig.json * In VS Code or Cursor, switch to the "workspace version" of TypeScript: 1. Open Command Palette (Ctrl+Shift+P or Cmd+Shift+P) 2. Type "TypeScript: Select TypeScript Version" 3. Select "Use Workspace Version" * For Vim, Neovim, and other editors, ensure they're using the workspace TypeScript version #### 2. Type-check Errors with Next.js **Symptoms**: Your build works, but Next.js type-checking fails with errors about `.sol` imports. **Solution**: * Next's built-in type checker might not handle dynamic `.sol` imports well * Option 1: Disable type-checking in next.config.mjs: ```js // next.config.mjs export default { typescript: { // Typechecking will only be available after the LSP is migrated to volar // Until then typechecking will work in editor but not during a next.js build ignoreBuildErrors: true, } } ``` * Option 2: Use the [Codegen Approach](#codegen-approach) (recommended for Next.js) #### 3. "File Not Found" Errors **Symptoms**: Bundler can't find imported Solidity files, especially with custom paths. **Solution**: * Check that your libraries or local imports are accounted for in libs or remappings * If you're using Foundry, ensure `foundryProject: true` is set in tevm.config.json * For npm packages, verify they're installed and the import path is correct * For complex import structures, add explicit remappings: ```json // tevm.config.json { "remappings": { "@customlib/": "node_modules/@customlib/", "local/": "./contracts/" } } ``` #### 4. Cache Stale Issues **Symptoms**: Changes to Solidity files don't appear to take effect. **Solution**: * If a contract's changes don't appear to be recognized, remove the `.tevm` folder and rebuild * The `.tevm` directory is ephemeral - you can safely delete it at any time * Add `.tevm` to your `.gitignore` to prevent caching issues in version control #### 5. No Bytecode Available **Symptoms**: Contract deployment fails with errors about missing bytecode. **Solution**: * Check that you're using the `.s.sol` extension for contracts you want to deploy * Regular `.sol` files only generate ABIs, not deployable bytecode * Rename your file from `Contract.sol` to `Contract.s.sol` if you need bytecode #### 6. Deployment Errors **Symptoms**: Contract deployment fails even with bytecode available. **Solution**: * Make sure you're calling `.deploy()` with any required constructor arguments: ```ts // Incorrect (when contract has constructor args) const deployed = await client.deployContract(MyToken) // Correct const deployed = await client.deployContract( MyToken.deploy("TokenName", "TKN", 18) ) ``` #### 7. Test Runner Issues **Symptoms**: Tests using `.sol` imports fail in Jest or other test runners. **Solution**: * Most test runners (Vitest) work out-of-the-box once the bundler plugin is configured * For Jest, you might need extra configuration or use the codegen approach * Consider using the bundler that matches your test environment (esbuild for Vitest, etc.) ### Codegen Approach If you're encountering persistent bundler-related issues, particularly with frameworks like Next.js, or if you prefer to have explicit TypeScript files for your contracts, the codegen approach may be better suited for your needs. #### What is Codegen? The codegen approach generates `.ts` files from your `.sol` files ahead of time, rather than during the build process. This results in regular TypeScript files that can be imported normally. #### Using Codegen To generate TypeScript files for your Solidity contracts: ```bash npx tevm gen ``` This will: * Generate `.ts` files next to each `.sol` file (or wherever configured) * Create TypeScript files that you can commit to source control * Produce the same output as the bundler, but in a pre-build step These generated `.ts` files can be imported directly in your code: ```ts // Import the generated TypeScript file import { MyContract } from './contracts/MyContract.js' ``` #### When to Use Codegen Codegen is especially helpful when: * You have a framework that tightly controls its build pipeline (e.g., Next.js with enforced type-checking) * You prefer explicit, committed TypeScript artifacts for contracts * You want a stable CI pipeline or want to avoid runtime resolution * You're using Jest or another test runner that has difficulty with dynamic imports #### Codegen Configuration You can configure the codegen tool by adding options to your `tevm.config.json`: ```json { "gen": { "outDir": "./generated", // Where to generate files "patterns": ["contracts/**/*.sol"], // Which files to process "exclude": ["**/node_modules/**"] // Which files to ignore } } ``` ### Additional Resources If you're still experiencing issues: * Check the [GitHub Issues](https://github.com/evmts/tevm-monorepo/issues) for similar problems and solutions * Look at the [Examples](https://github.com/evmts/tevm-monorepo/tree/main/examples) for working configurations * Join the Tevm community on [Discord](https://discord.gg/tevm) for direct assistance * For Next.js specific issues, see the [Next.js example](https://github.com/evmts/tevm-monorepo/tree/main/examples/next) in the Tevm repository ### Further Reading * [Bundler Overview](/reference/bundler/overview) - Key benefits and features * [Bundler Internals](/reference/bundler/internals) - How the bundler works * [Methods & Exports](/reference/bundler/methods) - Advanced APIs and utilities