Skip to content

feat(ev-deployer): part 1 - add ev-deployer CLI for genesis contract allocation#167

Merged
randygrok merged 22 commits intomainfrom
ev-deployer-part1-core
Mar 26, 2026
Merged

feat(ev-deployer): part 1 - add ev-deployer CLI for genesis contract allocation#167
randygrok merged 22 commits intomainfrom
ev-deployer-part1-core

Conversation

@randygrok
Copy link
Contributor

@randygrok randygrok commented Mar 13, 2026

Part of #119

Implements Part 1 (Core) of EV Deployer — a CLI tool for generating genesis alloc entries for ev-reth contracts via declarative TOML configuration.

The tool reads a TOML config file, encodes contract bytecode and storage state, and generates Ethereum-compatible genesis alloc JSON. Supports merging into existing genesis files with collision detection.

CLI commands

Command Description
init Generate a starter TOML config with all supported contracts commented out and documented
genesis Build genesis alloc JSON from a deploy config
compute-address Look up the configured address for a contract

genesis flags

  • --config — path to deploy TOML
  • --output — write JSON to file (stdout if omitted)
  • --merge-into — merge alloc entries into an existing genesis JSON
  • --force — allow overwriting existing addresses when merging
  • --addresses-out — export a JSON manifest of contract name → address

Contracts supported

Contract Description
admin_proxy Transparent proxy with owner-based access control (bytecode + owner in slot 0)

What's included

  • CLI framework with three subcommands (init, genesis, compute-address)
  • TOML config parsing and validation ([chain] required, [contracts] optional)
  • Genesis alloc builder with merge support and collision detection
  • Address manifest output
  • AdminProxy contract encoding (bytecode + owner storage)
  • E2E test that boots ev-reth with the generated genesis
  • CI workflow for unit and e2e tests

FeeVault and other contracts will be added in follow-up PRs.

@randygrok randygrok requested a review from a team as a code owner March 13, 2026 17:48
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new ev-deployer CLI crate and associated tooling: TOML config parsing, contract bytecode/storage encoding, genesis alloc JSON generation and merging, address manifest output, tests (unit + e2e), CI workflow, and build/test recipes.

Changes

Cohort / File(s) Summary
CI / Workflows
.github/workflows/ev_deployer.yml
New GitHub Actions workflow with jobs: verify-bytecodes, unit-tests, and e2e-genesis; triggers on push, pull_request, and workflow_dispatch with path filters.
Workspace
Cargo.toml
Added bin/ev-deployer to workspace members.
ev-deployer manifest
bin/ev-deployer/Cargo.toml
New crate manifest declaring dependencies and dev-deps for the CLI.
CLI entrypoint
bin/ev-deployer/src/main.rs
New binary with Genesis and ComputeAddress subcommands, CLI parsing, file I/O, and output behavior (--output, --merge-into, --addresses-out).
Config parsing & validation
bin/ev-deployer/src/config.rs
TOML-deserializable config types (DeployConfig, ChainConfig, ContractsConfig, AdminProxyConfig) and DeployConfig::load with validation (reject zero owner).
Genesis construction & merging
bin/ev-deployer/src/genesis.rs
Builds alloc JSON from contracts, formats code/storage, merges into existing genesis with collision detection and optional --force.
Contracts scaffolding
bin/ev-deployer/src/contracts/mod.rs, bin/ev-deployer/src/contracts/admin_proxy.rs
GenesisContract struct, admin_proxy module with embedded runtime bytecode and build that sets address, code, and slot-0 owner encoding; unit tests included.
Output manifest
bin/ev-deployer/src/output.rs
build_manifest to emit JSON mapping of configured contract names → addresses.
Documentation & examples
bin/ev-deployer/README.md, bin/ev-deployer/examples/devnet.toml
README describing TOML schema and CLI usage; example devnet config with chain_id and contracts.admin_proxy.
End-to-end test script
bin/ev-deployer/tests/e2e_genesis.sh
E2E script: builds binaries, generates merged genesis, boots ev-reth, polls JSON-RPC, verifies bytecode and storage value.
Build / Foundry
justfile, contracts/foundry.toml
Added build-deployer and test-deployer recipes; Foundry profile settings (solc_version, cbor_metadata, bytecode_hash).

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer (runs CLI)
  participant FS as Filesystem (config / genesis.json)
  participant CLI as ev-deployer
  participant Node as ev-reth (RPC)
  rect rgba(200,200,255,0.5)
    Dev->>CLI: run `ev-deployer genesis --merge-into base.json --config devnet.toml`
    CLI->>FS: read `devnet.toml`
    CLI->>CLI: build contract GenesisContract (address, code, storage)
    CLI->>FS: read `base.json`
    CLI->>FS: merge alloc entries (check collisions, apply --force if set)
    CLI->>FS: write merged genesis.json
  end
  rect rgba(200,255,200,0.5)
    Dev->>Node: start `ev-reth --genesis merged.json`
    Node->>Dev: exposes JSON-RPC
    Dev->>Node: curl eth_getCode / eth_getStorageAt
    Node->>Dev: return code and storage => verify matches expected
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • damiannolan
  • chatton
  • tac0turtle

