Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 14 additions & 14 deletions bin/ev-deployer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize};
use std::{collections::HashSet, path::Path};

/// Top-level deploy configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct DeployConfig {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeployConfig {
/// Chain configuration.
pub chain: ChainConfig,
/// Contract configurations.
Expand All @@ -15,15 +15,15 @@ pub(crate) struct DeployConfig {
}

/// Chain-level settings.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct ChainConfig {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ChainConfig {
/// The chain ID.
pub chain_id: u64,
}

/// All contract configurations.
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub(crate) struct ContractsConfig {
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ContractsConfig {
/// `AdminProxy` contract config (optional).
pub admin_proxy: Option<AdminProxyConfig>,
/// `Permit2` contract config (optional).
Expand Down Expand Up @@ -56,32 +56,32 @@ impl ContractsConfig {
}

/// `AdminProxy` configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct AdminProxyConfig {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AdminProxyConfig {
/// Address to deploy at (required for genesis, ignored for deploy).
pub address: Option<Address>,
/// Owner address.
pub owner: Address,
}

/// `Permit2` configuration (Uniswap token approval manager).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct Permit2Config {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Permit2Config {
/// Address to deploy at (required for genesis, ignored for deploy).
pub address: Option<Address>,
}

/// Deterministic deployer (Nick's factory) configuration.
/// Only used in genesis mode — in deploy mode this is the CREATE2 factory itself.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct DeterministicDeployerConfig {
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeterministicDeployerConfig {
/// Address (defaults to the canonical `0x4e59b44847b379578588920ca78fbf26c0b4956c`).
pub address: Option<Address>,
}

