Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6070a3b
refactor(ev-deployer): make address optional in contract configs
randygrok Mar 30, 2026
3c74168
feat(ev-deployer): add initcode constants for live deployment
randygrok Mar 30, 2026
1f7e2cc
feat(ev-deployer): add CREATE2 address computation module
randygrok Mar 30, 2026
826c10d
feat(ev-deployer): add deploy state file with immutability checks
randygrok Mar 30, 2026
f9054b7
feat(ev-deployer): add ChainDeployer trait and LiveDeployer
randygrok Mar 30, 2026
2b90e82
feat(ev-deployer): add deploy pipeline with mock-tested flow
randygrok Mar 30, 2026
0c67446
feat(ev-deployer): wire up deploy subcommand in CLI
randygrok Mar 30, 2026
308ac0e
docs(ev-deployer): update init template for deploy mode
randygrok Mar 30, 2026
cbdaaa2
style(ev-deployer): fix rustfmt formatting
randygrok Mar 30, 2026
58dd78a
chore: update Cargo.lock for ev-deployer deploy dependencies
randygrok Mar 30, 2026
868bd3a
fix(ev-deployer): address clippy lints and nightly rustfmt imports
randygrok Mar 30, 2026
77c098b
fix(ev-deployer): remove AdminProxy from deploy pipeline
randygrok Mar 30, 2026
922af60
fix(ev-deployer): use atomic writes for deploy state file
randygrok Mar 30, 2026
bb35228
feat(ev-deployer): add CLI flags to init command
randygrok Mar 31, 2026
48a4d39
feat(ev-deployer): add template builder with parameterized tests
randygrok Mar 31, 2026
9771899
feat(ev-deployer): wire parameterized init into CLI
randygrok Mar 31, 2026
9c2fcb6
docs(ev-deployer): rewrite README as comprehensive user guide
randygrok Mar 31, 2026
71afcec
fix(ev-deployer): address clippy lints and rustfmt in init module
randygrok Mar 31, 2026
2b9485c
feat(ev-deployer): add deterministic deployer as genesis contract
randygrok Mar 31, 2026
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
6 changes: 6 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions bin/ev-deployer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ authors.workspace = true

[dependencies]
alloy-primitives = { workspace = true, features = ["serde"] }
alloy = { workspace = true }
alloy-rpc-types-eth = { workspace = true }
alloy-signer-local = { workspace = true }
async-trait = { workspace = true }
tokio = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
toml = "0.8"
eyre = { workspace = true }
rand = { workspace = true }

[dev-dependencies]
tempfile = { workspace = true }
Expand Down
182 changes: 126 additions & 56 deletions bin/ev-deployer/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
# EV Deployer

CLI tool for generating genesis alloc entries for ev-reth contracts. It reads a declarative TOML config and produces the JSON needed to embed contracts into a chain's genesis state.
CLI tool for deploying ev-reth contracts. It reads a declarative TOML config and either embeds contracts into a chain's genesis state or deploys them to a live chain via CREATE2.

## Modes of Operation

EV Deployer has two deployment modes:

| Mode | When to use | What it does |
|------|-------------|-------------|
| **genesis** | Before the chain starts | Produces JSON alloc entries to embed contracts into the genesis state. No RPC needed. |
| **deploy** | On a running chain | Deploys contracts via CREATE2 through the deterministic deployer. Requires RPC + signer. |

Both modes read the same TOML config. The `address` field in each contract section is used by `genesis` to place the contract at that exact address. In `deploy` mode, addresses are computed deterministically via CREATE2 and the config `address` is ignored.

## Quick Start

```bash
# 1. Generate a config pre-populated for your chain
ev-deployer init --chain-id 42170 --permit2 --output deploy.toml

# 2a. Genesis mode: embed into genesis state
ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json

# 2b. Deploy mode: deploy to a live chain
ev-deployer deploy \
--config deploy.toml \
--rpc-url http://localhost:8545 \
--private-key 0x... \
--state deploy-state.json
```

## Building

Expand All @@ -10,98 +38,140 @@ just build-deployer

The binary is output to `target/release/ev-deployer`.

## Configuration
## Commands

EV Deployer uses a TOML config file to define what contracts to include and how to configure them. See [`examples/devnet.toml`](examples/devnet.toml) for a complete example.
### `init`

### Config reference
Generate a starter config file.

#### `[chain]`
```bash
# Bare template (all contracts commented out)
ev-deployer init

# Pre-populated with chain ID, Permit2, and deterministic deployer
ev-deployer init --chain-id 42170 --permit2 --deterministic-deployer

# Full config with all contracts
ev-deployer init \
--chain-id 42170 \
--permit2 \
--admin-proxy-owner 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \
--output deploy.toml
```