Poem

🐰 From TOML burrowed deep I hop and write,
Genesis keys gleam in the moonlit byte,
Contracts tucked in JSON's gentle nest,
The rabbit cheers: "Deployments now rest!" ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description provides comprehensive details about the implementation, including feature overview, CLI commands, supported flags, contracts, and scope. However, it diverges from the repository's template structure. Confirm whether the PR description fully aligns with template expectations or if the custom format is acceptable for this repository's standards.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding the ev-deployer CLI tool for genesis contract allocation, which matches the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 86.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ev-deployer-part1-core

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link

claude bot commented Mar 13, 2026

Claude finished @randygrok's task —— View job


PR Review - ev-deployer CLI Implementation

I've completed a comprehensive review of the ev-deployer implementation. Here's my detailed feedback:

Todo List:

  • Review core architecture and design patterns
  • Analyze code quality and best practices
  • Check for potential bugs and edge cases
  • Evaluate security considerations
  • Assess performance implications
  • Review test coverage and quality
  • Provide comprehensive feedback

Overall Assessment ✅

This is a well-designed and implemented CLI tool. The architecture is clean, code quality is high, and the test coverage is good. The implementation follows Rust best practices and includes appropriate error handling.

Strengths

📐 Architecture & Design

  • Clean modular architecture with well-separated concerns
  • Consistent error handling using eyre
  • Good CLI design with intuitive subcommands and flags
  • Extensible contract system that will easily accommodate future contracts

🔒 Security

  • Proper validation of zero addresses in config
  • Safe bytecode handling using alloy-primitives
  • Address normalization prevents collision detection bypass
  • No hardcoded secrets or sensitive data exposure

🧪 Test Coverage

  • Comprehensive unit tests for core functionality
  • End-to-end test that validates the full workflow
  • Golden value tests ensure bytecode consistency
  • Good edge case coverage (collision detection, force overwrite)

Issues Found

🟠 Major Issues (Already Addressed by CodeRabbit)

  1. Workflow Path Filters - The CI workflow was missing triggers for root-level file changes that could affect the deployer build
  2. Config Validation - Missing uniqueness check for contract addresses could cause silent overwrites
  3. Merge Behavior - --merge-into without --output didn't persist merged results back to the source file

🟡 Minor Issues

Code Robustness:

  • bin/ev-deployer/src/contracts/admin_proxy.rs:71 - Use trim_start_matches("0x") instead of strip_prefix("0x").unwrap() to avoid panics on unexpected forge output
  • bin/ev-deployer/src/main.rs:58 - Help text mentions fee_vault contract but it's not implemented yet - should clarify current limitations

Test Quality:

  • bin/ev-deployer/tests/e2e_genesis.sh:80 - Uses fragile substring grep instead of structured JSON validation for address presence
  • bin/ev-deployer/tests/e2e_genesis.sh:123 - Hardcoded expected values instead of deriving from test inputs

