Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ebd4365
feat(ev-dev): add interactive TUI dashboard with --tui flag
jgimeno Mar 19, 2026
e0c3330
feat(ev-dev): add real-time balance polling to TUI dashboard
randygrok Mar 20, 2026
8a3a4f3
fix(ev-dev): replace redundant match with direct let binding
randygrok Mar 23, 2026
fa99c4f
feat(ev-dev): add block selection and transaction detail overlay to TUI
randygrok Mar 24, 2026
86b92d7
merge: resolve conflicts with ev-deployer-part4-ev-dev-integration
randygrok Mar 24, 2026
b68f7f7
style(ev-dev): fix rustfmt formatting in TUI module
randygrok Mar 24, 2026
5030259
fix(ev-dev): use map_or_else to satisfy clippy or_fun_call lint
randygrok Mar 24, 2026
739b3e0
Merge remote-tracking branch 'origin/ev-deployer-part4-ev-dev-integra…
randygrok Mar 25, 2026
21a7989
Merge remote-tracking branch 'origin/ev-deployer-part4-ev-dev-integra…
randygrok Mar 27, 2026
5f950fb
Merge remote-tracking branch 'origin/ev-deployer-part4-ev-dev-integra…
randygrok Mar 31, 2026
d2bd5a6
Merge remote-tracking branch 'origin/ev-deployer-part4-ev-dev-integra…
randygrok Mar 31, 2026
f353150
style(ev-deployer): fix clippy warnings and missing docs
randygrok Mar 31, 2026
7449b21
merge: integrate ev-deployer-part4 clippy and rustfmt fixes
randygrok Mar 31, 2026
a0d9906
feat(ev-dev): include deterministic deployer in devnet genesis
randygrok Mar 31, 2026
28e24c9
Merge branch 'ev-deployer-part4-ev-dev-integration' of github.com-ran…
randygrok Mar 31, 2026
304f60a
feat(ev-deployer): use canonical Uniswap salt for Permit2 CREATE2 deploy
randygrok Apr 1, 2026
01815ea
refactor(ev-deployer): split init into genesis and deploy subcommands
randygrok Apr 2, 2026
9ed902f
fix(ev-deployer): remove deterministic deployer from deploy init temp…
randygrok Apr 2, 2026
160fd22
docs: update ev-deployer and ev-dev READMEs for init subcommands
randygrok Apr 2, 2026
d2624bf
refactor(ev-dev): rename --deploy-config to --genesis-config
randygrok Apr 2, 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
694 changes: 684 additions & 10 deletions Cargo.lock

Large diffs are not rendered by default.

59 changes: 42 additions & 17 deletions bin/ev-deployer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ Both modes read the same TOML config. The `address` field in each contract secti
## Quick Start

```bash
# 1. Generate a config pre-populated for your chain
ev-deployer init --chain-id 42170 --permit2 --output deploy.toml
# Genesis: embed contracts into the chain's genesis state
ev-deployer init genesis --chain-id 42170 --permit2 --deterministic-deployer --output genesis.toml
ev-deployer genesis --config genesis.toml --merge-into genesis.json --output genesis-out.json

# 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
# Deploy: deploy contracts to a running chain via CREATE2
ev-deployer init deploy --chain-id 42170 --permit2 --output deploy.toml
ev-deployer deploy \
--config deploy.toml \
--rpc-url http://localhost:8545 \
Expand All @@ -40,31 +39,55 @@ The binary is output to `target/release/ev-deployer`.

## Commands

### `init`
### `init genesis`

Generate a starter config file.
Generate a starter config for **genesis mode** (contracts embedded at chain start). Includes `address` fields for each contract.

```bash
# Bare template (all contracts commented out)
ev-deployer init
ev-deployer init genesis

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

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

| 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 |
| `--deterministic-deployer` | Enable the deterministic deployer (Nick's factory) |
| `--admin-proxy-owner <ADDR>` | Enable AdminProxy with the given owner |

### `init deploy`

Generate a starter config for **deploy mode** (contracts deployed via CREATE2 to a running chain). No `address` fields — addresses are computed deterministically. The deterministic deployer is not included in the config since it cannot be deployed via CREATE2 (it must already exist on-chain).

```bash
# Config with Permit2
ev-deployer init deploy --chain-id 42170 --permit2

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

| Flag | Description |
|------|-------------|
| `--output <PATH>` | Write to file instead of stdout |
| `--chain-id <ID>` | Set the chain ID (defaults to 0) |
| `--permit2` | Enable Permit2 |
| `--admin-proxy-owner <ADDR>` | Enable AdminProxy with the given owner |

### `genesis`
Expand Down Expand Up @@ -114,15 +137,17 @@ ev-deployer deploy \
The deploy pipeline:

1. Connects to the RPC and verifies the chain ID matches the config.
2. Checks that the deterministic deployer (`0x4e59b44847b379578588920ca78fbf26c0b4956c`) exists on-chain.
2. Checks that the [deterministic deployer](https://github.com/Arachnid/deterministic-deployment-proxy) (`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).

