Revm

CI License crates.io Chat

Revm is a highly efficient and stable implementation of the Ethereum Virtual Machine (EVM) written in Rust.

banner

Known for its robustness, it stands as one of the most popular libraries and a critical component of the Ethereum ecosystem. Revm plays a crucial role across various projects, being widely utilized by almost all tooling and block builders. It is integrated into Reth, multiple Layer 2 variants and other clients and serving as a standard for zkVMs.

Revm offers two primary applications: firstly, it functions as an executor where users can set up block info and process mainnet transactions; secondly, it acts as a framework that facilitates the extension and support of different EVM variants such as op-revm.

How to use:

Here is a straightforward example of using the Execution API: It allows us to create an Ethereum Virtual Machine (EVM) and execute transactions. Additionally, it can be utilized to generate traces with the inspector or more complex example of foundry cheatcodes.

let mut evm = Context::mainnet().with_block(block).build_mainnet();
let out = evm.transact(tx);

// or you can use powerful inspection tool to trace it
let mut evm = evm.with_inspector(tracer);
let out = evm.inspect_tx(tx);

The Evm Framework API is somewhat complex to use, but this document provides a detailed explanation. It enables users to extend logic, incorporate various context types, and offers built-in support for inspection. For a practical example, you can refer to the op-revm crate.

Users:

As previously noted, there are several groups of projects that utilize this technology:

The full list of projects that use Revm is available in the awesome-revm section of the book.

How to, dev section

The book and Architecture and API page is the best starting resource.

Some quick links can be found here. Some point to code documentation or the book. code docs are there to explain usage of a particular part of the code where the book is to get more of an overview of the architecture or how components/projects fit together.

  • How to build and use revm can be found here. book
  • Architecture overview can be seen here. book
  • Structure of the project (list of crates and their versions) can be seen here. book
  • How to use Revm Framework can be found in MyEvm example. book
  • Release procedure and changelogs explanation. book
  • How to use revme (Revm binary with few commands) can be found here. code
  • How to run Ethereum test can be found here: book
  • If there is more need for explanations please open a PR request.

Community:

For questions please open a github issue or join the public telegram group: https://t.me/+Ig4WDWOzikA3MzA0

Licence

Revm is licensed under MIT Licence.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, shall be licensed as above, without any additional terms or conditions.

If gmp feature flag is used, GPL code gets compiled, if enabled please make sure to follow this license.

Security

For any security questions or findings, please reach out to me directly via email at dragan0rakita@gmail.com or contact me on Keybase under the username @draganrakita.

Awesome Revm

A curated list of excellent Revm-related resources. Feel free to contribute to this list!

Repositories

Audits

API Extensions:

  • alloy-evm is an abstraction layer on top of revm providing common implementations of EVMs
  • Trevm is a typestate API wrapper for revm

Clients

  • Reth: A modular, contributor-friendly, and ultra-fast implementation of the Ethereum protocol.
  • Helios is a trustless, efficient, and portable multichain light client written in Rust.
  • Trin is a Rust implementation of a Portal Network client.

EVM Variants

  • Optimism is a ethereum L2 network.
  • Base is an Ethereum Layer 2 (L2) chain that offers a safe, low-cost, developer-friendly way to build on-chain
  • Scroll is its own Layer 2 network built on Ethereum (more specifically, a “zero-knowledge rollup”).

Tools

  • Foundry: A portable and modular toolkit for rapid Ethereum application development in Rust.
  • Hardhat: A comprehensive development environment for compiling, deploying, testing, and debugging Ethereum software.
  • Arbiter: smart-contract simulation.

Frameworks and Libraries

  • revm-inspectors: Hooks for EVM execution.
  • eip3074-instructions: Implements custom instructions for AUTH and AUTHCALL operations.
  • Revmc: JIT and AOT compiler for the Ethereum Virtual Machine, leveraging Revm.
  • Risc0-ethereum is a zero-knowledge verifiable general computing platform, with Ethereum integration
  • Kona is a suite of portable implementations of the OP Stack rollup state transition, namely the derivation pipeline and the block execution logic.
  • mevlog-rs: Rust-based CLI tool for querying and monitoring Ethereum blockchain transactions, with flexible filtering and EVM tracing capabilities. It's a tool for MEV searchers who prefer command-line workflows over web-based explorers.