Performance Considerations ⚡

  • Minimal performance impact - tool generates small JSON files
  • File I/O is appropriate for the use case
  • No unnecessary allocations or expensive operations
  • Forge subprocess execution is properly handled and only runs in tests

Code Quality Assessment 📊

Rust Best Practices: ✅ Excellent

  • Proper use of Result<T, E> for error handling
  • Good ownership patterns with minimal cloning
  • Appropriate visibility modifiers (pub(crate))
  • Clean module organization

Documentation: ✅ Good

  • Clear inline comments where needed
  • Good CLI help text and usage examples
  • README provides comprehensive usage guide

Error Handling: ✅ Excellent

  • Consistent use of eyre for error propagation
  • Meaningful error messages with context
  • Proper validation with clear failure modes

Security Analysis 🔐

No security concerns identified:

  • No unsafe code blocks
  • Proper input validation
  • No credential or secret handling
  • File operations are safely bounded
  • No network operations that could introduce vulnerabilities

Recommendations

  1. Consider adding validation for zero contract addresses in config validation alongside the existing owner checks
  2. Add more robust forge output parsing to handle edge cases gracefully
  3. Enhance e2e test with structured JSON validation instead of string matching
  4. Consider adding integration tests for the merge collision scenarios

Summary

This is a high-quality implementation that follows excellent engineering practices. The modular design makes it easy to extend with additional contracts, and the comprehensive test suite provides confidence in the implementation. The issues identified are minor and mostly already addressed by the automated review feedback.

The tool successfully achieves its goal of providing a declarative way to generate genesis configurations for ev-reth contracts, with good UX through the CLI interface and proper safety guards for merge operations.

Recommendation: ✅ Approve - This PR is ready to merge after addressing the CodeRabbit feedback.

@randygrok randygrok marked this pull request as draft March 13, 2026 17:48
@randygrok randygrok changed the title feat: add ev-deployer CLI for genesis contract allocation feat(ev-deployer): add ev-deployer CLI for genesis contract allocation Mar 18, 2026
@randygrok randygrok changed the title feat(ev-deployer): add ev-deployer CLI for genesis contract allocation feat(ev-deployer): part 1 - add ev-deployer CLI for genesis contract allocation Mar 18, 2026
@randygrok randygrok marked this pull request as ready for review March 19, 2026 09:48
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ev_deployer.yml:
- Around line 6-13: The pull_request path filters in ev_deployer.yml only
include 'contracts/src/**', 'contracts/foundry.toml', and 'bin/ev-deployer/**',
so edits to root Cargo.toml, Cargo.lock, or the workflow itself won't trigger EV
Deployer CI and can break cargo test -p ev-deployer; update the
pull_request.paths block (the pull_request.paths entry in
.github/workflows/ev_deployer.yml) to also include the root-level files like
'Cargo.toml', 'Cargo.lock', and the workflow file (e.g.
'.github/workflows/ev_deployer.yml') so changes to those files will trigger the
workflow.

In `@bin/ev-deployer/README.md`:
- Around line 86-90: The README's description of the merge behavior is
misleading: update the text around the ev-deployer genesis command (referencing
the --merge-into and --output flags) to remove the phrase "modifies the `alloc`
field in-place" and explicitly state that the CLI does not mutate the original
file but writes the merged result to the path provided by --output (or to stdout
if --output is omitted); ensure the example command and accompanying sentence
clearly say the original genesis.json is unchanged and the merged output is
written to genesis-out.json (or stdout).

In `@bin/ev-deployer/src/config.rs`:
- Around line 83-101: The validate() method currently doesn't ensure contract
addresses are unique, allowing admin_proxy.address == fee_vault.address which
later causes silent alloc overwrites; add a check in validate() that if both
self.contracts.admin_proxy and self.contracts.fee_vault are Some, their .address
fields must not be equal and fail fast (use eyre::ensure or eyre::bail) with a
clear error like "admin_proxy.address must not equal fee_vault.address"; update
the validate() function to perform this uniqueness assertion alongside the
existing owner and bridge_share_bps checks.

