Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Rundler is a high-performance, modular Rust implementation of an ERC-4337 bundler for Account Abstraction. It's built by Alchemy and designed for cloud-scale deployments with a focus on reliability and performance.

## Development Commands

### Building and Testing
```bash
# Build the project
make build
cargo build --all --all-features

# Run unit tests
make test-unit
cargo nextest run --locked --workspace --all-features --no-fail-fast

# Run all tests (unit + spec tests)
make test

# Run ERC-4337 spec tests (v0.6 and v0.7)
make test-spec-integrated
make test-spec-integrated-v0_6 # v0.6 only
make test-spec-integrated-v0_7 # v0.7 only

# Run spec tests in modular mode
make test-spec-modular
```

### Code Quality
```bash
# Format code (requires nightly Rust)
make fmt
cargo +nightly fmt

# Lint code
make lint
cargo clippy --all --all-features --tests -- -D warnings

# Clean build artifacts
make clean
cargo clean
```

### Running Locally
```bash
# Run full node (RPC + Pool + Builder in single process)
cargo run node

# Run individual components in distributed mode
cargo run rpc # RPC server only
cargo run pool # Pool server only
cargo run builder # Builder server only
```

## Architecture

Rundler consists of 3 main modular components:

1. **RPC Server** (`crates/rpc/`): Implements ERC-4337 RPC methods (`eth_*`, `debug_*`, `rundler_*` namespaces)
2. **Pool** (`crates/pool/`): User Operation mempool with validation, simulation, and chain reorg handling
3. **Builder** (`crates/builder/`): Bundle construction, transaction submission, and mining monitoring

### Communication Patterns
- **RPC → Pool**: Submits user operations via `eth_sendUserOperation`
- **RPC → Builder**: Debug namespace for manual bundling control
- **Builder ↔ Pool**: Bundle coordination and operation status updates

### Key Supporting Crates
- `crates/sim/`: Gas estimation and operation simulation
- `crates/provider/`: Ethereum provider abstractions with Alloy
- `crates/types/`: Core type definitions
- `crates/contracts/`: Smart contract bindings and utilities
- `crates/signer/`: Transaction signing (local keys, AWS KMS)

## Configuration

### Environment Variables
Most CLI options can be set via environment variables. Key ones:
- `NODE_HTTP`: Ethereum RPC endpoint (required)
- `NETWORK`: Predefined network (dev, ethereum, optimism, etc.)
- `CHAIN_SPEC`: Path to custom chain specification TOML
- `RUST_LOG`: Log level control

### Chain Specifications
Chain configs are in `bin/rundler/chain_specs/` with network-specific settings for gas estimation, fee calculation, and protocol parameters.

## Entry Point Support

- **v0.6**: ERC-4337 v0.6 specification (can be disabled with `--disable_entry_point_v0_6`)
- **v0.7**: ERC-4337 v0.7 specification (can be disabled with `--disable_entry_point_v0_7`)

Both versions are supported simultaneously by default.

## Prerequisites

- Rust 1.87+ with nightly for formatting
- Docker (for spec tests)
- PDM (Python dependency manager for spec tests)
- Protobuf compiler (protoc)
- Buf (protobuf linting)
- Foundry ^0.3.0 (contract compilation)

## Testing Setup

For spec tests, first install frameworks:
```bash
cd test/spec-tests/v0_6/bundler-spec-tests && pdm install && pdm run update-deps
cd test/spec-tests/v0_7/bundler-spec-tests && pdm install && pdm run update-deps
```

## Workspace Structure

This is a Cargo workspace with the main binary in `bin/rundler/` and library crates in `crates/`. The architecture is designed for both monolithic and distributed deployment modes.
8 changes: 0 additions & 8 deletions bin/rundler/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,6 @@ pub struct CommonArgs {
)]
pre_verification_gas_accept_percent: u32,

#[arg(
long = "execution_gas_limit_efficiency_reject_threshold",
name = "execution_gas_limit_efficiency_reject_threshold",
env = "EXECUTION_GAS_LIMIT_EFFICIENCY_REJECT_THRESHOLD",
default_value = "0.0"
)]
pub execution_gas_limit_efficiency_reject_threshold: f64,

#[arg(
long = "verification_gas_limit_efficiency_reject_threshold",
name = "verification_gas_limit_efficiency_reject_threshold",
Expand Down
24 changes: 20 additions & 4 deletions bin/rundler/src/cli/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use std::{collections::HashMap, net::SocketAddr, time::Duration};