Tutorials

Architecture and API

REVM is a flexible implementation of the Ethereum Virtual Machine (EVM). It follows the rules of the Ethereum mainnet and stays up to date with changes through hardforks as defined in the official Ethereum execution specs.

You can use REVM in two main ways:

  1. Run regular Ethereum transactions using an Execution API
  2. Create your own custom version of the EVM (for Layer 2 solutions or other chains) using the EVM framework

To see usage examples you can check the examples folder. Other than documentation, examples are the main resource to see and learn about Revm.

The main revm library combines all crates into one package and reexports them, standalone libraries are useful if there is a need to import functionality with smaller scope. You can see an overview of all revm crates in the crates folder.

REVM works in no_std environments which means it can be used in zero-knowledge virtual machines (zkVMs) and it is the standard library in that use case. It also has very few external dependencies.

Execution API

The Execution API provides the primary interface for running Ethereum transactions and interacting with the EVM. Whether you're building a blockchain client, testing framework, or analysis tool, this API offers multiple execution modes to suit your needs.

The API is designed around four key execution patterns:

  • Basic execution: Run transactions and get results
  • Execution with commit: Run transactions and automatically persist state changes
  • Execution with inspection: Run transactions with detailed tracing and observation

Evm the main structure for executing mainnet ethereum transaction is built with a Context and a builder, code for it looks like this:

let mut evm = Context::mainnet().with_block(block).build_mainnet();
let out = evm.transact(tx);

Evm struct contains:

And Context contains data used in execution:

  • Environment data, the data that is known before execution starts are Transaction, Block, Cfg.
  • Journal is the place where internal state is stored. Internal state is returned after execution ends.
    • And Database is an interface that allows fetching external data that is needed at runtime. That data are account, storage and bytecode. When loaded they are stored in Journal

REVM provides four ways to execute transactions through traits (API):

  • transact(tx) and replay() are function of ExecuteEvm trait that allow execution transactions. They return the status of execution with reason, changed state and in case of failed execution an error.
  • transact_commit(tx) and replay_commit() are part of ExecuteCommitEvm that internally commits the state diff to the database and returns status of execution. Database is required to support DatabaseCommit trait.
  • inspect(), inspect_replay(tx) and a few others are part of InspectEvm trait that allow execution with inspection. This is how tracers are called.
  • inspect_commit(),inspect_replay_commit(tx) are part of the InspectCommitEvm trait that extends InspectEvm to allow committing state diff after tracing.

For inspection API to be enabled, Evm needs to be created with inspector.

let mut evm = Context::mainnet().with_block(block).build_mainnet().with_inspector(inspector);
let _ = evm.inspect_tx(tx);

Inspector - EVM Execution Tracing

The Inspector trait is REVM's powerful mechanism for observing EVM execution. It provides hooks into every aspect of transaction execution, enabling sophisticated debugging, tracing and custom tooling.

Key capabilities include:

  • Step-by-step execution tracing: Hook into every opcode before and after execution
  • State monitoring: Track stack, memory, and storage changes in real-time
  • Call and creation tracing: Observe contract interactions and deployments
  • Event capture: Record logs, self-destructs, and other EVM events
  • Execution override: Optionally modify execution flow and outcomes

The Inspector is ideal for building debuggers, gas analyzers, security tools, testing frameworks, and any application that needs deep visibility into EVM execution. For detailed usage examples and advanced features, see the Inspector documentation.

EVM Framework

To learn how to build your own custom EVM:

Each trait needed to build custom EVM has detailed documentation explaining how it works and is worth reading.

In summary, REVM is built around several key traits that enable customizable EVM functionality. The core traits include:

  • EvmTr: The core EVM trait that provides access to Context, Instruction, Precompiles:
  • ContextTr: Accessed through EvmTr, defines the execution environment including Tx/Block/Journal/Db.
  • Handler: Implements the core execution logic, taking an EvmTr implementation. The default implementation follows Ethereum consensus.