In `@bin/ev-deployer/src/contracts/admin_proxy.rs`:
- Around line 68-73: The current normalization of forge output (building
forge_hex from output.stdout) uses unwraps and will panic on invalid UTF-8 or if
the "0x" prefix is missing; change it to handle errors safely by first
converting output.stdout with String::from_utf8 and propagating or logging the
error instead of unwrap, then perform trimming and lowercase on the resulting
string and remove a leading "0x" non-panically (e.g., use
strip_prefix("0x").unwrap_or(&s) or check starts_with and slice) to produce
forge_hex; update the code that assigns forge_hex (the variable constructed from
output.stdout) to return a Result or fallback value rather than panicking.

In `@bin/ev-deployer/src/contracts/fee_vault.rs`:
- Around line 171-176: The current normalization of forge output into forge_hex
uses multiple unwraps and can panic if the output is not valid UTF-8 or if the
"0x" prefix is missing; change it to a tolerant normalization that avoids
unwraps by using String::from_utf8_lossy on output.stdout and
trim_start_matches("0x") instead of strip_prefix/unwrap, e.g. build forge_hex
from
String::from_utf8_lossy(&output.stdout).trim().trim_start_matches("0x").to_lowercase()
so it safely handles missing "0x" and invalid UTF-8.

In `@bin/ev-deployer/src/main.rs`:
- Around line 71-75: When --merge-into is provided but no explicit --output is
given, the merged genesis currently only goes to stdout; change the control flow
so that when merge_into is Some(genesis_path) and output is None you persist the
merged result back to genesis_path. Concretely, after calling
genesis::merge_into(&cfg, genesis_path, force) (or using the merged value from
genesis::build_alloc when merging), write the resulting genesis bytes/string to
the same genesis_path (or call an existing genesis write helper) instead of only
printing to stdout; update the branch that handles merge_into (and the similar
branch around lines 79–84) to perform that write when output is unspecified.

In `@bin/ev-deployer/tests/e2e_genesis.sh`:
- Around line 35-38: The curl RPC calls that post JSON-RPC using RPC_URL with
variables method and params are currently blocking; update both curl invocations
(the ones that build the JSON payload with "$RPC_URL" and -d
"{\"jsonrpc\":\"2.0\",\"method\":\"$method\",\"params\":$params,\"id\":1}" and
the similar second call) to include connection and overall timeouts (for example
--connect-timeout 5 --max-time 10) so a stalled node/socket won’t hang CI; keep
the existing headers and piping to python3 unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 442c5b5a-2b3c-4deb-af81-3a72dbdffa67

📥 Commits

Reviewing files that changed from the base of the PR and between 2a96f46 and 7e19222.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • contracts/foundry.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .github/workflows/ev_deployer.yml
  • Cargo.toml
  • bin/ev-deployer/Cargo.toml
  • bin/ev-deployer/README.md
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/admin_proxy.rs
  • bin/ev-deployer/src/contracts/fee_vault.rs
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/src/genesis.rs
  • bin/ev-deployer/src/main.rs
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
  • contracts/foundry.toml
  • justfile

Comment on lines +171 to +176
let forge_hex = String::from_utf8(output.stdout)
.unwrap()
.trim()
.strip_prefix("0x")
.unwrap()
.to_lowercase();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use non-panicking forge hex normalization in test.

Line 174/175 can panic if forge output unexpectedly omits 0x. Prefer tolerant normalization.