| Field | Type | Description |
|------------|------|-------------|
| `chain_id` | u64 | Chain ID |
| Flag | Description |
|------|-------------|
| `--output <PATH>` | Write to file instead of stdout |
| `--chain-id <ID>` | Set the chain ID (defaults to 0) |
| `--permit2` | Enable Permit2 with its canonical address |
| `--deterministic-deployer` | Enable the deterministic deployer (Nick's factory) with its canonical address |
| `--admin-proxy-owner <ADDR>` | Enable AdminProxy with the given owner |

#### `[contracts.admin_proxy]`
### `genesis`

| Field | Type | Description |
|-----------|---------|---------------------------|
| `address` | address | Address to deploy at |
| `owner` | address | Owner (must not be zero) |
Generate genesis alloc JSON from a config.

#### `[contracts.permit2]`
```bash
# Print alloc to stdout
ev-deployer genesis --config deploy.toml

| Field | Type | Description |
|-----------|---------|----------------------------------------------------------|
| `address` | address | Address to deploy at (canonical: `0x000000000022D473030F116dDEE9F6B43aC78BA3`) |
# Write to file
ev-deployer genesis --config deploy.toml --output alloc.json

## Usage
# Merge into an existing genesis file
ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json

### Generate a starter config
# Overwrite existing addresses when merging
ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json --force

```bash
ev-deployer init --output deploy.toml
# Also export an address manifest
ev-deployer genesis --config deploy.toml --addresses-out addresses.json
```

This creates a TOML config template with all supported contracts commented out and documented.
In genesis mode, every configured contract must have an `address` field.

### Generate genesis alloc
### `deploy`

Print alloc JSON to stdout:
Deploy contracts to a live chain via CREATE2.

```bash
ev-deployer genesis --config deploy.toml
ev-deployer deploy \
--config deploy.toml \
--rpc-url http://localhost:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--state deploy-state.json \
--addresses-out addresses.json
```

Write to a file:
| Flag | Env var | Description |
|------|---------|-------------|
| `--config <PATH>` | | Path to the TOML config |
| `--rpc-url <URL>` | `EV_DEPLOYER_RPC_URL` | RPC endpoint of the target chain |
| `--private-key <HEX>` | `EV_DEPLOYER_PRIVATE_KEY` | Hex-encoded private key for signing |
| `--state <PATH>` | | Path to the state file (created if absent) |
| `--addresses-out <PATH>` | | Write a JSON address manifest |

```bash
ev-deployer genesis --config deploy.toml --output alloc.json
```
The deploy pipeline:

### Merge into an existing genesis file
1. Connects to the RPC and verifies the chain ID matches the config.
2. Checks that the deterministic deployer (`0x4e59b44847b379578588920ca78fbf26c0b4956c`) exists on-chain.
3. Deploys each configured contract via CREATE2.
4. Verifies that the on-chain bytecode matches the expected bytecode (including patched immutables).

Insert the generated entries into an existing `genesis.json`. The merged result is written to `--output` (or stdout if `--output` is omitted):
The `address` field in the config is **ignored** in deploy mode — addresses come from the CREATE2 computation.

```bash
ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json
```
#### State file and resumability

If an address already exists in the genesis, the command fails. Use `--force` to overwrite:
The `--state` file tracks deployment progress. On first run it generates a random CREATE2 salt and records which contracts have been deployed. If the process is interrupted, re-running with the same state file resumes where it left off.

```bash
ev-deployer genesis --config deploy.toml --merge-into genesis.json --output genesis-out.json --force
```
Immutability rules protect against accidental misconfiguration on resume:

### Export address manifest
- The `chain_id` cannot change between runs.
- A contract that was configured in the original run cannot be removed.
- New contracts can be added to subsequent runs.

Write a JSON mapping of contract names to their configured addresses:
### `compute-address`

Look up the configured address for a contract.

```bash
ev-deployer genesis --config deploy.toml --addresses-out addresses.json
ev-deployer compute-address --config deploy.toml --contract permit2
```

Output:
## Config Reference

```json
{
"admin_proxy": "0x000000000000000000000000000000000000Ad00",
"permit2": "0x000000000022D473030F116dDEE9F6B43aC78BA3"
}
```
### `[chain]`

### Look up a contract address
| Field | Type | Description |
|-------|------|-------------|
| `chain_id` | u64 | Chain ID |

```bash
ev-deployer compute-address --config deploy.toml --contract admin_proxy
```
### `[contracts.admin_proxy]`

| Field | Type | Description |
|-------|------|-------------|
| `address` | address | Address to deploy at (required for genesis, ignored for deploy) |
| `owner` | address | Owner address (must not be zero) |

### `[contracts.permit2]`

| Field | Type | Description |
|-------|------|-------------|
| `address` | address | Address to deploy at (canonical: `0x000000000022D473030F116dDEE9F6B43aC78BA3`). Required for genesis, ignored for deploy. |

### `[contracts.deterministic_deployer]`

| Field | Type | Description |
|-------|------|-------------|
| `address` | address | Address (canonical: `0x4e59b44847b379578588920cA78FbF26c0B4956C`). Required for genesis. Genesis-only — not used in deploy mode. |

## Contracts

| Contract | Description |
|---------------|----------------------------------------------------|
| `admin_proxy` | Proxy contract with owner-based access control |
| `permit2` | Uniswap canonical token approval manager |
| Contract | Description |
|----------|-------------|
| `admin_proxy` | Transparent proxy with owner-based access control |
| `permit2` | Uniswap canonical token approval manager (same address on all chains) |
| `deterministic_deployer` | Nick's CREATE2 factory — genesis-only, needed on post-merge chains |

Runtime bytecodes are embedded in the binary — no external toolchain is needed at deploy time.

Expand Down
Loading
Loading