# 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.
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.
Quick testing and development
Immediate transaction confirmation
Simulating instant finality
```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.
Only mines blocks when explicitly requested, giving you complete control over block production.
Complex test scenarios
Precise control over block timing
Testing mempool behavior
```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.
Testing gas-dependent behavior
Simulating block fullness scenarios
Performance testing with varying load
:::
### 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 codeWorking with pending transactionsAdvanced account management and impersonationCreating and managing event filters and subscriptionsAdding 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 networksUnderstand the state management capabilities of TevmExplore the different block mining strategiesUse Tevm's Ethereum-compatible JSON-RPC interfaceWork with low-level EVM execution eventsAdd 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:
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.
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:
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.
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 ๐."
"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."
### 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
```
### 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
Opcode
Count
Total Gas
{#each [...gasProfile] as [opcode, stats]}
{opcode}
{stats.count}
{stats.totalGas.toString()}
{/each}
```
### 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 gasSend transactions, sign messages, and interact with accountsManipulate blockchain state for testing and developmentTevm-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-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.
### 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