Suggested fix
-        let forge_hex = String::from_utf8(output.stdout)
-            .unwrap()
-            .trim()
-            .strip_prefix("0x")
-            .unwrap()
-            .to_lowercase();
+        let forge_hex = String::from_utf8(output.stdout)
+            .unwrap()
+            .trim()
+            .trim_start_matches("0x")
+            .to_lowercase();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/src/contracts/fee_vault.rs` around lines 171 - 176, The
current normalization of forge output into forge_hex uses multiple unwraps and
can panic if the output is not valid UTF-8 or if the "0x" prefix is missing;
change it to a tolerant normalization that avoids unwraps by using
String::from_utf8_lossy on output.stdout and trim_start_matches("0x") instead of
strip_prefix/unwrap, e.g. build forge_hex from
String::from_utf8_lossy(&output.stdout).trim().trim_start_matches("0x").to_lowercase()
so it safely handles missing "0x" and invalid UTF-8.

Comment on lines +71 to +75
let result = if let Some(ref genesis_path) = merge_into {
genesis::merge_into(&cfg, genesis_path, force)?
} else {
genesis::build_alloc(&cfg)
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

--merge-into without --output drops merged result to stdout only.

When users pass --merge-into alone, the merged genesis is not written back to the source file. That makes merge behavior easy to misuse and lose.

Suggested fix (persist merge target by default)
-            if let Some(ref out_path) = output {
-                std::fs::write(out_path, &json)?;
-                eprintln!("Wrote alloc to {}", out_path.display());
-            } else {
-                println!("{json}");
-            }
+            if let Some(target_path) = output.as_ref().or(merge_into.as_ref()) {
+                std::fs::write(target_path, &json)?;
+                eprintln!("Wrote alloc to {}", target_path.display());
+            } else {
+                println!("{json}");
+            }

Also applies to: 79-84

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/src/main.rs` around lines 71 - 75, When --merge-into is
provided but no explicit --output is given, the merged genesis currently only
goes to stdout; change the control flow so that when merge_into is
Some(genesis_path) and output is None you persist the merged result back to
genesis_path. Concretely, after calling genesis::merge_into(&cfg, genesis_path,
force) (or using the merged value from genesis::build_alloc when merging), write
the resulting genesis bytes/string to the same genesis_path (or call an existing
genesis write helper) instead of only printing to stdout; update the branch that
handles merge_into (and the similar branch around lines 79–84) to perform that
write when output is unspecified.

- Expand CI workflow path triggers to include Cargo.toml, Cargo.lock,
  and the workflow file itself
- Fix README merge behavior description (not in-place)
- Validate contract addresses are unique in config
- Use trim_start_matches("0x") instead of strip_prefix().unwrap() in
  bytecode verification tests
- Add curl timeouts to e2e test RPC calls to prevent CI hangs
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
bin/ev-deployer/src/contracts/fee_vault.rs (1)

171-175: ⚠️ Potential issue | 🟡 Minor

Avoid panic in forge output decoding path.

Line 171 still uses String::from_utf8(output.stdout).unwrap(), which can panic on unexpected bytes. Use lossy decoding to keep this parity test tolerant.

Proposed fix
-        let forge_hex = String::from_utf8(output.stdout)
-            .unwrap()
+        let forge_hex = String::from_utf8_lossy(&output.stdout)
             .trim()
             .trim_start_matches("0x")
             .to_lowercase();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/src/contracts/fee_vault.rs` around lines 171 - 175, The
current forge_hex construction uses String::from_utf8(output.stdout).unwrap()
which can panic on invalid UTF-8; change the decoding to a lossy form (e.g. use
String::from_utf8_lossy on output.stdout) before
trim/trim_start_matches/to_lowercase so the parity test tolerates unexpected
bytes and avoids panics when building forge_hex.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@bin/ev-deployer/src/contracts/fee_vault.rs`:
- Around line 171-175: The current forge_hex construction uses
String::from_utf8(output.stdout).unwrap() which can panic on invalid UTF-8;
change the decoding to a lossy form (e.g. use String::from_utf8_lossy on
output.stdout) before trim/trim_start_matches/to_lowercase so the parity test
tolerates unexpected bytes and avoids panics when building forge_hex.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7550381-d587-49ad-b622-1fca28fe6555

📥 Commits

Reviewing files that changed from the base of the PR and between 7e19222 and aeffc0d.

📒 Files selected for processing (6)
  • .github/workflows/ev_deployer.yml
  • bin/ev-deployer/README.md
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/admin_proxy.rs
  • bin/ev-deployer/src/contracts/fee_vault.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/ev_deployer.yml
  • bin/ev-deployer/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/admin_proxy.rs
  • bin/ev-deployer/tests/e2e_genesis.sh