use alloy_primitives::Address;
use anyhow::Context;
use clap::Args;
use clap::{builder::ValueParser, Args};
use rundler_pool::{LocalPoolBuilder, PoolConfig, PoolTask, PoolTaskArgs};
use rundler_provider::Providers;
use rundler_provider::{Providers, RevertCheckMode};
use rundler_sim::MempoolConfigs;
use rundler_task::TaskSpawnerExt;
use rundler_types::{
Expand Down Expand Up @@ -174,6 +174,23 @@ pub struct PoolArgs {
env = "POOL_MAX_TIME_IN_POOL_SECS"
)]
pub max_time_in_pool_secs: Option<u64>,

#[arg(
long = "pool.revert_check_mode",
name = "pool.revert_check_mode",
env = "POOL_REVERT_CHECK_MODE",
value_parser = ValueParser::new(parse_revert_check_mode)
)]
pub revert_check_mode: Option<RevertCheckMode>,
}

fn parse_revert_check_mode(s: &str) -> Result<RevertCheckMode, anyhow::Error> {
match s {
"eth_call" => Ok(RevertCheckMode::EthCall),
"eth_simulate_v1" => Ok(RevertCheckMode::EthSimulateV1),
"debug_trace_call" => Ok(RevertCheckMode::DebugTraceCall),
_ => Err(anyhow::anyhow!("invalid revert check mode: {}, must be one of eth_call, eth_simulate_v1, debug_trace_call", s)),
}
}

impl PoolArgs {
Expand Down Expand Up @@ -223,12 +240,11 @@ impl PoolArgs {
reputation_tracking_enabled: self.reputation_tracking_enabled,
drop_min_num_blocks: self.drop_min_num_blocks,
da_gas_tracking_enabled,
execution_gas_limit_efficiency_reject_threshold: common
.execution_gas_limit_efficiency_reject_threshold,
verification_gas_limit_efficiency_reject_threshold: common
.verification_gas_limit_efficiency_reject_threshold,
max_time_in_pool: self.max_time_in_pool_secs.map(Duration::from_secs),
max_expected_storage_slots: common.max_expected_storage_slots.unwrap_or(usize::MAX),
revert_check_mode: self.revert_check_mode,
};

let mut pool_configs = vec![];
Expand Down
4 changes: 4 additions & 0 deletions crates/builder/src/bundle_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ where

async fn process_revert(&self, tx_hash: B256) -> anyhow::Result<()> {
warn!("Bundle transaction {tx_hash:?} reverted onchain");
if !self.chain_spec.rpc_debug_trace_transaction_enabled {
warn!("Debug trace transaction is not enabled, skipping trace");
return Ok(());
}

let trace_options = GethDebugTracingOptions::new_tracer(
GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer),
Expand Down
2 changes: 2 additions & 0 deletions crates/contracts/src/v0_7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ sol!(

error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);

error PostOpReverted(bytes returnData);

error SignatureValidationFailed(address aggregator);

function handleOps(
Expand Down
12 changes: 12 additions & 0 deletions crates/pool/proto/op_pool/op_pool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,8 @@ message MempoolError {
UseUnsupportedEIP use_unsupported_eip = 20;
AggregatorError aggregator = 21;
Invalid7702AuthSignature invalid_7702_auth_signature = 22;
ExecutionRevert execution_revert = 23;
PostOpRevert post_op_revert = 24;
}
}

Expand Down Expand Up @@ -721,6 +723,16 @@ message UseUnsupportedEIP {
string eip_name = 1;
}

message ExecutionRevert {
bytes data = 1;
string reason = 2;
}

message PostOpRevert {
bytes data = 1;
string reason = 2;
}

// PRECHECK VIOLATIONS
message PrecheckViolationError {
oneof violation {
Expand Down
7 changes: 3 additions & 4 deletions crates/pool/src/mempool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod size;

mod paymaster;
pub(crate) use paymaster::{PaymasterConfig, PaymasterTracker};
use rundler_provider::RevertCheckMode;

mod uo_pool;
use std::{
Expand Down Expand Up @@ -173,16 +174,14 @@ pub struct PoolConfig {
pub drop_min_num_blocks: u64,
/// Reject user operations with gas limit efficiency below this threshold.
/// Gas limit efficiency is defined as the ratio of the gas limit to the gas used.
/// This applies to the execution gas limit.
pub execution_gas_limit_efficiency_reject_threshold: f64,
/// Reject user operations with gas limit efficiency below this threshold.
/// Gas limit efficiency is defined as the ratio of the gas limit to the gas used.
/// This applies to the verification gas limit.
pub verification_gas_limit_efficiency_reject_threshold: f64,
/// Maximum time a UO is allowed in the pool before being dropped
pub max_time_in_pool: Option<Duration>,
/// The maximum number of storage slots that can be expected to be used by a user operation during validation
pub max_expected_storage_slots: usize,
/// Whether to enable the revert check and the mode to use
pub revert_check_mode: Option<RevertCheckMode>,
}

/// Origin of an operation.
Expand Down
Loading
Loading