The `address` field in the config is **ignored** in deploy mode — addresses come from the CREATE2 computation.
Permit2 is deployed using the [canonical Uniswap salt](https://github.com/Uniswap/permit2/blob/main/script/DeployPermit2.s.sol), so it lands at its well-known address `0x000000000022D473030F116dDEE9F6B43aC78BA3` on any chain.

> **Using with ev-dev**: The deterministic deployer can be included in the ev-dev genesis via `ev-deployer init genesis --deterministic-deployer`, so `ev-deployer deploy` works against ev-dev. See the [ev-dev README](../ev-dev/README.md#live-contract-deployment-create2) for examples.

#### State file and resumability

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.
The `--state` file tracks deployment progress and records which contracts have been deployed. If the process is interrupted, re-running with the same state file resumes where it left off. Contracts with well-known salts (e.g. Permit2) use their canonical salt; others use a random salt generated on first run.

Immutability rules protect against accidental misconfiguration on resume:

Expand Down
8 changes: 8 additions & 0 deletions bin/ev-deployer/src/contracts/permit2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ pub(crate) const HASHED_NAME: B256 = B256::new(hex!(
"9ac997416e8ff9d2ff6bebeb7149f65cdae5e32e2b90440b566bb3044041d36a"
));

/// The CREATE2 salt used by Uniswap to deploy Permit2 at its canonical address
/// (`0x000000000022D473030F116dDEE9F6B43aC78BA3`) via Nick's factory.
///
/// Source: <https://github.com/Uniswap/permit2/blob/main/script/DeployPermit2.s.sol>
pub(crate) const PERMIT2_CANONICAL_SALT: B256 = B256::new(hex!(
"0000000000000000000000000000000000000000d3af2663da51c10215000000"
));

/// Build the expected runtime bytecode for a Permit2 deployed at `address` on `chain_id`.
/// Used by the deploy pipeline to verify on-chain bytecode matches.
pub(crate) fn expected_runtime_bytecode(chain_id: u64, address: Address) -> Vec<u8> {
Expand Down
10 changes: 10 additions & 0 deletions bin/ev-deployer/src/deploy/create2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ mod tests {
assert_ne!(addr1, addr2);
}

#[test]
fn permit2_canonical_salt_produces_canonical_address() {
use crate::contracts::permit2::{PERMIT2_CANONICAL_SALT, PERMIT2_INITCODE};
let addr = compute_address(PERMIT2_CANONICAL_SALT, PERMIT2_INITCODE);
let expected: Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3"
.parse()
.unwrap();
assert_eq!(addr, expected);
}

#[test]
fn factory_calldata_format() {
let salt = B256::with_last_byte(0x42);
Expand Down
2 changes: 2 additions & 0 deletions bin/ev-deployer/src/deploy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! On-chain deployment pipeline: CREATE2 addressing, state tracking, and orchestration.

pub mod create2;
pub mod deployer;
pub mod pipeline;
Expand Down
7 changes: 3 additions & 4 deletions bin/ev-deployer/src/deploy/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ pub async fn run(pipeline_cfg: &PipelineConfig, deployer: &dyn ChainDeployer) ->
state
};

let salt = state.create2_salt;

// ── Step 2: Deploy Permit2 ──
if let Some(ref p2_config) = pipeline_cfg.config.contracts.permit2 {
eprintln!("[3/4] Deploying Permit2...");
Expand All @@ -70,8 +68,9 @@ pub async fn run(pipeline_cfg: &PipelineConfig, deployer: &dyn ChainDeployer) ->
eprintln!(" WARN: contracts.permit2.address is ignored in deploy mode");
}

let permit2_salt = contracts::permit2::PERMIT2_CANONICAL_SALT;
let initcode = contracts::permit2::PERMIT2_INITCODE.to_vec();
let address = compute_address(salt, &initcode);
let address = compute_address(permit2_salt, &initcode);

let expected_runtime = contracts::permit2::expected_runtime_bytecode(chain_id, address);

Expand All @@ -81,7 +80,7 @@ pub async fn run(pipeline_cfg: &PipelineConfig, deployer: &dyn ChainDeployer) ->
&DeployContractParams {
name: "permit2",
address,
salt,
salt: permit2_salt,
initcode: &initcode,
expected_runtime: &expected_runtime,
state_path: &pipeline_cfg.state_path,
Expand Down
Loading
Loading