ev-deployer compute-address --config deploy.toml --contract admin_proxy
```

## Contracts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these optional to deploy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I am gonna move them into ev-deployer directly.

Copy link
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, left one comment on optionality but otherwise super nice job

FeeVault will be added in a separate PR to keep this one focused on
AdminProxy only.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
bin/ev-deployer/tests/e2e_genesis.sh (2)

79-83: Use structured JSON validation for alloc presence instead of substring grep.

grep can false-positive on incidental matches and does not prove the address exists as an alloc key.

Suggested refactor
-# Quick sanity: address should be in the alloc
-grep -q "000000000000000000000000000000000000Ad00" "$GENESIS" \
-    || fail "AdminProxy address not found in genesis"
+python3 - "$GENESIS" <<'PY' || fail "AdminProxy address not found in genesis alloc"
+import json, sys
+genesis_path = sys.argv[1]
+with open(genesis_path, "r", encoding="utf-8") as f:
+    data = json.load(f)
+alloc = data.get("alloc", {})
+target = "000000000000000000000000000000000000ad00"
+keys = {k.lower()[2:] if k.lower().startswith("0x") else k.lower() for k in alloc.keys()}
+if target not in keys:
+    raise SystemExit(1)
+PY
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/tests/e2e_genesis.sh` around lines 79 - 83, Replace the
fragile substring grep check that looks for
"000000000000000000000000000000000000Ad00" in "$GENESIS" with a structured JSON
key existence check using a JSON parser (e.g., jq) to assert the address is
present under the "alloc" object; update the test to call jq on "$GENESIS" to
test .alloc["0x000000000000000000000000000000000000Ad00"] (or the canonical
address form used elsewhere) and call fail() if jq indicates the key is missing,
otherwise call pass() — locate this logic around the existing GENESIS variable
reference and the fail/pass calls in the e2e_genesis.sh test.

109-125: Derive expected AdminProxy values from test input instead of hardcoding literals.

Duplicating ADMIN_PROXY/owner as constants makes this flow less sensitive to regressions in config ingestion. Prefer sourcing expected values from the same deploy input (or generated addresses manifest) before asserting RPC state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/tests/e2e_genesis.sh` around lines 109 - 125, The test
currently hardcodes expected AdminProxy values; change it to derive expectations
from the test inputs by using the existing ADMIN_OWNER (and ADMIN_PROXY)
variables rather than literal addresses: build expected_owner_slot by taking
ADMIN_OWNER, stripping the 0x, lowercasing it and left-padding with zeros to 64
hex chars and prefixing 0x, then compare that to admin_slot0 (normalized to
lowercase) produced by rpc_call; keep using ADMIN_PROXY when fetching admin_code
and use fail/pass as before. Ensure you reference the symbols ADMIN_OWNER,
ADMIN_PROXY, admin_code, admin_slot0, expected_owner_slot, rpc_call and fail so
the logic remains robust to changes in deploy inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/ev-deployer/tests/e2e_genesis.sh`:
- Around line 79-83: Replace the fragile substring grep check that looks for
"000000000000000000000000000000000000Ad00" in "$GENESIS" with a structured JSON
key existence check using a JSON parser (e.g., jq) to assert the address is
present under the "alloc" object; update the test to call jq on "$GENESIS" to
test .alloc["0x000000000000000000000000000000000000Ad00"] (or the canonical
address form used elsewhere) and call fail() if jq indicates the key is missing,
otherwise call pass() — locate this logic around the existing GENESIS variable
reference and the fail/pass calls in the e2e_genesis.sh test.
- Around line 109-125: The test currently hardcodes expected AdminProxy values;
change it to derive expectations from the test inputs by using the existing
ADMIN_OWNER (and ADMIN_PROXY) variables rather than literal addresses: build
expected_owner_slot by taking ADMIN_OWNER, stripping the 0x, lowercasing it and
left-padding with zeros to 64 hex chars and prefixing 0x, then compare that to
admin_slot0 (normalized to lowercase) produced by rpc_call; keep using
ADMIN_PROXY when fetching admin_code and use fail/pass as before. Ensure you
reference the symbols ADMIN_OWNER, ADMIN_PROXY, admin_code, admin_slot0,
expected_owner_slot, rpc_call and fail so the logic remains robust to changes in
deploy inputs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16ba6325-c7f2-4619-9c3a-119a04863a7f