impl DeployConfig {
/// Load and validate config from a TOML file.
pub(crate) fn load(path: &Path) -> eyre::Result<Self> {
pub fn load(path: &Path) -> eyre::Result<Self> {
let content = std::fs::read_to_string(path)?;
let config: Self = toml::from_str(&content)?;
config.validate()?;
Expand Down Expand Up @@ -125,7 +125,7 @@ impl DeployConfig {
}

/// Additional validation for genesis mode: all addresses must be specified.
pub(crate) fn validate_for_genesis(&self) -> eyre::Result<()> {
pub fn validate_for_genesis(&self) -> eyre::Result<()> {
if let Some(ref ap) = self.contracts.admin_proxy {
eyre::ensure!(
ap.address.is_some(),
Expand Down
3 changes: 2 additions & 1 deletion bin/ev-deployer/src/contracts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use alloy_primitives::{Address, Bytes, B256};
use std::collections::BTreeMap;

/// A contract ready to be placed in genesis alloc.
pub(crate) struct GenesisContract {
#[derive(Debug)]
pub struct GenesisContract {
/// The address to deploy at.
pub address: Address,
/// Runtime bytecode.
Expand Down
16 changes: 12 additions & 4 deletions bin/ev-deployer/src/deploy/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ use async_trait::async_trait;

/// Receipt from a confirmed transaction.
#[derive(Debug)]
pub(crate) struct TxReceipt {
pub struct TxReceipt {
/// Hash of the confirmed transaction.
pub tx_hash: B256,
/// Whether the transaction executed successfully.
pub success: bool,
}

/// Abstracts on-chain operations for the deploy pipeline.
#[async_trait]
pub(crate) trait ChainDeployer: Send + Sync {
pub trait ChainDeployer: Send + Sync {
/// Get the chain ID of the connected chain.
async fn chain_id(&self) -> eyre::Result<u64>;

Expand All @@ -32,13 +34,19 @@ pub(crate) trait ChainDeployer: Send + Sync {
}

/// Live deployer using alloy provider + signer.
pub(crate) struct LiveDeployer {
pub struct LiveDeployer {
provider: Box<dyn Provider>,
}

impl std::fmt::Debug for LiveDeployer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LiveDeployer").finish_non_exhaustive()
}
}

impl LiveDeployer {
/// Create a new `LiveDeployer` from an RPC URL and a hex-encoded private key.
pub(crate) fn new(rpc_url: &str, private_key_hex: &str) -> eyre::Result<Self> {
pub fn new(rpc_url: &str, private_key_hex: &str) -> eyre::Result<Self> {
let key_hex = private_key_hex
.strip_prefix("0x")
.unwrap_or(private_key_hex);
Expand Down
8 changes: 4 additions & 4 deletions bin/ev-deployer/src/deploy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub(crate) mod create2;
pub(crate) mod deployer;
pub(crate) mod pipeline;
pub(crate) mod state;
pub mod create2;
pub mod deployer;
pub mod pipeline;
pub mod state;
8 changes: 3 additions & 5 deletions bin/ev-deployer/src/deploy/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use alloy_primitives::{Address, B256};
use std::path::{Path, PathBuf};

/// Configuration for the deploy pipeline.
pub(crate) struct PipelineConfig {
#[derive(Debug)]
pub struct PipelineConfig {
/// Parsed deployment intent loaded from the user-provided config file.
pub config: DeployConfig,
/// Path to the persisted deploy state JSON file.
Expand All @@ -26,10 +27,7 @@ pub(crate) struct PipelineConfig {
}

/// Run the full deploy pipeline.
pub(crate) async fn run(
pipeline_cfg: &PipelineConfig,
deployer: &dyn ChainDeployer,
) -> eyre::Result<()> {
pub async fn run(pipeline_cfg: &PipelineConfig, deployer: &dyn ChainDeployer) -> eyre::Result<()> {
// ── Step 1: Init ──
eprintln!("[1/4] Connecting to RPC...");
let chain_id = deployer.chain_id().await?;
Expand Down
15 changes: 8 additions & 7 deletions bin/ev-deployer/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde_json::{Map, Value};
use std::path::Path;

/// Build the alloc JSON from config.
pub(crate) fn build_alloc(config: &DeployConfig) -> Value {
pub fn build_alloc(config: &DeployConfig) -> Value {
let mut alloc = Map::new();

if let Some(ref ap_config) = config.contracts.admin_proxy {
Expand All @@ -31,14 +31,15 @@ pub(crate) fn build_alloc(config: &DeployConfig) -> Value {
}

/// Build alloc and merge into an existing genesis JSON file.
pub(crate) fn merge_into(
config: &DeployConfig,
genesis_path: &Path,
force: bool,
) -> eyre::Result<Value> {
pub fn merge_into(config: &DeployConfig, genesis_path: &Path, force: bool) -> eyre::Result<Value> {
let content = std::fs::read_to_string(genesis_path)?;
let mut genesis: Value = serde_json::from_str(&content)?;
merge_alloc(config, &mut genesis, force)?;
Ok(genesis)
}

/// Merge deployer contracts into a genesis JSON value in memory.
pub fn merge_alloc(config: &DeployConfig, genesis: &mut Value, force: bool) -> eyre::Result<()> {
let alloc = build_alloc(config);

let genesis_alloc = genesis
Expand All @@ -62,7 +63,7 @@ pub(crate) fn merge_into(
genesis_alloc.insert(canonical, entry.clone());
}

Ok(genesis)
Ok(())
}

fn normalize_addr(addr: &str) -> String {
Expand Down
9 changes: 7 additions & 2 deletions bin/ev-deployer/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
//! Dynamic config template generation for the `init` command.

/// Parameters for generating the init template.
pub(crate) struct InitParams {
#[derive(Debug)]
pub struct InitParams {
/// Target chain ID.
pub chain_id: u64,
/// Whether to include Permit2 with its canonical address.
pub permit2: bool,
/// Whether to include the deterministic deployer (Nick's factory).
pub deterministic_deployer: bool,
/// Optional `AdminProxy` owner address.
pub admin_proxy_owner: Option<String>,
}

/// Generate a TOML config template based on the given parameters.
pub(crate) fn generate_template(params: &InitParams) -> String {
pub fn generate_template(params: &InitParams) -> String {
let mut out = String::new();

// Header
Expand Down
13 changes: 13 additions & 0 deletions bin/ev-deployer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! EV Deployer — genesis alloc generator for ev-reth contracts.
//!
//! This crate provides both a CLI tool and a library for generating genesis
//! alloc entries from declarative TOML configurations.

pub mod config;
pub mod contracts;
/// CREATE2 deploy pipeline for live chain deployment.
pub mod deploy;
pub mod genesis;
/// Dynamic config template generation for the `init` command.
pub mod init;
pub mod output;
8 changes: 1 addition & 7 deletions bin/ev-deployer/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
//! EV Deployer — genesis alloc generator and live deployer for ev-reth contracts.

mod config;
mod contracts;
mod deploy;
mod genesis;
mod init;
mod output;

use alloy_primitives::Address;
use clap::{Parser, Subcommand};
use ev_deployer::{config, deploy, genesis, init, output};
use std::path::PathBuf;

/// EV Deployer: generate genesis alloc or deploy ev-reth contracts.
Expand Down
2 changes: 1 addition & 1 deletion bin/ev-deployer/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::config::DeployConfig;
use serde_json::{Map, Value};

/// Build an address manifest JSON from config.
pub(crate) fn build_manifest(config: &DeployConfig) -> Value {
pub fn build_manifest(config: &DeployConfig) -> Value {
let mut manifest = Map::new();

if let Some(ref ap) = config.contracts.admin_proxy {
Expand Down
1 change: 1 addition & 0 deletions bin/ev-dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ path = "src/main.rs"
[dependencies]
# Core evolve crates
ev-node = { path = "../../crates/node" }
ev-deployer = { path = "../ev-deployer" }
evolve-ev-reth = { path = "../../crates/evolve" }

# Reth CLI and core dependencies
Expand Down
40 changes: 36 additions & 4 deletions bin/ev-dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ ev-dev [OPTIONS]
| `--block-time` | `1` | Block time in seconds (`0` = mine on transaction) |
| `--silent` | `false` | Suppress the startup banner |
| `--accounts` | `10` | Number of accounts to display (1-20) |
| `--deploy-config` | — | Path to an ev-deployer TOML config to deploy contracts at genesis |

### Examples

Expand All @@ -40,8 +41,38 @@ ev-dev --host 0.0.0.0

# Custom port, faster blocks
ev-dev --port 9545 --block-time 2

# Start with genesis contracts deployed
ev-dev --deploy-config bin/ev-deployer/examples/devnet.toml
```

## Genesis Contract Deployment

You can deploy contracts into the genesis state by passing a `--deploy-config` flag pointing to an [ev-deployer](../ev-deployer/README.md) TOML config file.

```bash
ev-dev --deploy-config path/to/deploy.toml
```

When a deploy config is provided, ev-dev will:

1. Load and validate the config
2. Override the config's `chain_id` to match the devnet genesis (a warning is printed if they differ)
3. Merge the contract alloc entries into the genesis state before starting the node
4. Print the deployed contract addresses in the startup banner

The startup banner will show an extra section:

```
Genesis Contracts (from path/to/deploy.toml)
==================
admin_proxy "0x000000000000000000000000000000000000Ad00"
fee_vault "0x000000000000000000000000000000000000FE00"
...
```

See the [ev-deployer README](../ev-deployer/README.md) for full config reference and available contracts.

## Chain Details

| Property | Value |
Expand Down Expand Up @@ -204,9 +235,10 @@ ev-dev includes all Evolve customizations out of the box:

ev-dev is a thin wrapper around the full `ev-reth` node. On startup it:

1. Writes the embedded devnet genesis to a temp file
2. Creates a temporary data directory (clean state every run)
3. Launches `ev-reth` in `--dev` mode with networking disabled
4. Exposes HTTP and WebSocket RPC on the configured host/port
1. If `--deploy-config` is provided, loads the config and merges contract alloc entries into the genesis
2. Writes the (possibly extended) devnet genesis to a temp file
3. Creates a temporary data directory (clean state every run)
4. Launches `ev-reth` in `--dev` mode with networking disabled
5. Exposes HTTP and WebSocket RPC on the configured host/port

Each run starts from a fresh genesis — there is no persistent state between restarts.
Loading
Loading