feat(ev-dev): part 5 – add interactive TUI dashboard#184
Conversation
…e patching Add Hyperlane monorepo as git submodule pinned to @hyperlane-xyz/core@11.0.3. Implement bytecode patching utility for Solidity immutable variables and MerkleTreeHook genesis alloc generation with mailbox/localDomain/deployedBlock patching. This is the foundation for embedding Hyperlane contracts at genesis.
# Conflicts: # Cargo.lock
…deployer-merkle-tree-hook
The MerkleTreeHook bytecode test needs OpenZeppelin dependencies from the Hyperlane monorepo, which are managed by soldeer.
Verify bytecode, storage slots, and patched immutables (mailbox, localDomain, deployedBlock) for the MerkleTreeHook contract via RPC.
Rustdoc interprets [644], [578], and [32] as intra-doc links, causing the docs CI job to fail with -D warnings.
Add Uniswap Permit2 as a genesis-deployable contract with EIP-712 immutable patching (_CACHED_CHAIN_ID, _CACHED_DOMAIN_SEPARATOR).
…racts Add three Hyperlane core contracts to ev-deployer so the full messaging stack can be embedded at genesis without post-deploy transactions. - Mailbox: core messaging hub with localDomain/deployedBlock immutables - NoopIsm: stateless ISM that accepts all messages (for devnet) - ProtocolFee: post-dispatch hook with MAX_PROTOCOL_FEE immutable
…lean ci build The embedded bytecodes were compiled with --extra-output storageLayout which subtly altered the output. Regenerated from a clean ci profile build to match what forge produces without extra flags.
…noop_ism, protocol_fee)
- 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
…s validation Merge ev-deployer-part1-core into ev-deployer-merkle-tree-hook, resolving config.rs conflicts by keeping both the MerkleTreeHook/ProtocolFee validations and the new duplicate-address check between AdminProxy and FeeVault.
- Add ev-deployer dependency to ev-dev - Accept optional --deploy-config argument in ev-dev CLI - Load deploy config and override chain ID if needed - Display deployed contract addresses in dev chain banner - Add just recipes for installing ev-dev and ev-deployer binaries
- Integrate ratatui for terminal UI with blocks, logs, and accounts panels - Implement custom tracing layer to capture real-time log events - Add keyboard navigation (Tab for panel switch, arrows for scroll, q to quit) - Support coexistence of TUI and plain log output modes - Add crossterm for terminal event handling
📝 WalkthroughWalkthroughThis PR implements Permit2 canonical CREATE2 deployment, introduces a dual-mode (genesis/deploy) configuration system for ev-deployer, adds a complete terminal UI for ev-dev chain monitoring with live balance polling, upgrades workspace dependencies to reth v2.2.0, and refactors ev-reth executor traits to use references instead of clones. ChangesDeployment and Development Tooling Enhancement
Sequence Diagram(s)sequenceDiagram
participant User
participant EventHandler as handle_key
participant App
participant BalancePoller
participant RPC as HTTP/RPC
participant UI as TUI Renderer
User->>EventHandler: keyboard input
EventHandler->>App: navigate/scroll/fetch block
BalancePoller->>RPC: poll balances
RPC-->>BalancePoller: balances
BalancePoller->>App: send balance update
App->>App: drain logs/balances
App->>UI: draw (blocks/logs/accounts/footer)
UI-->>User: terminal display
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Add Nick's CREATE2 factory (0x4e59b44...956c) to the devnet genesis so that ev-deployer deploy works against ev-dev out of the box. On post-merge chains the canonical keyless deployment transaction cannot be replayed, so the runtime bytecode is embedded directly in the genesis alloc. Document the live deployment workflow in both ev-dev and ev-deployer READMEs.
Inject Nick's CREATE2 factory (0x4e59b44847b379578588920ca78fbf26c0b4956c) into genesis state so ev-deployer deploy works on post-merge chains where the canonical keyless transaction cannot land. Genesis-only — the deploy pipeline already validates its existence on-chain.
…dy:evstack/ev-reth into feat/ev-dev-part5-tui # Conflicts: # bin/ev-deployer/src/deploy/pipeline.rs # bin/ev-deployer/src/init.rs
Deploy mode now uses the original Uniswap salt so Permit2 lands at its canonical address (0x000000000022D473030F116dDEE9F6B43aC78BA3) instead of a random one.
`ev-deployer init genesis` generates config with address fields for genesis injection. `ev-deployer init deploy` generates config without addresses (computed via CREATE2) and auto-includes the deterministic deployer since it is required for deploy mode.
…late The deterministic deployer cannot be deployed via CREATE2 (circular dependency). The deploy pipeline already verifies it exists on-chain, so including it in the deploy config template was misleading.
Reflect the init genesis/deploy split, canonical Permit2 CREATE2 salt, and removal of deterministic deployer from deploy config.
The flag passes a config for embedding contracts into genesis, not for deploying to a live chain. The old name was misleading after the init genesis/deploy split.
…oyer-part4-ev-dev-integration
…tion' into feat/ev-dev-part5-tui
# Conflicts: # Cargo.lock
…oyer-part4-ev-dev-integration
…tion' into feat/ev-dev-part5-tui # Conflicts: # Cargo.lock
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.51.1 to 1.52.3. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](tokio-rs/tokio@tokio-1.51.1...tokio-1.52.3) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.52.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
bin/ev-dev/src/tui/app.rs (2)
246-246: 💤 Low valueConsider graceful error handling instead of
expect.While the RPC URL is constructed from CLI arguments and should always be valid, using
expectcan cause a panic in unexpected edge cases. Consider usingunwrap_or_elsewith a fallback or early return to avoid panicking.♻️ Suggested improvement
- let provider = - ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")); + let Ok(url) = rpc_url.parse() else { + return; + }; + let provider = ProviderBuilder::new().connect_http(url);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bin/ev-dev/src/tui/app.rs` at line 246, The call ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")) uses expect and may panic; replace this with proper Result handling for rpc_url.parse() (e.g., rpc_url.parse().unwrap_or_else(|e| { log the error and return/propagate Err }) or a match/if let) and ensure connect_http is only called when parsing succeeds—update the surrounding function (where ProviderBuilder::new().connect_http is invoked) to return an error or early-return on parse failure instead of panicking.
339-339: 💤 Low valueConsider graceful error handling instead of
expect.Similar to the issue in
fetch_block_detail, usingexpecton URL parsing could cause a panic. Consider handling the error gracefully.♻️ Suggested improvement
- let provider = - ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")); + let Ok(url) = rpc_url.parse() else { + break; + }; + let provider = ProviderBuilder::new().connect_http(url);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bin/ev-dev/src/tui/app.rs` at line 339, The call using ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL")) panics on invalid URLs; replace the expect with graceful error handling: parse rpc_url with rpc_url.parse() and handle the Result (via match, ? propagation, or map_err) to return or log a descriptive error instead of panicking, then pass the successfully parsed URL into ProviderBuilder::new().connect_http; update the surrounding function signature to return a Result if necessary to propagate the error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/ev-deployer/src/deploy/pipeline.rs`:
- Around line 71-74: Add a pre-deploy guard that verifies any existing saved
Permit2 address in state matches the canonical computed address
(compute_address(permit2_salt, &initcode)) and fail fast if they differ so we
don't resume with a legacy non-canonical address; specifically, before deciding
to skip deployment based on status, load the stored Permit2 address from state,
compute the canonical address using permit2_salt and PERMIT2_INITCODE
(initcode), compare it to the stored address, and return an explicit error
instructing migration if they mismatch (apply the same check around the second
occurrence at the line 83 codepath).
---
Nitpick comments:
In `@bin/ev-dev/src/tui/app.rs`:
- Line 246: The call
ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL"))
uses expect and may panic; replace this with proper Result handling for
rpc_url.parse() (e.g., rpc_url.parse().unwrap_or_else(|e| { log the error and
return/propagate Err }) or a match/if let) and ensure connect_http is only
called when parsing succeeds—update the surrounding function (where
ProviderBuilder::new().connect_http is invoked) to return an error or
early-return on parse failure instead of panicking.
- Line 339: The call using
ProviderBuilder::new().connect_http(rpc_url.parse().expect("valid RPC URL"))
panics on invalid URLs; replace the expect with graceful error handling: parse
rpc_url with rpc_url.parse() and handle the Result (via match, ? propagation, or
map_err) to return or log a descriptive error instead of panicking, then pass
the successfully parsed URL into ProviderBuilder::new().connect_http; update the
surrounding function signature to return a Result if necessary to propagate the
error.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1735bea3-e0e5-45ff-876b-1bb56409d2e4
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (21)
Cargo.tomlbin/ev-deployer/README.mdbin/ev-deployer/src/contracts/permit2.rsbin/ev-deployer/src/deploy/create2.rsbin/ev-deployer/src/deploy/mod.rsbin/ev-deployer/src/deploy/pipeline.rsbin/ev-deployer/src/init.rsbin/ev-deployer/src/init_template.tomlbin/ev-deployer/src/main.rsbin/ev-dev/Cargo.tomlbin/ev-dev/README.mdbin/ev-dev/assets/devnet-genesis.jsonbin/ev-dev/src/main.rsbin/ev-dev/src/tui/app.rsbin/ev-dev/src/tui/events.rsbin/ev-dev/src/tui/mod.rsbin/ev-dev/src/tui/tracing_layer.rsbin/ev-dev/src/tui/ui.rsclients/tests/e2e/flows.e2e.test.tscrates/node/src/builder.rscrates/node/src/evm_executor.rs
| 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); | ||
|
|
There was a problem hiding this comment.
Enforce canonical Permit2 address when resuming from existing state.
Line 71 makes Permit2 canonical, but a pre-existing state file can still skip deployment based only on status and retain a legacy non-canonical address. Add a mismatch guard before deploy to fail fast and force explicit migration.
Suggested fix
let permit2_salt = contracts::permit2::PERMIT2_CANONICAL_SALT;
let initcode = contracts::permit2::PERMIT2_INITCODE.to_vec();
let address = compute_address(permit2_salt, &initcode);
+ if let Some(existing) = state.contracts.permit2.as_ref() {
+ if existing.status >= ContractStatus::Deployed && existing.address != address {
+ eyre::bail!(
+ "state permit2 address mismatch: state={}, canonical={}. \
+ migrate or delete the state file before resuming",
+ existing.address,
+ address
+ );
+ }
+ }
let expected_runtime = contracts::permit2::expected_runtime_bytecode(chain_id, address);Also applies to: 83-83
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/ev-deployer/src/deploy/pipeline.rs` around lines 71 - 74, Add a
pre-deploy guard that verifies any existing saved Permit2 address in state
matches the canonical computed address (compute_address(permit2_salt,
&initcode)) and fail fast if they differ so we don't resume with a legacy
non-canonical address; specifically, before deciding to skip deployment based on
status, load the stored Permit2 address from state, compute the canonical
address using permit2_salt and PERMIT2_INITCODE (initcode), compare it to the
stored address, and return an explicit error instructing migration if they
mismatch (apply the same check around the second occurrence at the line 83
codepath).
Description
Adds an interactive terminal UI dashboard to ev-dev with the
--tuiflag. The TUI displays real-time blocks, logs, and accounts panels using ratatui, with a custom tracing layer that captures log events in real-time. Plain log output remains the default behavior.Type of Change
Testing
--tuioff) unchangedSummary by CodeRabbit
Release Notes
New Features
--tui) toev-devfor live chain monitoring displaying blocks, logs, and accounts with keyboard navigationGenesisandDeploysubcommands inev-deployerDocumentation
ev-deployerREADME with genesis/deploy workflow steps and contract deployment pipeline documentationev-devREADME with TUI usage guide, keyboard shortcuts, and live CREATE2 deployment instructionsTests
Chores