📥 Commits

Reviewing files that changed from the base of the PR and between aeffc0d and e8a39f8.

📒 Files selected for processing (8)
  • bin/ev-deployer/README.md
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/src/genesis.rs
  • bin/ev-deployer/src/main.rs
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
✅ Files skipped from review due to trivial changes (2)
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/README.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/main.rs
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/genesis.rs

Keep only the CLI framework (config, genesis merge, manifest output)
without any contract implementations. Contracts will be added in
subsequent PRs.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
bin/ev-deployer/src/genesis.rs (1)

109-118: Cover the collision paths in tests.

This test only proves that an existing alloc entry survives when build_alloc() returns {}. It does not exercise the address collision error or the force overwrite branch, which are the core behaviors of this module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/src/genesis.rs` around lines 109 - 118, Add tests that
exercise the address-collision and force-overwrite branches of merge_into:
create a temp genesis file containing an alloc entry for a specific address,
then call merge_into(&test_config(), tmp.path(), false) with build_alloc()
returning a colliding address and assert it returns an error describing the
collision; then call merge_into(&test_config(), tmp.path(), true) and assert the
alloc entry is replaced/updated (e.g., the value written reflects the new
build_alloc result). Reference the existing test merge_into_existing_genesis and
the merge_into function (and the code path that checks/overwrites alloc entries)
to add two new tests — one expecting a collision error and one verifying
force=true overwrites the existing alloc entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/ev-deployer/src/genesis.rs`:
- Around line 31-35: Normalize alloc keys before checking collisions and
inserting: convert both the incoming key (addr from new_alloc) and existing keys
in genesis_alloc to a canonical address form (e.g., lowercase hex without 0x or
a checksum-normalized form) before calling genesis_alloc.contains_key(addr) and
before genesis_alloc.insert(...). In practice, compute a canonical_addr for each
addr (and ensure genesis_alloc is keyed by canonical forms or compare against
canonicalized existing keys) inside the loop that iterates new_alloc (the block
using variables addr and entry) so collision detection and insertion use the
same normalized representation.

In `@bin/ev-deployer/src/main.rs`:
- Around line 93-98: The ComputeAddress subcommand currently ignores the
provided config and always bails with "unknown contract"; update the
Command::ComputeAddress handling to first load and validate the config (use the
passed-in config path instead of _config_path so errors are surfaced) and
propagate any config file errors, then match on the contract name and return a
clear "not implemented yet" error for unsupported contracts (or remove/gate the
subcommand for part 1 if you prefer). Specifically, replace the unconditional
eyre::bail! in the Command::ComputeAddress arm with code that attempts to
read/parse the config, reports config load failures, and only then returns an
explicit not-implemented error for the contract branch of compute-address.

---

Nitpick comments:
In `@bin/ev-deployer/src/genesis.rs`:
- Around line 109-118: Add tests that exercise the address-collision and
force-overwrite branches of merge_into: create a temp genesis file containing an
alloc entry for a specific address, then call merge_into(&test_config(),
tmp.path(), false) with build_alloc() returning a colliding address and assert
it returns an error describing the collision; then call
merge_into(&test_config(), tmp.path(), true) and assert the alloc entry is
replaced/updated (e.g., the value written reflects the new build_alloc result).
Reference the existing test merge_into_existing_genesis and the merge_into
function (and the code path that checks/overwrites alloc entries) to add two new
tests — one expecting a collision error and one verifying force=true overwrites
the existing alloc entry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c54e065-9fde-42e2-8e3e-9a85c37a9ff9

📥 Commits

Reviewing files that changed from the base of the PR and between e8a39f8 and 089ef22.