And traits that provide support for inspection and tracing:

Inspector - EVM Execution Tracing

The Inspector trait is REVM's powerful mechanism for observing and tracing EVM execution. It provides hooks into every aspect of transaction execution, making it ideal for building debuggers, analyzers, and custom tooling.

What is the Inspector?

The Inspector trait defines callbacks that are invoked during EVM execution. It allows you to:

  • Step through execution: Hook into every opcode before and after execution
  • Monitor state changes: Track stack, memory, and storage modifications
  • Trace calls: Observe contract calls, creations, and their outcomes
  • Capture events: Record logs, self-destructs, and other EVM events
  • Override behavior: Optionally modify execution flow and results

Core Inspector Methods

pub trait Inspector<CTX, INTR: InterpreterTypes> {
    // Opcode-level tracing
    fn step(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {}
    fn step_end(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {}
    
    // Call and creation tracing  
    fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> { None }
    fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {}
    fn create(&mut self, context: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> { None }
    fn create_end(&mut self, context: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {}
    
    // Event tracing
    fn log(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX, log: Log) {}
    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {}
}

Basic Usage

1. Create an Inspector

#[derive(Default)]
struct MyInspector {
    gas_used: u64,
    call_count: usize,
}

impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for MyInspector {
    fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
        self.gas_used += interp.gas.spent();
    }
    
    fn call(&mut self, _context: &mut CTX, _inputs: &mut CallInputs) -> Option<CallOutcome> {
        self.call_count += 1;
        None // Don't override the call
    }
}

2. Use with EVM

let inspector = MyInspector::default();
let mut evm = Context::mainnet()
    .with_db(db)
    .build_mainnet_with_inspector(inspector);

// Execute with inspection
let result = evm.inspect_one_tx(tx)?;
println!("Gas used: {}", evm.inspector.gas_used);
println!("Calls made: {}", evm.inspector.call_count);

Advanced Features

State Inspection

Access complete interpreter state during execution:

fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
    let pc = interp.bytecode.pc();
    let opcode = interp.bytecode.opcode();
    let stack_len = interp.stack.len();
    let memory_size = interp.memory.size();
    
    println!("PC: {}, Opcode: 0x{:02x}, Stack: {}, Memory: {}", 
             pc, opcode, stack_len, memory_size);
}

Call Override

Modify execution by returning custom outcomes:

fn call(&mut self, _context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
    if inputs.target_address == SPECIAL_ADDRESS {
        // Override this call with custom logic
        return Some(CallOutcome::new(
            InterpreterResult::new(InstructionResult::Return, Bytes::from("custom")),
            0..0
        ));
    }
    None // Let normal execution continue
}

Event Logging

Capture and process EVM events:

fn log(&mut self, _interp: &mut Interpreter<INTR>, _ctx: &mut CTX, log: Log) {
    println!("LOG emitted from: {:?}", log.address);
    println!("Topics: {:?}", log.topics());
    println!("Data: {}", hex::encode(log.data.data));
}

Built-in Inspectors

REVM provides several ready-to-use inspectors:

  • GasInspector: Tracks gas consumption throughout execution
  • TracerEip3155: Generates EIP-3155 compatible execution traces
  • NoOpInspector: Default no-operation inspector for when inspection is disabled

Performance Considerations

  • Inspector callbacks have minimal overhead when not implemented (empty default methods)
  • Use inspection judiciously in production - detailed tracing can impact performance
  • Consider batching inspector data collection for high-throughput scenarios

Common Use Cases

  • Debuggers: Step-by-step execution analysis
  • Gas analyzers: Detailed gas consumption tracking
  • Security tools: Detecting suspicious patterns or calls
  • Development tools: Contract interaction tracing
  • Testing frameworks: Execution verification and state checking

The Inspector trait makes REVM very observable EVM implementations available, enabling sophisticated tooling and analysis capabilities.

External State Transitions (EIP-4788 & EIP-2935)

Some Ethereum Improvement Proposals (EIPs) require state transitions that are not triggered by regular user transactions, but are instead performed by the client using special system calls (such as transact_system_call). These transitions are part of the EVM state changes, but are initiated by the client at specific block boundaries (pre- or post-block hooks), as required by the EIP.

What are external state transitions?

External state transitions refer to updates to the Ethereum state that are not performed by regular user transactions, but are instead performed by the client using system calls at block boundaries. These are typically required for EIPs that introduce new system contracts or require special state updates at block boundaries.

EIP-4788: Beacon block root in the EVM

EIP-4788 requires that the root of each beacon chain block is committed to the execution layer and made available in the EVM via a special contract. This is achieved by the client calling a system contract at a fixed address (0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02) with the beacon root as input, at the start of each block. The contract maintains a ring buffer of recent roots.

  • The system call is performed by the client, not by EVM transaction execution.
  • If the contract does not exist, the call fails silently.
  • See EIP-4788 for full details.
  • Example implementation in Reth: reth#4457

EIP-2935: Add blockHash and blockNumber to the EVM

EIP-2935 introduces a system contract that stores recent block hashes, allowing contracts to query them. The client is responsible for updating this contract at each block, by calling a system contract at a fixed address (0x0000F90827F1C53a10cb7A02335B175320002935) with the new block hash.

  • The system call is performed by the client, not by EVM transaction execution.
  • See EIP-2935 for full details.
  • Example implementation in Reth: reth#7818

How does this affect REVM users?

  • To perform these block state transitions, the client or test harness should use the system call mechanism (transact_system_call) provided by REVM.
  • REVM itself does not automatically perform these transitions; it expects the client to initiate them at the appropriate block boundaries, as specified by the EIPs.
  • If you are building a full Ethereum client or a test harness, you are responsible for performing these system calls at the appropriate block boundaries, as specified in the EIPs.
  • If you are only using REVM for transaction execution, you may need to ensure that the state of these system contracts is kept up to date externally.

References

Building from source

It requires running

git clone https://github.com/bluealloy/revm.git
cd revm
cargo build --release

Note: This project tends to use the newest rust version, so if you're encountering a build error try running rustup update first.

Note: clang is required for building revm with c-kzg or secp256k1 feature flags as they depend on C libraries. If you don't have it installed, you can install it with apt install clang.

Revme

Is a binary that allows running statetest and eof validation.

$: revme --help
Usage: revme <COMMAND>

Commands:
  statetest       Execute Ethereum state tests
  evm             Run arbitrary EVM bytecode
  bytecode        Print the structure of an EVM bytecode
  bench           Run bench from specified list
  help            Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

Running eth tests

Eth tests are a suite of tests from the Ethereum Foundation that are used to test EVM implementations. Part of these tests are included in the revm repository in the tests folder.

Test suites for the latest hardforks can be found in EEST releases https://github.com/ethereum/execution-spec-tests/releases, and there are additional tests that cover older hardforks in https://github.com/ethereum/legacytests

Revm can run statetest type of tests with revme using the following command: cargo run --release -p revme -- statetest folder_path

For running EEST tests, we can use the ./scripts/run-tests.sh.

For legacy tests, we need to first to download the repo git clone https://github.com/ethereum/legacytests and then run it with cargo run --release -p revme -- statetest legacytests/Cancun/GeneralStateTests All statetest that can be run by revme can be found in the GeneralStateTests folder.

Release procedure

This document describes the procedure for releasing a new version of the revm project. The repository is hosted on github and published to crates.io. Every published crate is part of one tag (currently tag v55) that contains all crates versions. The versions depending on the change can be major, minor, or patch; we don't have one version for all packages.

Link of tag to the particular version of the crate can be found in CHANGELOG file.

Contact

The git repository can be found at https://github.com/bluealloy/revm/

For questions please open a github issue or join the public telegram group: https://t.me/+Ig4WDWOzikA3MzA0

Licence

Licensed under MIT Licence.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, shall be licensed as above, without any additional terms or conditions.

Security

If there is a security question or finding please contact me directly via email at dragan0rakita@gmail.com or on keybase @draganrakita