📒 Files selected for processing (8)
  • bin/ev-deployer/README.md
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/src/genesis.rs
  • bin/ev-deployer/src/main.rs
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
✅ Files skipped from review due to trivial changes (3)
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/README.md
  • bin/ev-deployer/src/config.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
  • bin/ev-deployer/src/output.rs

Default to an empty ContractsConfig when the section is omitted,
so a minimal config only needs [chain].
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
bin/ev-deployer/src/config.rs (1)

50-60: Consider validating that admin_proxy.address is non-zero.

The validation ensures owner is not the zero address, but address (the deployment target) is not checked. A zero address would place the contract at 0x0, which is almost certainly unintended.

Suggested fix
     fn validate(&self) -> eyre::Result<()> {
         if let Some(ref ap) = self.contracts.admin_proxy {
+            eyre::ensure!(
+                !ap.address.is_zero(),
+                "admin_proxy.address must not be the zero address"
+            );
             eyre::ensure!(
                 !ap.owner.is_zero(),
                 "admin_proxy.owner must not be the zero address"
             );
         }

         Ok(())
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ev-deployer/src/config.rs` around lines 50 - 60, The validate() method
currently checks admin_proxy.owner but not admin_proxy.address; update validate
(the fn validate(&self) -> eyre::Result<()>) to also ensure the
admin_proxy.address is not the zero address by adding an
eyre::ensure!(!ap.address.is_zero(), "admin_proxy.address must not be the zero
address") alongside the existing owner check so both ap.owner and ap.address are
validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/ev-deployer/src/main.rs`:
- Around line 52-53: The help text for the --contract arg mentions "fee_vault"
but only "admin_proxy" is handled, so either remove "fee_vault" from the
docstring or implement support for it in the command handling; to fix, either
update the doc comment above the #[arg(long)] so it lists only "admin_proxy", or
add a matching branch in the ComputeAddress logic (e.g., the match handling the
contract name in ComputeAddress) that recognizes "fee_vault" and
computes/returns the correct address/behavior for it; ensure the symbol
ComputeAddress (and whatever enum/handler that matches contract names) is
updated accordingly so help text and runtime behavior remain consistent.

---

Nitpick comments:
In `@bin/ev-deployer/src/config.rs`:
- Around line 50-60: The validate() method currently checks admin_proxy.owner
but not admin_proxy.address; update validate (the fn validate(&self) ->
eyre::Result<()>) to also ensure the admin_proxy.address is not the zero address
by adding an eyre::ensure!(!ap.address.is_zero(), "admin_proxy.address must not
be the zero address") alongside the existing owner check so both ap.owner and
ap.address are validated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f93c772-ab31-42a1-b97a-0d88098125c9

📥 Commits

Reviewing files that changed from the base of the PR and between 089ef22 and 6b85563.

📒 Files selected for processing (9)
  • bin/ev-deployer/README.md
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/config.rs
  • bin/ev-deployer/src/contracts/admin_proxy.rs
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/src/genesis.rs
  • bin/ev-deployer/src/main.rs
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
✅ Files skipped from review due to trivial changes (3)
  • bin/ev-deployer/examples/devnet.toml
  • bin/ev-deployer/src/contracts/mod.rs
  • bin/ev-deployer/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • bin/ev-deployer/src/output.rs
  • bin/ev-deployer/tests/e2e_genesis.sh
  • bin/ev-deployer/src/genesis.rs

randygrok and others added 6 commits March 24, 2026 13:47
Generates a TOML config template with all supported contracts
commented out and documented.
…ence

Move Init subcommand first in help output and remove leftover
fee_vault mention from --contract flag doc.
Canonicalize address keys (lowercase, strip 0x) before comparing
during merge, so collisions are detected regardless of case or prefix
in the existing genesis file.
@randygrok randygrok enabled auto-merge (squash) March 26, 2026 08:21
@randygrok randygrok merged commit 899f2fa into main Mar 26, 2026
24 checks passed
@randygrok randygrok deleted the ev-deployer-part1-core branch March 26, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants